public static async Task KillTreeAsync(int pid, Log log, bool?diagnostics = true) { var pids = new List <int> (); GetChildrenPS(log, pids, pid); if (diagnostics == true) { log.WriteLine($"Pids to kill: {string.Join (", ", pids.Select ((v) => v.ToString ()).ToArray ())}"); using (var ps = new Process()) { log.WriteLine("Writing process list:"); ps.StartInfo.FileName = "ps"; ps.StartInfo.Arguments = "-A -o pid,ruser,ppid,pgid,%cpu=%CPU,%mem=%MEM,flags=FLAGS,lstart,rss,vsz,tty,state,time,command"; await ps.RunAsync(log, true, TimeSpan.FromSeconds(5), diagnostics : false); } foreach (var diagnose_pid in pids) { var template = Path.GetTempFileName(); try { var commands = new StringBuilder(); using (var dbg = new Process()) { commands.AppendLine($"process attach --pid {diagnose_pid}"); commands.AppendLine("thread list"); commands.AppendLine("thread backtrace all"); commands.AppendLine("detach"); commands.AppendLine("quit"); dbg.StartInfo.FileName = "/usr/bin/lldb"; dbg.StartInfo.Arguments = $"--source {StringUtils.Quote (template)}"; File.WriteAllText(template, commands.ToString()); log.WriteLine($"Printing backtrace for pid={pid}"); await dbg.RunAsync(log, true, TimeSpan.FromSeconds(30), diagnostics : false); } } finally { try { File.Delete(template); } catch { // Don't care } } } } // Send SIGABRT since that produces a crash report // lldb may fail to attach to system processes, but crash reports will still be produced with potentially helpful stack traces. for (int i = 0; i < pids.Count; i++) { ProcessHelper.kill(pids [i], 6); } // send kill -9 anyway as a last resort for (int i = 0; i < pids.Count; i++) { ProcessHelper.kill(pids [i], 9); } }
public static async Task <ProcessExecutionResult> RunAsync(this Process process, TextWriter log, TextWriter StdoutStream, TextWriter StderrStream, TimeSpan?timeout = null, Dictionary <string, string> environment_variables = null, CancellationToken?cancellation_token = null) { var stdout_completion = new TaskCompletionSource <bool> (); var stderr_completion = new TaskCompletionSource <bool> (); var exit_completion = new TaskCompletionSource <bool> (); var rv = new ProcessExecutionResult(); process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.UseShellExecute = false; if (environment_variables != null) { foreach (var kvp in environment_variables) { process.StartInfo.EnvironmentVariables [kvp.Key] = kvp.Value; } } process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => { if (e.Data != null) { lock (StdoutStream) { StdoutStream.WriteLine(e.Data); StdoutStream.Flush(); } } else { stdout_completion.SetResult(true); } }; process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => { if (e.Data != null) { lock (StderrStream) { StderrStream.WriteLine(e.Data); StderrStream.Flush(); } } else { stderr_completion.SetResult(true); } }; log.WriteLine("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); cancellation_token?.Register(() => { if (!exit_completion.Task.IsCompleted) { StderrStream.WriteLine($"Execution was cancelled."); ProcessHelper.kill(process.Id, 9); } }); new Thread(() => { if (timeout.HasValue) { if (!process.WaitForExit((int)timeout.Value.TotalMilliseconds)) { ProcessHelper.kill(process.Id, 9); process.WaitForExit((int)TimeSpan.FromSeconds(5).TotalMilliseconds); // Wait 5s for the kill to work rv.TimedOut = true; lock (StderrStream) log.WriteLine($"Execution timed out after {timeout.Value.TotalSeconds} seconds and the process was killed."); } } else { process.WaitForExit(); } exit_completion.TrySetResult(true); }) { IsBackground = true, }.Start(); await Task.WhenAll(stderr_completion.Task, stdout_completion.Task, exit_completion.Task); rv.ExitCode = process.ExitCode; return(rv); }
public static async Task<ProcessExecutionResult> RunAsync (this Process process, Log log, TextWriter StdoutStream, TextWriter StderrStream, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null) { var stdout_completion = new TaskCompletionSource<bool> (); var stderr_completion = new TaskCompletionSource<bool> (); var rv = new ProcessExecutionResult (); process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.UseShellExecute = false; if (environment_variables != null) { foreach (var kvp in environment_variables) process.StartInfo.EnvironmentVariables [kvp.Key] = kvp.Value; } process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => { if (e.Data != null) { lock (StdoutStream) { StdoutStream.WriteLine (e.Data); StdoutStream.Flush (); } } else { stdout_completion.TrySetResult (true); } }; process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => { if (e.Data != null) { lock (StderrStream) { StderrStream.WriteLine (e.Data); StderrStream.Flush (); } } else { stderr_completion.TrySetResult (true); } }; var sb = new StringBuilder (); if (process.StartInfo.EnvironmentVariables != null) { var currentEnvironment = Environment.GetEnvironmentVariables ().Cast<System.Collections.DictionaryEntry> ().ToDictionary ((v) => (string) v.Key, (v) => (string) v.Value, StringComparer.Ordinal); var processEnvironment = process.StartInfo.EnvironmentVariables.Cast<System.Collections.DictionaryEntry> ().ToDictionary ((v) => (string) v.Key, (v) => (string) v.Value, StringComparer.Ordinal); var allKeys = currentEnvironment.Keys.Union (processEnvironment.Keys).Distinct (); foreach (var key in allKeys) { string a = null, b = null; currentEnvironment.TryGetValue (key, out a); processEnvironment.TryGetValue (key, out b); if (a != b) sb.Append ($"{key}={StringUtils.Quote (b)} "); } } sb.Append ($"{StringUtils.Quote (process.StartInfo.FileName)} {process.StartInfo.Arguments}"); log.WriteLine (sb); process.Start (); var pid = process.Id; process.BeginErrorReadLine (); process.BeginOutputReadLine (); cancellation_token?.Register (() => { if (!process.HasExited) { StderrStream.WriteLine ($"Execution was cancelled."); ProcessHelper.kill (process.Id, 9); } }); if (timeout.HasValue) { if (!await process.WaitForExitAsync (timeout.Value)) { await process.KillTreeAsync (log, diagnostics ?? true); rv.TimedOut = true; lock (StderrStream) log.WriteLine ($"{pid} Execution timed out after {timeout.Value.TotalSeconds} seconds and the process was killed."); } } await process.WaitForExitAsync (); Task.WaitAll (new Task [] { stderr_completion.Task, stdout_completion.Task }, TimeSpan.FromSeconds (1)); try { rv.ExitCode = process.ExitCode; } catch (Exception e) { rv.ExitCode = 12345678; log.WriteLine ($"Failed to get ExitCode: {e}"); } return rv; }