diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionAttachmentManager.cs b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionAttachmentManager.cs index f1606df6b6..6938babef3 100644 --- a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionAttachmentManager.cs +++ b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionAttachmentManager.cs @@ -140,7 +140,10 @@ public List GetAttachments(DataCollectionContext dataCollectionCo { try { - Task.WhenAll(this.attachmentTasks[dataCollectionContext].ToArray()).Wait(); + if (this.attachmentTasks.TryGetValue(dataCollectionContext, out var tasks)) + { + Task.WhenAll(tasks.ToArray()).Wait(); + } } catch (Exception ex) { diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameCollector.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameCollector.cs index ea0f92321c..86cbec6b42 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameCollector.cs +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameCollector.cs @@ -49,6 +49,8 @@ public class BlameCollector : DataCollector, ITestExecutionEnvironmentSpecifier private bool dumpWasCollectedByHangDumper; private string targetFramework; private List> environmentVariables = new List>(); + private bool uploadDumpFiles; + private string tempDirectory; /// /// Initializes a new instance of the class. @@ -147,10 +149,7 @@ public override void Initialize( this.environmentVariables.Add(new KeyValuePair("COMPlus_DbgMiniDumpType", this.processFullDumpEnabled ? "2" : "1")); } - var guid = Guid.NewGuid().ToString(); - - var dumpDirectory = Path.Combine(Path.GetTempPath(), guid); - Directory.CreateDirectory(dumpDirectory); + var dumpDirectory = this.GetDumpDirectory(); var dumpPath = Path.Combine(dumpDirectory, $"%e_%p_%t_crashdump.dmp"); this.environmentVariables.Add(new KeyValuePair("COMPlus_DbgMiniDumpName", dumpPath)); } @@ -206,7 +205,8 @@ private void CollectDumpAndAbortTesthost() try { - this.processDumpUtility.StartHangBasedProcessDump(this.testHostProcessId, this.GetTempDirectory(), this.processFullDumpEnabled, this.targetFramework); + var dumpDirectory = this.GetDumpDirectory(); + this.processDumpUtility.StartHangBasedProcessDump(this.testHostProcessId, dumpDirectory, this.processFullDumpEnabled, this.targetFramework); } catch (Exception ex) { @@ -220,35 +220,42 @@ private void CollectDumpAndAbortTesthost() this.processDumpUtility.DetachFromTargetProcess(this.testHostProcessId); } - try + if (this.uploadDumpFiles) { - var dumpFiles = this.processDumpUtility.GetDumpFiles(); - foreach (var dumpFile in dumpFiles) + try { - try + var dumpFiles = this.processDumpUtility.GetDumpFiles(); + foreach (var dumpFile in dumpFiles) { - if (!string.IsNullOrEmpty(dumpFile)) + try { - this.dumpWasCollectedByHangDumper = true; - var fileTransferInformation = new FileTransferInformation(this.context.SessionDataCollectionContext, dumpFile, true, this.fileHelper); - this.dataCollectionSink.SendFileAsync(fileTransferInformation); + if (!string.IsNullOrEmpty(dumpFile)) + { + this.dumpWasCollectedByHangDumper = true; + var fileTransferInformation = new FileTransferInformation(this.context.SessionDataCollectionContext, dumpFile, true, this.fileHelper); + this.dataCollectionSink.SendFileAsync(fileTransferInformation); + } + } + catch (Exception ex) + { + // Eat up any exception here and log it but proceed with killing the test host process. + EqtTrace.Error(ex); } - } - catch (Exception ex) - { - // Eat up any exception here and log it but proceed with killing the test host process. - EqtTrace.Error(ex); - } - if (!dumpFiles.Any()) - { - EqtTrace.Error("BlameCollector.CollectDumpAndAbortTesthost: blame:CollectDumpOnHang was enabled but dump file was not generated."); + if (!dumpFiles.Any()) + { + EqtTrace.Error("BlameCollector.CollectDumpAndAbortTesthost: blame:CollectDumpOnHang was enabled but dump file was not generated."); + } } } + catch (Exception ex) + { + ConsoleOutput.Instance.Error(true, $"Blame: Collecting hang dump failed with error {ex}."); + } } - catch (Exception ex) + else { - ConsoleOutput.Instance.Error(true, $"Blame: Collecting hang dump failed with error {ex}."); + EqtTrace.Info("BlameCollector.CollectDumpAndAbortTesthost: Custom path to dump directory was provided via VSTEST_DUMP_PATH. Skipping attachment upload, the caller is responsible for collecting and uploading the dumps themselves."); } try @@ -440,30 +447,37 @@ private void SessionEndedHandler(object sender, SessionEndEventArgs args) // we won't dump the killed process again and that would just show a warning on the command line if ((this.testStartCount > this.testEndCount || this.collectDumpAlways) && !this.dumpWasCollectedByHangDumper) { - try + if (this.uploadDumpFiles) { - var dumpFiles = this.processDumpUtility.GetDumpFiles(); - foreach (var dumpFile in dumpFiles) + try { - if (!string.IsNullOrEmpty(dumpFile)) + var dumpFiles = this.processDumpUtility.GetDumpFiles(); + foreach (var dumpFile in dumpFiles) { - try - { - var fileTranferInformation = new FileTransferInformation(this.context.SessionDataCollectionContext, dumpFile, true); - this.dataCollectionSink.SendFileAsync(fileTranferInformation); - } - catch (FileNotFoundException ex) + if (!string.IsNullOrEmpty(dumpFile)) { - EqtTrace.Warning(ex.ToString()); - this.logger.LogWarning(args.Context, ex.ToString()); + try + { + var fileTranferInformation = new FileTransferInformation(this.context.SessionDataCollectionContext, dumpFile, true); + this.dataCollectionSink.SendFileAsync(fileTranferInformation); + } + catch (FileNotFoundException ex) + { + EqtTrace.Warning(ex.ToString()); + this.logger.LogWarning(args.Context, ex.ToString()); + } } } } + catch (FileNotFoundException ex) + { + EqtTrace.Warning(ex.ToString()); + this.logger.LogWarning(args.Context, ex.ToString()); + } } - catch (FileNotFoundException ex) + else { - EqtTrace.Warning(ex.ToString()); - this.logger.LogWarning(args.Context, ex.ToString()); + EqtTrace.Info("BlameCollector.CollectDumpAndAbortTesthost: Custom path to dump directory was provided via VSTEST_DUMP_PATH. Skipping attachment upload, the caller is responsible for collecting and uploading the dumps themselves."); } } } @@ -497,7 +511,8 @@ private void TestHostLaunchedHandler(object sender, TestHostLaunchedEventArgs ar try { - this.processDumpUtility.StartTriggerBasedProcessDump(args.TestHostProcessId, this.GetTempDirectory(), this.processFullDumpEnabled, this.targetFramework); + var dumpDirectory = this.GetDumpDirectory(); + this.processDumpUtility.StartTriggerBasedProcessDump(args.TestHostProcessId, dumpDirectory, this.processFullDumpEnabled, this.targetFramework); } catch (TestPlatformException e) { @@ -552,26 +567,30 @@ private void DeregisterEvents() private string GetTempDirectory() { - string tempPath = null; - var netDumperPath = this.environmentVariables.SingleOrDefault(p => p.Key == "COMPlus_DbgMiniDumpName").Value; - - try - { - if (!string.IsNullOrWhiteSpace(netDumperPath)) - { - tempPath = Path.GetDirectoryName(netDumperPath); - } - } - catch (ArgumentException) + if (string.IsNullOrWhiteSpace(this.tempDirectory)) { - // the path was not correct do nothing + this.tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(this.tempDirectory); + return this.tempDirectory; } - var tmp = !string.IsNullOrWhiteSpace(tempPath) ? tempPath : Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - - Directory.CreateDirectory(tmp); + return this.tempDirectory; + } - return tmp; + private string GetDumpDirectory() + { + // Using a custom dump path for scenarios where we want to upload the + // dump files ourselves, such as when running in Helix. + // This will save into the directory specified via VSTEST_DUMP_PATH, and + // skip uploading dumps via attachments. + var dumpDirectoryOverride = Environment.GetEnvironmentVariable("VSTEST_DUMP_PATH"); + var dumpDirectoryOverrideHasValue = !string.IsNullOrWhiteSpace(dumpDirectoryOverride); + this.uploadDumpFiles = !dumpDirectoryOverrideHasValue; + + var dumpDirectory = dumpDirectoryOverrideHasValue ? dumpDirectoryOverride : this.GetTempDirectory(); + Directory.CreateDirectory(dumpDirectory); + var dumpPath = Path.Combine(Path.GetFullPath(dumpDirectory)); + return dumpPath; } } } diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/CrashDumperFactory.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/CrashDumperFactory.cs index 867e9acb4f..06d5eda6a4 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/CrashDumperFactory.cs +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/CrashDumperFactory.cs @@ -31,18 +31,14 @@ public ICrashDumper Create(string targetFramework) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - EqtTrace.Info($"CrashDumperFactory: This is Windows, returning ProcDumpCrashDumper that uses ProcDump utility."); - return new ProcDumpCrashDumper(); - - // enable this once crashdump can trigger itself on exceptions that originate from task, then we can avoid using procdump - // if (!string.IsNullOrWhiteSpace(targetFramework) && !targetFramework.Contains("v5.0")) - // { - // EqtTrace.Info($"CrashDumperFactory: This is Windows on {targetFramework} which is not net5.0 or newer, returning ProcDumpCrashDumper that uses ProcDump utility."); - // return new ProcDumpCrashDumper(); - // } - - // EqtTrace.Info($"CrashDumperFactory: This is Windows on {targetFramework}, returning the .NETClient dumper which uses env variables to collect crashdumps of testhost and any child process."); - // return new NetClientCrashDumper(); + if (!isNet50OrNewer) + { + EqtTrace.Info($"CrashDumperFactory: This is Windows on {targetFramework} which is not net5.0 or newer, returning ProcDumpCrashDumper that uses ProcDump utility."); + return new ProcDumpCrashDumper(); + } + + EqtTrace.Info($"CrashDumperFactory: This is Windows on {targetFramework}, returning the .NETClient dumper which uses env variables to collect crashdumps of testhost and any child process."); + return new NetClientCrashDumper(); } if (isNet50OrNewer) diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.Designer.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.Designer.cs index 5918a0cae6..0a7b1ff5e9 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.Designer.cs +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.Designer.cs @@ -106,7 +106,7 @@ internal static string InactivityTimeout { } /// - /// Looks up a localized string similar to All tests finished running, Sequence file will not be generated.. + /// Looks up a localized string similar to All tests finished running, Sequence file will not be generated. /// internal static string NotGeneratingSequenceFile { get { diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.resx b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.resx index 7918d18973..20041e7dcf 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.resx +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.resx @@ -133,8 +133,8 @@ The specified inactivity time of {0} minute/s has elapsed. Collecting a dump and killing the test host process. - All tests finished running, Sequence file will not be generated. - "Sequence" is the name of the file. + All tests finished running, Sequence file will not be generated + "Sequence" is the name of the file. No . at the end, because this is a blame message and the . will be added automatically. Could not start process dump: {0} diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.cs.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.cs.xlf index fc1bd5a73e..1aebce1522 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.cs.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.cs.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.de.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.de.xlf index 67502cf46b..afa37f91b9 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.de.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.de.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.es.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.es.xlf index eb40bf814a..9bf5ac9a94 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.es.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.es.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.fr.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.fr.xlf index e282169af4..a6a012b8f1 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.fr.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.fr.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.it.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.it.xlf index 74951f49e1..7ba7520eae 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.it.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.it.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ja.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ja.xlf index 5082c851e6..d9f4ea0ce7 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ja.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ja.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ko.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ko.xlf index 7a4dca0d94..5f731f80b3 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ko.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ko.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pl.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pl.xlf index 4c375ebe9b..eb4dd57d5b 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pl.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pl.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pt-BR.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pt-BR.xlf index 8684edf7a7..22816e8d10 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pt-BR.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pt-BR.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ru.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ru.xlf index 92e1a6d39f..734804eb7e 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ru.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ru.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.tr.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.tr.xlf index c53dabb6d6..34441a0205 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.tr.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.tr.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.xlf index 62e0f8a38c..3568bcd081 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.xlf @@ -48,7 +48,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hans.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hans.xlf index 93ecfe6116..ce63ea223f 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hans.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hans.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file. diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hant.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hant.xlf index 19ea289d05..b54eff9f49 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hant.xlf +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hant.xlf @@ -66,7 +66,7 @@ - All tests finished running, Sequence file will not be generated. + All tests finished running, Sequence file will not be generated All tests finished running, Sequence file will not be generated. "Sequence" is the name of the file.