private bool WaitForProcessToFinish(Process process, ProcessRunnerArguments runnerArgs) { process.BeginErrorReadLine(); process.BeginOutputReadLine(); // Warning: do not log the raw command line args as they // may contain sensitive data LogDebug(CFamilyStrings.MSG_ExecutingFile, runnerArgs.ExeName, runnerArgs.AsLogText(), runnerArgs.WorkingDirectory, runnerArgs.TimeoutInMilliseconds, process.Id); var succeeded = process.WaitForExit(runnerArgs.TimeoutInMilliseconds); if (succeeded) { process.WaitForExit(); // Give any asynchronous events the chance to complete } // false means we asked the process to stop but it didn't. // true: we might still have timed out, but the process ended when we asked it to if (succeeded) { LogDebug(CFamilyStrings.MSG_ExecutionExitCode, process.ExitCode); ExitCode = process.ExitCode; if (process.ExitCode != 0 && !runnerArgs.CancellationToken.IsCancellationRequested) { LogError(CFamilyStrings.ERROR_ProcessRunner_Failed, process.ExitCode); } } else { ExitCode = ErrorCode; try { process.Kill(); LogWarning(CFamilyStrings.WARN_ExecutionTimedOutKilled, runnerArgs.TimeoutInMilliseconds, runnerArgs.ExeName); } catch { LogWarning(CFamilyStrings.WARN_ExecutionTimedOutNotKilled, runnerArgs.TimeoutInMilliseconds, runnerArgs.ExeName); } } return(succeeded && ExitCode == 0); }
internal /* for testing */ static void CallClangAnalyzer(Action <Message> handleMessage, Request request, IProcessRunner runner, ILogger logger, CancellationToken cancellationToken) { if (analyzerExeFilePath == null) { logger.WriteLine("Unable to locate the CFamily analyzer exe"); return; } const string communicateViaStreaming = "-"; // signal the subprocess we want to communicate via standard IO streams. var args = new ProcessRunnerArguments(analyzerExeFilePath, false) { CmdLineArgs = new[] { communicateViaStreaming }, CancellationToken = cancellationToken, WorkingDirectory = WorkingDirectory, HandleInputStream = writer => { using (var binaryWriter = new BinaryWriter(writer.BaseStream)) { Protocol.Write(binaryWriter, request); } }, HandleOutputStream = reader => { if ((request.Flags & Request.CreateReproducer) != 0) { reader.ReadToEnd(); logger.WriteLine(CFamilyStrings.MSG_ReproducerSaved, ReproducerFilePath); } else if ((request.Flags & Request.BuildPreamble) != 0) { reader.ReadToEnd(); logger.WriteLine(CFamilyStrings.MSG_PchSaved, request.File, request.PchFile); } else { using (var binaryReader = new BinaryReader(reader.BaseStream)) { Protocol.Read(binaryReader, handleMessage, request.File); } } } }; runner.Execute(args); }
private static bool ExecuteAnalysis(IProcessRunner runner, string fileName, ILogger logger) { if (analyzerExeFilePath == null) { logger.WriteLine("Unable to locate the CFamily analyzer exe"); return(false); } ProcessRunnerArguments args = new ProcessRunnerArguments(analyzerExeFilePath, false); args.CmdLineArgs = new string[] { fileName }; args.TimeoutInMilliseconds = GetTimeoutInMs(); bool success = runner.Execute(args); return(success); }
private static bool ExecuteAnalysis(IProcessRunner runner, string fileName, string workingDirectory, ILogger logger, CancellationToken cancellationToken) { if (analyzerExeFilePath == null) { logger.WriteLine("Unable to locate the CFamily analyzer exe"); return(false); } var args = new ProcessRunnerArguments(analyzerExeFilePath, false) { CmdLineArgs = new[] { fileName }, TimeoutInMilliseconds = GetTimeoutInMs(), CancellationToken = cancellationToken, WorkingDirectory = workingDirectory }; var success = runner.Execute(args); return(success); }
/// <summary> /// Runs the specified executable and returns a boolean indicating success or failure /// </summary> /// <remarks>The standard and error output will be streamed to the logger. Child processes do not inherit the env variables from the parent automatically</remarks> public bool Execute(ProcessRunnerArguments runnerArgs) { if (runnerArgs == null) { throw new ArgumentNullException(nameof(runnerArgs)); } Debug.Assert(!string.IsNullOrWhiteSpace(runnerArgs.ExeName), "Process runner exe name should not be null/empty"); if (!File.Exists(runnerArgs.ExeName)) { LogError(CFamilyStrings.ERROR_ProcessRunner_ExeNotFound, runnerArgs.ExeName); ExitCode = ErrorCode; return(false); } var psi = new ProcessStartInfo { FileName = runnerArgs.ExeName, RedirectStandardError = true, RedirectStandardOutput = true, UseShellExecute = false, // required if we want to capture the error output ErrorDialog = false, CreateNoWindow = true, Arguments = runnerArgs.GetEscapedArguments(), WorkingDirectory = runnerArgs.WorkingDirectory }; SetEnvironmentVariables(psi, runnerArgs.EnvironmentVariables); var hasProcessStarted = false; using (var process = new Process()) using (runnerArgs.CancellationToken.Register(() => { LogMessage(CFamilyStrings.MSG_ExecutionCancelled); lock (process) { if (!hasProcessStarted) { // Cancellation was requested before process started - do nothing return; } } // Cancellation was requested after process started - kill it KillProcess(process); })) { process.StartInfo = psi; process.ErrorDataReceived += OnErrorDataReceived; process.OutputDataReceived += OnOutputDataReceived; lock (process) { if (!runnerArgs.CancellationToken.IsCancellationRequested) { process.Start(); hasProcessStarted = true; } else { LogMessage(CFamilyStrings.MSG_ExecutionCancelled); return(false); } } var result = WaitForProcessToFinish(process, runnerArgs); return(result); } }
/// <summary> /// Runs the specified executable and returns a boolean indicating success or failure /// </summary> /// <remarks>The standard and error output will be streamed to the logger. Child processes do not inherit the env variables from the parent automatically</remarks> public bool Execute(ProcessRunnerArguments runnerArgs) { if (runnerArgs == null) { throw new ArgumentNullException(nameof(runnerArgs)); } Debug.Assert(!string.IsNullOrWhiteSpace(runnerArgs.ExeName), "Process runner exe name should not be null/empty"); if (!File.Exists(runnerArgs.ExeName)) { LogError(CFamilyStrings.ERROR_ProcessRunner_ExeNotFound, runnerArgs.ExeName); ExitCode = ErrorCode; return(false); } var psi = new ProcessStartInfo() { FileName = runnerArgs.ExeName, RedirectStandardError = true, RedirectStandardOutput = true, UseShellExecute = false, // required if we want to capture the error output ErrorDialog = false, CreateNoWindow = true, Arguments = runnerArgs.GetEscapedArguments(), WorkingDirectory = runnerArgs.WorkingDirectory }; SetEnvironmentVariables(psi, runnerArgs.EnvironmentVariables); bool succeeded; using (var process = new Process()) { process.StartInfo = psi; process.ErrorDataReceived += OnErrorDataReceived; process.OutputDataReceived += OnOutputDataReceived; process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); // Warning: do not log the raw command line args as they // may contain sensitive data LogDebug(CFamilyStrings.MSG_ExecutingFile, runnerArgs.ExeName, runnerArgs.AsLogText(), runnerArgs.WorkingDirectory, runnerArgs.TimeoutInMilliseconds, process.Id); succeeded = process.WaitForExit(runnerArgs.TimeoutInMilliseconds); if (succeeded) { process.WaitForExit(); // Give any asynchronous events the chance to complete } // false means we asked the process to stop but it didn't. // true: we might still have timed out, but the process ended when we asked it to if (succeeded) { LogDebug(CFamilyStrings.MSG_ExecutionExitCode, process.ExitCode); ExitCode = process.ExitCode; } else { ExitCode = ErrorCode; try { process.Kill(); LogWarning(CFamilyStrings.WARN_ExecutionTimedOutKilled, runnerArgs.TimeoutInMilliseconds, runnerArgs.ExeName); } catch { LogWarning(CFamilyStrings.WARN_ExecutionTimedOutNotKilled, runnerArgs.TimeoutInMilliseconds, runnerArgs.ExeName); } } succeeded = succeeded && (ExitCode == 0); } return(succeeded); }
/// <summary> /// Runs the specified executable and communicates with it via Standard IO streams. /// The method blocks until the handler has read to the end of the output stream, or when the cancellation token is cancelled. /// </summary> /// <remarks> /// Child processes do not inherit the env variables from the parent automatically. /// The stream reader callbacks are executed on the original calling thread. /// Errors and timeouts are written to the logger, which in turn writes to the output window. The caller won't see them and has no way of checking the outcome. /// </remarks> public void Execute(ProcessRunnerArguments runnerArgs) { if (runnerArgs == null) { throw new ArgumentNullException(nameof(runnerArgs)); } Debug.Assert(!string.IsNullOrWhiteSpace(runnerArgs.ExeName), "Process runner exe name should not be null/empty"); if (!File.Exists(runnerArgs.ExeName)) { LogError(CFamilyStrings.ERROR_ProcessRunner_ExeNotFound, runnerArgs.ExeName); ExitCode = ErrorCode; return; } var psi = new ProcessStartInfo { FileName = runnerArgs.ExeName, RedirectStandardError = true, RedirectStandardOutput = true, RedirectStandardInput = true, UseShellExecute = false, // required if we want to capture the error output ErrorDialog = false, CreateNoWindow = true, Arguments = runnerArgs.GetEscapedArguments(), WorkingDirectory = runnerArgs.WorkingDirectory }; SetEnvironmentVariables(psi, runnerArgs.EnvironmentVariables); var hasProcessStarted = false; var isRunningProcessCancelled = false; using (var process = new Process()) using (runnerArgs.CancellationToken.Register(() => { LogMessage(CFamilyStrings.MSG_ExecutionCancelled); lock (process) { if (!hasProcessStarted) { // Cancellation was requested before process started - do nothing return; } } // Cancellation was requested after process started - kill it isRunningProcessCancelled = true; KillProcess(process); })) { process.ErrorDataReceived += OnErrorDataReceived; process.StartInfo = psi; lock (process) { if (!runnerArgs.CancellationToken.IsCancellationRequested) { process.Start(); hasProcessStarted = true; } else { LogMessage(CFamilyStrings.MSG_ExecutionCancelled); return; } } process.BeginErrorReadLine(); // Warning: do not log the raw command line args as they // may contain sensitive data LogDebug(CFamilyStrings.MSG_ExecutingFile, runnerArgs.ExeName, runnerArgs.AsLogText(), runnerArgs.WorkingDirectory, process.Id); try { runnerArgs.HandleInputStream?.Invoke(process.StandardInput); // the caller needs to start a blocking read operation, otherwise the method would exit. runnerArgs.HandleOutputStream?.Invoke(process.StandardOutput); // Give any asynchronous events the chance to complete process.WaitForExit(); ExitCode = process.ExitCode; LogDebug(CFamilyStrings.MSG_ExecutionExitCode, process.ExitCode); } catch (Exception ex) when(isRunningProcessCancelled && !ErrorHandler.IsCriticalException(ex)) { // If a process is cancelled mid-stream, an exception will be thrown. } } }