diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/NetClientHangDumper.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/NetClientHangDumper.cs index ef2f75ac0d..2a7d0e16d6 100644 --- a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/NetClientHangDumper.cs +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/NetClientHangDumper.cs @@ -4,12 +4,9 @@ namespace Microsoft.TestPlatform.Extensions.BlameDataCollector { using System; - using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; - using System.Threading; - using System.Threading.Tasks; using Microsoft.Diagnostics.NETCore.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.Utilities; @@ -43,45 +40,35 @@ public void Dump(int processId, string outputDirectory, DumpTypeOption type) var bottomUpTree = processTree.OrderByDescending(t => t.Level).Select(t => t.Process); - // Do not suspend processes with NetClient dumper it stops the diagnostic thread running in - // them and hang dump request will get stuck forever, because the process is not co-operating. - // Instead we start one task per dump asynchronously, and hope that the parent process will start dumping - // before the child process is done dumping. This way if the parent is waiting for the children to exit, - // we will be dumping it before it observes the child exiting and we get a more accurate results. If we did not - // do this, then parent that is awaiting child might exit before we get to dumping it. - var tasks = new List(); - var timeout = new CancellationTokenSource(); - timeout.CancelAfter(TimeSpan.FromMinutes(5)); foreach (var p in bottomUpTree) { - tasks.Add(Task.Run( - () => - { - try - { - var outputFile = Path.Combine(outputDirectory, $"{p.ProcessName}_{p.Id}_{DateTime.Now:yyyyMMddTHHmmss}_hangdump.dmp"); - EqtTrace.Verbose($"NetClientHangDumper.CollectDump: Selected dump type {type}. Dumping {process.Id} - {process.ProcessName} in {outputFile}. "); - - var client = new DiagnosticsClient(p.Id); - - // Connecting the dump generation logging to verbose output to avoid changing the interfaces again -> EqtTrace.IsVerboseEnabled - // before we test this on some big repo. - client.WriteDump(type == DumpTypeOption.Full ? DumpType.Full : DumpType.Normal, outputFile, logDumpGeneration: false); - } - catch (Exception ex) - { - EqtTrace.Error($"NetClientHangDumper.Dump: Error dumping process {p.Id} - {p.ProcessName}: {ex}."); - } - }, timeout.Token)); + try + { + p.Suspend(); + } + catch (Exception ex) + { + EqtTrace.Error($"NetClientHangDumper.Dump: Error suspending process {p.Id} - {p.ProcessName}: {ex}."); + } } - try - { - Task.WhenAll(tasks).GetAwaiter().GetResult(); - } - catch (TaskCanceledException) + foreach (var p in bottomUpTree) { - EqtTrace.Error($"NetClientHangDumper.Dump: Hang dump timed out."); + try + { + var outputFile = Path.Combine(outputDirectory, $"{p.ProcessName}_{p.Id}_{DateTime.Now:yyyyMMddTHHmmss}_hangdump.dmp"); + EqtTrace.Verbose($"NetClientHangDumper.CollectDump: Selected dump type {type}. Dumping {process.Id} - {process.ProcessName} in {outputFile}. "); + + var client = new DiagnosticsClient(p.Id); + + // Connecting the dump generation logging to verbose output to avoid changing the interfaces again -> EqtTrace.IsVerboseEnabled + // before we test this on some big repo. + client.WriteDump(type == DumpTypeOption.Full ? DumpType.Full : DumpType.Normal, outputFile, logDumpGeneration: false); + } + catch (Exception ex) + { + EqtTrace.Error($"NetClientHangDumper.Dump: Error dumping process {p.Id} - {p.ProcessName}: {ex}."); + } } foreach (var p in bottomUpTree) @@ -96,6 +83,6 @@ public void Dump(int processId, string outputDirectory, DumpTypeOption type) EqtTrace.Error($"NetClientHangDumper.Dump: Error killing process {p.Id} - {p.ProcessName}: {ex}."); } } - } + } } }