public static void TakeMemoryDump(this Process process, string outputFolderPath, ITestOutputHelper output) { // this environment variable is set by Github Actions CI to `true` https://docs.github.com/en/actions/learn-github-actions/environment-variables string ciEnvVar = Environment.GetEnvironmentVariable("CI"); if (!bool.TryParse(ciEnvVar, out var result) || !result) { output.WriteLine("^^^^^^^^^^^^^^^^^^^^^^ Currently not running in Github Actions CI. No memory dump will be taken."); return; } output.WriteLine($"^^^^^^^^^^^^^^^^^^^^^^ Taking memory dump of process Id {process.Id}..."); using var processDump = new Process(); if (EnvironmentHelper.IsRunningOnWindows()) { // In Github Actions CI, we downloaded, extracted procdump. We also add its folder to the PATH processDump.StartInfo.FileName = Environment.Is64BitProcess ? "procdump64.exe" : "procdump.exe"; processDump.StartInfo.Arguments = $@"-ma {process.Id} -accepteula"; } else { processDump.StartInfo.FileName = "dotnet-dump"; processDump.StartInfo.Arguments = $"collect --process-id {process.Id}"; } processDump.StartInfo.UseShellExecute = false; processDump.StartInfo.CreateNoWindow = true; processDump.StartInfo.RedirectStandardOutput = true; processDump.StartInfo.RedirectStandardError = true; processDump.StartInfo.RedirectStandardInput = false; processDump.StartInfo.WorkingDirectory = outputFolderPath; processDump.Start(); using var helper = new ProcessHelper(processDump); bool ranToCompletion = processDump.WaitForExit(MemoryDumpDurationMs) && helper.Drain(MemoryDumpDurationMs / 2); if (!ranToCompletion) { output.WriteLine(" Failed to take a memory dump."); if (!processDump.HasExited) { output.WriteLine($" Dumping tool (Id {processDump.Id}) has not exited. Terminating it."); try { processDump.KillTree(); } catch { // do nothing } } } else { output.WriteLine($" Memory dump successfully taken for process {process.Id}"); } var standardOutput = helper.StandardOutput; if (!string.IsNullOrWhiteSpace(standardOutput)) { output.WriteLine($" Memory dump tool output: {Environment.NewLine}{standardOutput}"); } var errorOutput = helper.ErrorOutput; if (!string.IsNullOrWhiteSpace(errorOutput)) { output.WriteLine($" Memory dump tool error: {Environment.NewLine}{errorOutput}"); } }
private void RunTest(int agentPort) { (var executor, var arguments) = BuildTestCommandLine(); using var process = new Process(); SetEnvironmentVariables(process.StartInfo.EnvironmentVariables, agentPort); process.StartInfo.FileName = executor; process.StartInfo.Arguments = arguments; process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardInput = false; process.Start(); using var processHelper = new ProcessHelper(process); var ranToCompletion = process.WaitForExit((int)_maxTestRunDuration.TotalMilliseconds) && processHelper.Drain((int)_maxTestRunDuration.TotalMilliseconds / 2); var standardOutput = processHelper.StandardOutput; var errorOutput = processHelper.ErrorOutput; if (!ranToCompletion) { if (!process.HasExited) { process.GetAllThreadsStack(_testBaseOutputDir, _output); process.TakeMemoryDump(_testBaseOutputDir, _output); try { process.KillTree(); } catch { // do nothing } _output.WriteLine($"The test {_appName} is running for too long (more than {_maxTestRunDuration.TotalSeconds} seconds) or was lost."); _output.WriteLine(standardOutput); _output.WriteLine(errorOutput); throw new TimeoutException($"The test {_appName} is running for too long or was lost"); } } if (!string.IsNullOrWhiteSpace(standardOutput)) { _output.WriteLine($"[TestRunner] Standard output: {standardOutput}"); Assert.False(standardOutput.Contains("[Error]"), "An error occured during the test. See the standard output above."); } if (!string.IsNullOrWhiteSpace(errorOutput)) { _output.WriteLine($"[TestRunner] Error output: {errorOutput}"); Assert.False(errorOutput.Contains("[Error]"), "An error occured during the test. See the error output above."); } Assert.True( 0 == process.ExitCode, $"Exit code of \"{Path.GetFileName(process.StartInfo?.FileName ?? string.Empty)}\" should be 0 instead of {process.ExitCode} (= 0x{process.ExitCode.ToString("X")})"); }