/// <summary> /// Invoked when a timeout occurs and we need to dump all of the test processes and shut down /// the runnner. /// </summary> private static async Task HandleTimeout(Options options, CancellationToken cancellationToken) { async Task DumpProcess(Process targetProcess, string procDumpExeFilePath, string dumpFilePath) { var name = targetProcess.ProcessName; // Our space for saving dump files is limited. Skip dumping for processes that won't contribute // to bug investigations. if (name == "procdump" || name == "conhost") { return; } ConsoleUtil.Write($"Dumping {name} {targetProcess.Id} to {dumpFilePath} ... "); try { var args = $"-accepteula -ma {targetProcess.Id} {dumpFilePath}"; var processInfo = ProcessRunner.CreateProcess(procDumpExeFilePath, args, cancellationToken: cancellationToken); var processOutput = await processInfo.Result; // The exit code for procdump doesn't obey standard windows rules. It will return non-zero // for successful cases (possibly returning the count of dumps that were written). Best // backup is to test for the dump file being present. if (File.Exists(dumpFilePath)) { ConsoleUtil.WriteLine($"succeeded ({new FileInfo(dumpFilePath).Length} bytes)"); } else { ConsoleUtil.WriteLine($"FAILED with {processOutput.ExitCode}"); ConsoleUtil.WriteLine($"{procDumpExeFilePath} {args}"); ConsoleUtil.WriteLine(string.Join(Environment.NewLine, processOutput.OutputLines)); } } catch (Exception ex) when(!cancellationToken.IsCancellationRequested) { ConsoleUtil.WriteLine("FAILED"); ConsoleUtil.WriteLine(ex.Message); Logger.Log("Failed to dump process", ex); } } ConsoleUtil.WriteLine("Roslyn Error: test timeout exceeded, dumping remaining processes"); var procDumpInfo = GetProcDumpInfo(options); if (procDumpInfo != null) { var counter = 0; foreach (var proc in ProcessUtil.GetProcessTree(Process.GetCurrentProcess()).OrderBy(x => x.ProcessName)) { var dumpDir = PrimaryProcessNames.Contains(proc.ProcessName) ? procDumpInfo.Value.DumpDirectory : procDumpInfo.Value.SecondaryDumpDirectory; var dumpFilePath = Path.Combine(dumpDir, $"{proc.ProcessName}-{counter}.dmp"); await DumpProcess(proc, procDumpInfo.Value.ProcDumpFilePath, dumpFilePath); counter++; } } else { ConsoleUtil.WriteLine("Could not locate procdump"); } WriteLogFile(options); }
internal async Task <RunAllResult> RunAllAsync(IEnumerable <AssemblyInfo> assemblyInfoList, CancellationToken cancellationToken) { // Use 1.5 times the number of processors for unit tests, but only 1 processor for the open integration tests // since they perform actual UI operations (such as mouse clicks and sending keystrokes) and we don't want two // tests to conflict with one-another. var max = (_options.TestVsi) ? 1 : (int)(Environment.ProcessorCount * 1.5); var cacheCount = 0; var waiting = new Stack <AssemblyInfo>(assemblyInfoList); var running = new List <Task <TestResult> >(); var completed = new List <TestResult>(); var failures = 0; do { cancellationToken.ThrowIfCancellationRequested(); var i = 0; while (i < running.Count) { var task = running[i]; if (task.IsCompleted) { try { var testResult = await task.ConfigureAwait(false); if (!testResult.Succeeded) { failures++; } if (testResult.IsFromCache) { cacheCount++; } completed.Add(testResult); } catch (Exception ex) { ConsoleUtil.WriteLine($"Error: {ex.Message}"); failures++; } running.RemoveAt(i); } else { i++; } } while (running.Count < max && waiting.Count > 0) { var task = _testExecutor.RunTestAsync(waiting.Pop(), cancellationToken); running.Add(task); } // Display the current status of the TestRunner. // Note: The { ... , 2 } is to right align the values, thus aligns sections into columns. ConsoleUtil.Write($" {running.Count,2} running, {waiting.Count,2} queued, {completed.Count,2} completed"); if (failures > 0) { ConsoleUtil.Write($", {failures,2} failures"); } ConsoleUtil.WriteLine(); if (running.Count > 0) { await Task.WhenAny(running.ToArray()); } } while (running.Count > 0); Print(completed); var processResults = ImmutableArray.CreateBuilder <ProcessResult>(); foreach (var c in completed) { processResults.AddRange(c.ProcessResults); } return(new RunAllResult((failures == 0), cacheCount, completed.ToImmutableArray(), processResults.ToImmutable())); }