in tools/perf-automation/Azure.Sdk.Tools.PerfAutomation/Microsoft.Crank.Agent/ProcessUtil.cs [21:169]
public static async Task<ProcessResult> RunAsync(
string filename,
string arguments,
TimeSpan? timeout = null,
string workingDirectory = null,
bool throwOnError = true,
IDictionary<string, string> environmentVariables = null,
Action<string> outputDataReceived = null,
bool log = false,
Action<int> onStart = null,
Action<int> onStop = null,
CancellationToken cancellationToken = default(CancellationToken),
bool captureOutput = false,
bool captureError = false
)
{
var logWorkingDirectory = workingDirectory ?? Directory.GetCurrentDirectory();
if (log)
{
Log.WriteLine($"[{logWorkingDirectory}] {filename} {arguments}");
}
using var process = new Process()
{
StartInfo =
{
FileName = filename,
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
},
EnableRaisingEvents = true
};
if (workingDirectory != null)
{
process.StartInfo.WorkingDirectory = workingDirectory;
}
if (environmentVariables != null)
{
foreach (var kvp in environmentVariables)
{
process.StartInfo.Environment.Add(kvp);
}
}
var outputBuilder = new StringBuilder();
process.OutputDataReceived += (_, e) =>
{
if (e.Data != null)
{
if (captureOutput)
{
outputBuilder.AppendLine(e.Data);
}
if (outputDataReceived != null)
{
outputDataReceived.Invoke(e.Data);
}
if (log)
{
Log.WriteLine(e.Data);
}
}
};
var errorBuilder = new StringBuilder();
process.ErrorDataReceived += (_, e) =>
{
if (e.Data != null)
{
if (captureError)
{
errorBuilder.AppendLine(e.Data);
}
if (outputDataReceived != null)
{
outputDataReceived.Invoke(e.Data);
}
Log.WriteLine("[STDERR] " + e.Data);
}
};
var processLifetimeTask = new TaskCompletionSource<ProcessResult>();
process.Exited += (_, e) =>
{
// Even though the Exited event has been raised, WaitForExit() must still be called to ensure the output buffers
// have been flushed before the process is considered completely done.
process.WaitForExit();
if (throwOnError && process.ExitCode != 0)
{
processLifetimeTask.TrySetException(new InvalidOperationException($"Command {filename} {arguments} returned exit code {process.ExitCode}"));
}
else
{
processLifetimeTask.TrySetResult(new ProcessResult(process.ExitCode, outputBuilder.ToString(), errorBuilder.ToString()));
}
};
process.Start();
onStart?.Invoke(process.Id);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
var cancelledTcs = new TaskCompletionSource<object>();
await using var _ = cancellationToken.Register(() => cancelledTcs.TrySetResult(null));
var result = await Task.WhenAny(processLifetimeTask.Task, cancelledTcs.Task, Task.Delay(timeout.HasValue ? (int)timeout.Value.TotalMilliseconds : -1));
if (result != processLifetimeTask.Task)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
sys_kill(process.Id, sig: 2); // SIGINT
var cancel = new CancellationTokenSource();
await Task.WhenAny(processLifetimeTask.Task, Task.Delay(TimeSpan.FromSeconds(5), cancel.Token));
cancel.Cancel();
}
if (!process.HasExited)
{
process.CloseMainWindow();
if (!process.HasExited)
{
process.Kill();
}
}
}
var processResult = await processLifetimeTask.Task;
onStop?.Invoke(processResult.ExitCode);
return processResult;
}