[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:
parent
a2081d584f
commit
bcd542a1bb
|
|
@ -83,22 +83,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
process.BeginOutputReadLine();
|
process.BeginOutputReadLine();
|
||||||
process.BeginErrorReadLine();
|
process.BeginErrorReadLine();
|
||||||
|
|
||||||
var timeoutTask = Task.Delay(timeout.Value).ContinueWith<ProcessResult>((t) =>
|
var timeoutTask = GetTimeoutForProcess(process, timeout);
|
||||||
{
|
|
||||||
// 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 waitTask = Task.Run(() =>
|
var waitTask = Task.Run(() =>
|
||||||
{
|
{
|
||||||
|
|
@ -142,6 +127,87 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
output.AppendLine(e.Data);
|
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
|
internal class ProcessResult
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,16 @@
|
||||||
<_Parameter2>$(MSBuildThisFileDirectory)..\testapps\TestPackageRestoreSource</_Parameter2>
|
<_Parameter2>$(MSBuildThisFileDirectory)..\testapps\TestPackageRestoreSource</_Parameter2>
|
||||||
</AssemblyAttribute>
|
</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>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- The test projects rely on these binaries being available -->
|
<!-- 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)" />
|
<MSBuild Projects="..\testapps\RestoreTestProjects\RestoreTestProjects.csproj" Targets="Restore" Properties="MicrosoftNetCompilersToolsetPackageVersion=$(MicrosoftNetCompilersToolsetPackageVersion)" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="EnsureLogFolder" AfterTargets="Build">
|
||||||
|
<MakeDir Directories="$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'log', '$(_BuildConfiguration)'))" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue