From b30d6fda919f09f2b3dcb1fa4f6edbe4eb83ba22 Mon Sep 17 00:00:00 2001 From: dotnet-maestro-bot Date: Wed, 25 Sep 2019 10:10:33 -0700 Subject: [PATCH] Capture process dumps for child dotnet processes (dotnet/aspnetcore-tooling#1170) (dotnet/aspnetcore-tooling#1171) \n\nCommit migrated from https://github.com/dotnet/aspnetcore-tooling/commit/00e5f3e4d1802d284b75425a6b9ddd1beada9e78 --- .../IntegrationTests/MSBuildProcessManager.cs | 74 +++++++++++++++---- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/MSBuildProcessManager.cs b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/MSBuildProcessManager.cs index 97000aa280..d5d9ee61de 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/MSBuildProcessManager.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/MSBuildProcessManager.cs @@ -76,6 +76,9 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests var output = new StringBuilder(); var outputLock = new object(); + var diagnostics = new StringBuilder(); + diagnostics.AppendLine("Process execution diagnostics:"); + process.ErrorDataReceived += Process_ErrorDataReceived; process.OutputDataReceived += Process_OutputDataReceived; @@ -83,7 +86,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests process.BeginOutputReadLine(); process.BeginErrorReadLine(); - var timeoutTask = GetTimeoutForProcess(process, timeout); + var timeoutTask = GetTimeoutForProcess(process, timeout, diagnostics); var waitTask = Task.Run(() => { @@ -100,10 +103,19 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests process.WaitForExit(); + string outputString; lock (outputLock) { - outputString = output.ToString(); + // This marks the end of the diagnostic info which we collect when something goes wrong. + diagnostics.AppendLine("Process output:"); + + // Expected output + // Process execution diagnostics: + // ... + // Process output: + outputString = diagnostics.ToString(); + outputString += output.ToString(); } var result = new ProcessResult(process.StartInfo.FileName, process.StartInfo.Arguments, process.ExitCode, outputString); @@ -128,7 +140,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } } - async Task GetTimeoutForProcess(Process process, TimeSpan? timeout) + async Task GetTimeoutForProcess(Process process, TimeSpan? timeout, StringBuilder diagnostics) { await Task.Delay(timeout.Value); @@ -139,12 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } if (!process.HasExited) { - var procDumpProcess = await CaptureDump(process); - if (procDumpProcess != null && procDumpProcess.HasExited) - { - Console.WriteLine("ProcDump failed to run."); - procDumpProcess.Kill(); - } + await CollectDumps(process, timeout, diagnostics); // This is a timeout. process.Kill(); @@ -153,7 +160,46 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests throw new TimeoutException($"command '${process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out after {timeout}. Output: {output.ToString()}"); } - async Task CaptureDump(Process process) + static async Task CollectDumps(Process process, TimeSpan? timeout, StringBuilder diagnostics) + { + var procDumpProcess = await CaptureDump(process, timeout, diagnostics); + var allDotNetProcesses = Process.GetProcessesByName("dotnet"); + + var allDotNetChildProcessCandidates = allDotNetProcesses + .Where(p => p.StartTime >= process.StartTime && p.Id != process.Id); + + if (!allDotNetChildProcessCandidates.Any()) + { + diagnostics.AppendLine("Couldn't find any candidate child process."); + foreach (var dotnetProcess in allDotNetProcesses) + { + diagnostics.AppendLine($"Found dotnet process with PID {dotnetProcess.Id} and start time {dotnetProcess.StartTime}."); + } + } + + foreach (var childProcess in allDotNetChildProcessCandidates) + { + diagnostics.AppendLine($"Found child process candidate '{childProcess.Id}'."); + } + + var childrenProcessDumpProcesses = await Task.WhenAll(allDotNetChildProcessCandidates.Select(d => CaptureDump(d, timeout, diagnostics))); + foreach (var childProcess in childrenProcessDumpProcesses) + { + if (childProcess != null && childProcess.HasExited) + { + diagnostics.AppendLine($"ProcDump failed to run for child dotnet process candidate '{process.Id}'."); + childProcess.Kill(); + } + } + + if (procDumpProcess != null && procDumpProcess.HasExited) + { + diagnostics.AppendLine($"ProcDump failed to run for '{process.Id}'."); + procDumpProcess.Kill(); + } + } + + static async Task CaptureDump(Process process, TimeSpan? timeout, StringBuilder diagnostics) { var metadataAttributes = Assembly.GetExecutingAssembly() .GetCustomAttributes(); @@ -163,13 +209,13 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests if (string.IsNullOrEmpty(procDumpPath)) { - Console.WriteLine("ProcDumpPath not defined."); + diagnostics.AppendLine("ProcDumpPath not defined."); return null; } var procDumpExePath = Path.Combine(procDumpPath, "procdump.exe"); if (!File.Exists(procDumpExePath)) { - Console.WriteLine($"Can't find procdump.exe in '{procDumpPath}'."); + diagnostics.AppendLine($"Can't find procdump.exe in '{procDumpPath}'."); return null; } @@ -178,13 +224,13 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests if (string.IsNullOrEmpty(dumpDirectory)) { - Console.WriteLine("ArtifactsLogDir not defined."); + diagnostics.AppendLine("ArtifactsLogDir not defined."); return null; } if (!Directory.Exists(dumpDirectory)) { - Console.WriteLine($"'{dumpDirectory}' does not exist."); + diagnostics.AppendLine($"'{dumpDirectory}' does not exist."); return null; }