[Infrastructure] Install procdump and capture dumps on build hangs (dotnet/aspnetcore-tooling#1146)

* Captures process dumps when the build hangs.
* Captures process dumps when a child process hangs inside a test.
\n\nCommit migrated from a9b7e24791
This commit is contained in:
Javier Calvarro Nelson 2019-09-19 18:36:02 +02:00 committed by GitHub
parent a2081d584f
commit bcd542a1bb
2 changed files with 96 additions and 16 deletions

View File

@ -83,22 +83,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
process.BeginOutputReadLine();
process.BeginErrorReadLine();
var timeoutTask = Task.Delay(timeout.Value).ContinueWith<ProcessResult>((t) =>
{
// Don't timeout during debug sessions
while (Debugger.IsAttached)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
if (!process.HasExited)
{
// This is a timeout.
process.Kill();
}
throw new TimeoutException($"command '${process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out after {timeout}. Output: {output.ToString()}");
});
var timeoutTask = GetTimeoutForProcess(process, timeout);
var waitTask = Task.Run(() =>
{
@ -142,6 +127,87 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
output.AppendLine(e.Data);
}
}
async Task<ProcessResult> GetTimeoutForProcess(Process process, TimeSpan? timeout)
{
await Task.Delay(timeout.Value);
// Don't timeout during debug sessions
while (Debugger.IsAttached)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
if (!process.HasExited)
{
var procDumpProcess = await CaptureDump(process);
if (procDumpProcess != null && procDumpProcess.HasExited)
{
Console.WriteLine("ProcDump failed to run.");
procDumpProcess.Kill();
}
// This is a timeout.
process.Kill();
}
throw new TimeoutException($"command '${process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out after {timeout}. Output: {output.ToString()}");
}
async Task<Process> CaptureDump(Process process)
{
var metadataAttributes = Assembly.GetExecutingAssembly()
.GetCustomAttributes<AssemblyMetadataAttribute>();
var procDumpPath = metadataAttributes
.SingleOrDefault(ama => ama.Key == "ProcDumpPath")?.Value;
if (string.IsNullOrEmpty(procDumpPath))
{
Console.WriteLine("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}'.");
return null;
}
var dumpDirectory = metadataAttributes
.SingleOrDefault(ama => ama.Key == "ArtifactsLogDir")?.Value;
if (string.IsNullOrEmpty(dumpDirectory))
{
Console.WriteLine("ArtifactsLogDir not defined.");
return null;
}
if (!Directory.Exists(dumpDirectory))
{
Console.WriteLine($"'{dumpDirectory}' does not exist.");
return null;
}
var procDumpPattern = Path.Combine(dumpDirectory, "HangingProcess_PROCESSNAME_PID_YYMMDD_HHMMSS.dmp");
var procDumpStartInfo = new ProcessStartInfo(
procDumpExePath,
$"-accepteula -ma {process.Id} {procDumpPattern}")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
var procDumpProcess = Process.Start(procDumpStartInfo);
var tcs = new TaskCompletionSource<object>();
procDumpProcess.Exited += (s, a) => tcs.TrySetResult(null);
procDumpProcess.EnableRaisingEvents = true;
await Task.WhenAny(tcs.Task, Task.Delay(timeout ?? TimeSpan.FromSeconds(30)));
return procDumpProcess;
}
}
internal class ProcessResult

View File

@ -43,6 +43,16 @@
<_Parameter2>$(MSBuildThisFileDirectory)..\testapps\TestPackageRestoreSource</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>ArtifactsLogDir</_Parameter1>
<_Parameter2>$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'log', '$(_BuildConfiguration)'))</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>ProcDumpToolPath</_Parameter1>
<_Parameter2>$(ProcDumpPath)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>
<!-- The test projects rely on these binaries being available -->
@ -112,4 +122,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
<MSBuild Projects="..\testapps\RestoreTestProjects\RestoreTestProjects.csproj" Targets="Restore" Properties="MicrosoftNetCompilersToolsetPackageVersion=$(MicrosoftNetCompilersToolsetPackageVersion)" />
</Target>
<Target Name="EnsureLogFolder" AfterTargets="Build">
<MakeDir Directories="$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'log', '$(_BuildConfiguration)'))" />
</Target>
</Project>