/// <summary> /// Deserializes an instance of <see cref="SandboxedProcessResult"/>. /// </summary> /// <param name="reader"></param> /// <returns></returns> public static SandboxedProcessResult Deserialize(BuildXLReader reader) { int exitCode = reader.ReadInt32(); bool killed = reader.ReadBoolean(); bool timedOut = reader.ReadBoolean(); bool hasDetoursInjectionFailures = reader.ReadBoolean(); IReadOnlyList <ReportedProcess> allReportedProcesses = reader.ReadReadOnlyList(r => ReportedProcess.Deserialize(r)); IReadOnlyList <ReportedProcess> survivingChildProcesses = reader.ReadNullable(r => r.ReadReadOnlyList(r2 => allReportedProcesses[r2.ReadInt32()])); ProcessTimes primaryProcessTimes = reader.ReadNullable(r => ProcessTimes.Deserialize(r)); JobObject.AccountingInformation? jobAccountingInformation = reader.ReadNullableStruct(r => JobObject.AccountingInformation.Deserialize(r)); SandboxedProcessOutput standardOutput = reader.ReadNullable(r => SandboxedProcessOutput.Deserialize(r)); SandboxedProcessOutput standardError = reader.ReadNullable(r => SandboxedProcessOutput.Deserialize(r)); IReadOnlyList <ReportedFileAccess> fileAccesses = reader.ReadNullable(r => r.ReadReadOnlyList(r2 => ReportedFileAccess.Deserialize(r2, allReportedProcesses, readPath: null))); IReadOnlyList <ReportedFileAccess> explicitlyReportedFileAccesses = reader.ReadNullable(r => r.ReadReadOnlyList(r2 => ReportedFileAccess.Deserialize(r2, allReportedProcesses, readPath: null))); IReadOnlyList <ReportedFileAccess> allUnexpectedFileAccesses = reader.ReadNullable(r => r.ReadReadOnlyList(r2 => ReportedFileAccess.Deserialize(r2, allReportedProcesses, readPath: null))); IReadOnlyList <ReportedProcess> processes = reader.ReadNullable(r => r.ReadReadOnlyList(r2 => allReportedProcesses[r2.ReadInt32()])); IReadOnlyList <ProcessDetouringStatusData> detouringStatuses = reader.ReadNullable(r => r.ReadReadOnlyList(r2 => ProcessDetouringStatusData.Deserialize(r2))); string dumpFileDirectory = reader.ReadNullableString(); string dumpCreationExceptionMessage = reader.ReadNullableString(); string standardInputExceptionMessage = reader.ReadNullableString(); int numberOfPRocessLaunchRetries = reader.ReadInt32(); bool hasReadWriteToReadFileAccessRequest = reader.ReadBoolean(); string messageProcessingFailureMessage = reader.ReadNullableString(); long processStartTime = reader.ReadInt64(); int warningCount = reader.ReadInt32(); long detoursMaxHeapSize = reader.ReadInt64(); int lastMessageCount = reader.ReadInt32(); bool messageCountSemaphoreCreated = reader.ReadBoolean(); return(new SandboxedProcessResult() { ExitCode = exitCode, Killed = killed, TimedOut = timedOut, HasDetoursInjectionFailures = hasDetoursInjectionFailures, SurvivingChildProcesses = survivingChildProcesses, PrimaryProcessTimes = primaryProcessTimes, JobAccountingInformation = jobAccountingInformation, StandardOutput = standardOutput, StandardError = standardError, FileAccesses = fileAccesses != null ? new HashSet <ReportedFileAccess>(fileAccesses) : null, ExplicitlyReportedFileAccesses = explicitlyReportedFileAccesses != null ? new HashSet <ReportedFileAccess>(explicitlyReportedFileAccesses) : null, AllUnexpectedFileAccesses = allUnexpectedFileAccesses != null ? new HashSet <ReportedFileAccess>(allUnexpectedFileAccesses) : null, Processes = processes, DetouringStatuses = detouringStatuses, DumpFileDirectory = dumpFileDirectory, DumpCreationException = dumpCreationExceptionMessage != null ? new Exception(dumpCreationExceptionMessage) : null, StandardInputException = standardInputExceptionMessage != null ? new Exception(standardInputExceptionMessage) : null, NumberOfProcessLaunchRetries = numberOfPRocessLaunchRetries, HasReadWriteToReadFileAccessRequest = hasReadWriteToReadFileAccessRequest, MessageProcessingFailure = messageProcessingFailureMessage != null ? new Failure <string>(messageProcessingFailureMessage) : null, ProcessStartTime = processStartTime, WarningCount = warningCount, DetoursMaxHeapSize = detoursMaxHeapSize, LastMessageCount = lastMessageCount, MessageCountSemaphoreCreated = messageCountSemaphoreCreated }); }
internal static SandboxedProcessPipExecutionResult RetryProcessDueToAzureWatsonExitCode( int numberOfProcessLaunchRetries, int exitCode, ProcessTimes primaryProcessTimes, JobObject.AccountingInformation?jobAccountingInformation, IReadOnlyList <ProcessDetouringStatusData> detouringStatuses, long sandboxPrepMs, long processSandboxedProcessResultMs, long processStartTime, long maxDetoursHeapSize, ContainerConfiguration containerConfiguration, Dictionary <string, int> pipProperties) { return(new SandboxedProcessPipExecutionResult( SandboxedProcessPipExecutionStatus.ShouldBeRetriedDueToAzureWatsonExitCode, observedFileAccesses: default(SortedReadOnlyArray <ObservedFileAccess, ObservedFileAccessExpandedPathComparer>), sharedDynamicDirectoryWriteAccesses: default(Dictionary <AbsolutePath, IReadOnlyCollection <AbsolutePath> >), encodedStandardError: null, encodedStandardOutput: null, numberOfWarnings: 0, unexpectedFileAccesses: null, primaryProcessTimes: primaryProcessTimes, jobAccountingInformation: jobAccountingInformation, numberOfProcessLaunchRetries: numberOfProcessLaunchRetries, exitCode: exitCode, sandboxPrepMs: sandboxPrepMs, processSandboxedProcessResultMs: processSandboxedProcessResultMs, processStartTime: processStartTime, allReportedFileAccesses: null, detouringStatuses: detouringStatuses, maxDetoursHeapSize: maxDetoursHeapSize, containerConfiguration: containerConfiguration, pipProperties: pipProperties)); }
internal static SandboxedProcessPipExecutionResult FailureButRetryAble( SandboxedProcessPipExecutionStatus status, RetryInfo retryInfo, long maxDetoursHeapSize = 0, ProcessTimes primaryProcessTimes = null) { return(new SandboxedProcessPipExecutionResult( status, observedFileAccesses: default(SortedReadOnlyArray <ObservedFileAccess, ObservedFileAccessExpandedPathComparer>), sharedDynamicDirectoryWriteAccesses: null, encodedStandardError: null, encodedStandardOutput: null, numberOfWarnings: 0, unexpectedFileAccesses: null, primaryProcessTimes: primaryProcessTimes, jobAccountingInformation: null, exitCode: 0, sandboxPrepMs: 0, processSandboxedProcessResultMs: 0, processStartTime: 0L, allReportedFileAccesses: null, detouringStatuses: null, maxDetoursHeapSize: maxDetoursHeapSize, containerConfiguration: ContainerConfiguration.DisabledIsolation, pipProperties: null, timedOut: false, retryInfo: retryInfo, createdDirectories: null)); }
/// <nodoc /> public SandboxedProcessPipExecutionResult( SandboxedProcessPipExecutionStatus status, SortedReadOnlyArray <ObservedFileAccess, ObservedFileAccessExpandedPathComparer> observedFileAccesses, IReadOnlyDictionary <AbsolutePath, IReadOnlyCollection <FileArtifactWithAttributes> > sharedDynamicDirectoryWriteAccesses, Tuple <AbsolutePath, Encoding> encodedStandardOutput, Tuple <AbsolutePath, Encoding> encodedStandardError, int numberOfWarnings, FileAccessReportingContext unexpectedFileAccesses, ProcessTimes primaryProcessTimes, JobObject.AccountingInformation?jobAccountingInformation, int exitCode, long sandboxPrepMs, long processSandboxedProcessResultMs, long processStartTime, IReadOnlyList <ReportedFileAccess> allReportedFileAccesses, IReadOnlyList <ProcessDetouringStatusData> detouringStatuses, long maxDetoursHeapSize, ContainerConfiguration containerConfiguration, Dictionary <string, int> pipProperties, bool timedOut, IReadOnlySet <AbsolutePath> createdDirectories, RetryInfo retryInfo = null) { Contract.Requires(!ProcessCompletedExecution(status) || observedFileAccesses.IsValid); Contract.Requires(!ProcessCompletedExecution(status) || unexpectedFileAccesses != null); Contract.Requires(!ProcessCompletedExecution(status) || primaryProcessTimes != null); Contract.Requires(encodedStandardOutput == null || (encodedStandardOutput.Item1.IsValid && encodedStandardOutput.Item2 != null)); Contract.Requires(encodedStandardError == null || (encodedStandardError.Item1.IsValid && encodedStandardError.Item2 != null)); Contract.Requires(numberOfWarnings >= 0); Contract.Requires(containerConfiguration != null); Contract.Requires(retryInfo == null || status != SandboxedProcessPipExecutionStatus.Succeeded); // Protect against invalid combinations of RetryLocation and RetryReason Contract.Requires(!retryInfo.CanBeRetriedInlineOrFalseIfNull() || retryInfo.RetryReason != RetryReason.ResourceExhaustion); Contract.Requires(!retryInfo.CanBeRetriedInlineOrFalseIfNull() || retryInfo.RetryReason != RetryReason.ProcessStartFailure); Contract.Requires(!retryInfo.CanBeRetriedInlineOrFalseIfNull() || retryInfo.RetryReason != RetryReason.TempDirectoryCleanupFailure); Contract.Requires(!retryInfo.CanBeRetriedInlineOrFalseIfNull() || retryInfo.RetryReason != RetryReason.StoppedWorker); Status = status; ObservedFileAccesses = observedFileAccesses; UnexpectedFileAccesses = unexpectedFileAccesses; EncodedStandardOutput = encodedStandardOutput; EncodedStandardError = encodedStandardError; NumberOfWarnings = numberOfWarnings; PrimaryProcessTimes = primaryProcessTimes; JobAccountingInformation = jobAccountingInformation; ExitCode = exitCode; SandboxPrepMs = sandboxPrepMs; ProcessSandboxedProcessResultMs = processSandboxedProcessResultMs; ProcessStartTimeMs = processStartTime; AllReportedFileAccesses = allReportedFileAccesses; DetouringStatuses = detouringStatuses; MaxDetoursHeapSizeInBytes = maxDetoursHeapSize; SharedDynamicDirectoryWriteAccesses = sharedDynamicDirectoryWriteAccesses; ContainerConfiguration = containerConfiguration; PipProperties = pipProperties; TimedOut = timedOut; RetryInfo = retryInfo; CreatedDirectories = createdDirectories ?? CollectionUtilities.EmptySet <AbsolutePath>(); }
/// <nodoc /> public SandboxedProcessPipExecutionResult( SandboxedProcessPipExecutionStatus status, SortedReadOnlyArray<ObservedFileAccess, ObservedFileAccessExpandedPathComparer> observedFileAccesses, IReadOnlyDictionary<AbsolutePath, IReadOnlyCollection<FileArtifactWithAttributes>> sharedDynamicDirectoryWriteAccesses, Tuple<AbsolutePath, Encoding> encodedStandardOutput, Tuple<AbsolutePath, Encoding> encodedStandardError, int numberOfWarnings, FileAccessReportingContext unexpectedFileAccesses, ProcessTimes primaryProcessTimes, JobObject.AccountingInformation? jobAccountingInformation, int numberOfProcessLaunchRetries, int exitCode, long sandboxPrepMs, long processSandboxedProcessResultMs, long processStartTime, IReadOnlyList<ReportedFileAccess> allReportedFileAccesses, IReadOnlyList<ProcessDetouringStatusData> detouringStatuses, long maxDetoursHeapSize, ContainerConfiguration containerConfiguration, Dictionary<string, int> pipProperties, bool timedOut, CancellationReason cancellationReason = CancellationReason.None) { Contract.Requires( (status == SandboxedProcessPipExecutionStatus.PreparationFailed || status == SandboxedProcessPipExecutionStatus.ShouldBeRetriedDueToUserSpecifiedExitCode || status == SandboxedProcessPipExecutionStatus.Canceled) || observedFileAccesses.IsValid); Contract.Requires( (status == SandboxedProcessPipExecutionStatus.PreparationFailed || status == SandboxedProcessPipExecutionStatus.ShouldBeRetriedDueToUserSpecifiedExitCode || status == SandboxedProcessPipExecutionStatus.Canceled) || unexpectedFileAccesses != null); Contract.Requires((status == SandboxedProcessPipExecutionStatus.PreparationFailed || status == SandboxedProcessPipExecutionStatus.Canceled) || primaryProcessTimes != null); Contract.Requires(encodedStandardOutput == null || (encodedStandardOutput.Item1.IsValid && encodedStandardOutput.Item2 != null)); Contract.Requires(encodedStandardError == null || (encodedStandardError.Item1.IsValid && encodedStandardError.Item2 != null)); Contract.Requires(numberOfWarnings >= 0); Contract.Requires(containerConfiguration != null); Status = status; ObservedFileAccesses = observedFileAccesses; UnexpectedFileAccesses = unexpectedFileAccesses; EncodedStandardOutput = encodedStandardOutput; EncodedStandardError = encodedStandardError; NumberOfWarnings = numberOfWarnings; PrimaryProcessTimes = primaryProcessTimes; JobAccountingInformation = jobAccountingInformation; NumberOfProcessLaunchRetries = numberOfProcessLaunchRetries; ExitCode = exitCode; SandboxPrepMs = sandboxPrepMs; ProcessSandboxedProcessResultMs = processSandboxedProcessResultMs; ProcessStartTimeMs = processStartTime; AllReportedFileAccesses = allReportedFileAccesses; DetouringStatuses = detouringStatuses; MaxDetoursHeapSizeInBytes = maxDetoursHeapSize; SharedDynamicDirectoryWriteAccesses = sharedDynamicDirectoryWriteAccesses; ContainerConfiguration = containerConfiguration; PipProperties = pipProperties; TimedOut = timedOut; CancellationReason = cancellationReason; }
internal static SandboxedProcessPipExecutionResult RetryProcessDueToUserSpecifiedExitCode( int exitCode, ProcessTimes primaryProcessTimes, JobObject.AccountingInformation?jobAccountingInformation, IReadOnlyList <ProcessDetouringStatusData> detouringStatuses, long sandboxPrepMs, long processSandboxedProcessResultMs, long processStartTime, long maxDetoursHeapSize, ContainerConfiguration containerConfiguration, Tuple <AbsolutePath, Encoding> encodedStandardError, Tuple <AbsolutePath, Encoding> encodedStandardOutput, Dictionary <string, int> pipProperties, IReadOnlyDictionary <AbsolutePath, IReadOnlyCollection <FileArtifactWithAttributes> > sharedDynamicDirectoryWriteAccesses, RetryInfo retryInfo = null) { return(new SandboxedProcessPipExecutionResult( SandboxedProcessPipExecutionStatus.ExecutionFailed, observedFileAccesses: default(SortedReadOnlyArray <ObservedFileAccess, ObservedFileAccessExpandedPathComparer>), sharedDynamicDirectoryWriteAccesses: sharedDynamicDirectoryWriteAccesses, encodedStandardError: encodedStandardError, encodedStandardOutput: encodedStandardOutput, numberOfWarnings: 0, unexpectedFileAccesses: null, primaryProcessTimes: primaryProcessTimes, jobAccountingInformation: jobAccountingInformation, exitCode: exitCode, sandboxPrepMs: sandboxPrepMs, processSandboxedProcessResultMs: processSandboxedProcessResultMs, processStartTime: processStartTime, allReportedFileAccesses: null, detouringStatuses: detouringStatuses, maxDetoursHeapSize: maxDetoursHeapSize, containerConfiguration: containerConfiguration, pipProperties: pipProperties, timedOut: false, retryInfo: retryInfo ?? RetryInfo.GetDefault(RetryReason.UserSpecifiedExitCode), createdDirectories: null)); }
private async Task OnProcessExited() { // Wait until all incoming report messages from the detoured process have been handled. await WaitUntilReportEof(m_detouredProcess.Killed); // Ensure no further modifications to the report m_reports?.Freeze(); // We can get extended accounting information (peak memory, etc. rolled up for the entire process tree) if this process was wrapped in a job. JobObject.AccountingInformation?jobAccountingInformation = null; JobObject jobObject = m_detouredProcess.GetJobObject(); if (jobObject != null) { jobAccountingInformation = jobObject.GetAccountingInformation(); } ProcessTimes primaryProcessTimes = m_detouredProcess.GetTimesForPrimaryProcess(); IOException standardInputException = null; try { await m_standardInputTcs.Task; } catch (IOException ex) { standardInputException = ex; } // Construct result; note that the process is expected to have exited at this point, even if we decided to forcefully kill it // (this callback is always a result of the process handle being signaled). int exitCode = 0; if (m_reports?.MessageProcessingFailure != null) { exitCode = ExitCodes.MessageProcessingFailure; } else { Contract.Assert(m_detouredProcess.HasExited, "Detoured process has not been marked as exited"); exitCode = m_detouredProcess.GetExitCode(); } SandboxedProcessResult result = new SandboxedProcessResult { // If there is a message parsing failure, fail the pip. ExitCode = exitCode, Killed = m_detouredProcess.Killed, TimedOut = m_detouredProcess.TimedOut, HasDetoursInjectionFailures = m_detouredProcess.HasDetoursInjectionFailures, SurvivingChildProcesses = m_survivingChildProcesses?.Values.ToArray(), PrimaryProcessTimes = primaryProcessTimes, JobAccountingInformation = jobAccountingInformation, StandardOutput = m_output.Freeze(), StandardError = m_error.Freeze(), AllUnexpectedFileAccesses = m_reports?.FileUnexpectedAccesses, FileAccesses = m_reports?.FileAccesses, DetouringStatuses = m_reports?.ProcessDetoursStatuses, ExplicitlyReportedFileAccesses = m_reports?.ExplicitlyReportedFileAccesses, Processes = m_reports?.Processes, DumpFileDirectory = m_detouredProcess.DumpFileDirectory, DumpCreationException = m_detouredProcess.DumpCreationException, StandardInputException = standardInputException, MessageProcessingFailure = m_reports?.MessageProcessingFailure, ProcessStartTime = m_detouredProcess.StartTime, HasReadWriteToReadFileAccessRequest = m_reports?.HasReadWriteToReadFileAccessRequest ?? false, }; SetResult(result); }
private async Task OnProcessExitedAsync() { // Wait until all incoming report messages from the detoured process have been handled. await WaitUntilReportEof(m_detouredProcess.Killed); // Ensure no further modifications to the report m_reports?.Freeze(); // We can get extended accounting information (peak memory, etc. rolled up for the entire process tree) if this process was wrapped in a job. JobObject.AccountingInformation?jobAccountingInformation = null; JobObject jobObject = m_detouredProcess.GetJobObject(); if (jobObject != null) { var accountingInfo = jobObject.GetAccountingInformation(); // Only overwrite memory counters if <see cref="GetMemoryCountersSnapshot"/> did get triggered previously. This isn't the case if the // detours sandbox is used outside of BuildXL (currently only the scheduler calls this). The <see cref="JobObject.GetAccountingInformation"/> // function does populate memory counters for the process tree if possible, so don't overwrite them with empty aggregator values. if (m_peakWorkingSet.Count > 0 || m_workingSet.Count > 0 || m_peakCommitSize.Count > 0 || m_commitSize.Count > 0) { accountingInfo.MemoryCounters = Pips.ProcessMemoryCounters.CreateFromBytes( peakWorkingSet: Convert.ToUInt64(m_peakWorkingSet.Maximum), averageWorkingSet: Convert.ToUInt64(m_workingSet.Average), peakCommitSize: Convert.ToUInt64(m_peakCommitSize.Maximum), averageCommitSize: Convert.ToUInt64(m_commitSize.Average)); } jobAccountingInformation = accountingInfo; } ProcessTimes primaryProcessTimes = m_detouredProcess.GetTimesForPrimaryProcess(); IOException standardInputException = null; try { await m_standardInputTcs.Task; } catch (IOException ex) { standardInputException = ex; } // Construct result; note that the process is expected to have exited at this point, even if we decided to forcefully kill it // (this callback is always a result of the process handle being signaled). int exitCode = 0; if (m_reports?.MessageProcessingFailure != null) { exitCode = ExitCodes.MessageProcessingFailure; } else { Contract.Assert(m_detouredProcess.HasExited, "Detoured process has not been marked as exited"); exitCode = m_detouredProcess.GetExitCode(); } SandboxedProcessResult result = new SandboxedProcessResult { // If there is a message parsing failure, fail the pip. ExitCode = exitCode, Killed = m_detouredProcess.Killed, TimedOut = m_detouredProcess.TimedOut, HasDetoursInjectionFailures = m_detouredProcess.HasDetoursInjectionFailures, SurvivingChildProcesses = m_survivingChildProcesses?.Values.ToArray(), PrimaryProcessTimes = primaryProcessTimes, JobAccountingInformation = jobAccountingInformation, StandardOutput = m_output.Freeze(), StandardError = m_error.Freeze(), AllUnexpectedFileAccesses = m_reports?.FileUnexpectedAccesses, FileAccesses = m_reports?.FileAccesses, DetouringStatuses = m_reports?.ProcessDetoursStatuses, ExplicitlyReportedFileAccesses = m_reports?.ExplicitlyReportedFileAccesses, Processes = m_reports?.Processes, DumpFileDirectory = m_detouredProcess.DumpFileDirectory, DumpCreationException = m_detouredProcess.DumpCreationException, StandardInputException = standardInputException, MessageProcessingFailure = m_reports?.MessageProcessingFailure, ProcessStartTime = m_detouredProcess.StartTime, HasReadWriteToReadFileAccessRequest = m_reports?.HasReadWriteToReadFileAccessRequest ?? false, DiagnosticMessage = m_detouredProcess.Diagnostics }; SetResult(result); }