Repository: microsoft/onefuzz
File: src/agent/coverage/src/record/linux/debugger.rs (Rust)

Code before:

impl<'eh> Debugger<'eh> {
    pub fn new(event_handler: &'eh mut dyn DebugEventHandler) -> Self {
        let context = DebuggerContext::new();

        Self {
            context,
            event_handler,
        }
    }

    pub fn run(mut self, cmd: Command) -> Result<Output> {
        let mut child = self.context.tracer.spawn(cmd)?;

        if let Err(err) = self.wait_on_stops() {
            // Ignore error if child already exited.
            let _ = child.kill();

            return Err(err);
        }

        let output = child.wait_with_output()?;

        Ok(output)
    }

    fn wait_on_stops(mut self) -> Result<()> {
        use pete::ptracer::Options;

        // Continue the tracee process until the return from its initial `execve()`.
        let mut tracee = continue_to_init_execve(&mut self.context.tracer)?;

        // Do not follow forks.
        //
        // After this, we assume that any new tracee is a thread in the same
        // group as the root tracee.
        let mut options = Options::all();
        options.remove(Options::PTRACE_O_TRACEFORK);
        options.remove(Options::PTRACE_O_TRACEVFORK);
        options.remove(Options::PTRACE_O_TRACEEXEC);
        tracee.set_options(options)?;

        // Initialize index of mapped modules now that we have a PID to query.
        self.context.images = Some(Images::new(tracee.pid.as_raw()));
        self.update_images(&mut tracee)?;

        // Restart tracee and enter the main debugger loop.
        self.context.tracer.restart(tracee, Restart::Syscall)?;

        while let Some(mut tracee) = self.context.tracer.wait()? {
            match tracee.stop {

Review comment (suggestion):
If we had the `wait(2)` status integer that `pete` internally parses, then we could construct a standard library `ExitStatus` using [`ExitStatusExt::from_raw()`](https://doc.rust-lang.org/std/process/struct.ExitStatus.html#method.from_raw).

This raw `wait(2)` status isn't currently exposed in the `Stop` enum. Rather than try to be clever and resynthesize one from the exited/signaled/core dumped info we _do_ get, just omit it for now.

Code after:
    context: DebuggerContext,
    event_handler: &'eh mut dyn DebugEventHandler,
}

impl<'eh> Debugger<'eh> {
    pub fn new(event_handler: &'eh mut dyn DebugEventHandler) -> Self {
        let context = DebuggerContext::new();

        Self {
            context,
            event_handler,
        }
    }

    pub fn run(mut self, cmd: Command) -> Result<Output> {
        let mut child = self.context.tracer.spawn(cmd)?;

        if let Err(err) = self.wait_on_stops() {
            // Ignore error if child already exited.
            let _ = child.kill();

            return Err(err);
        }

        // Currently unavailable on Linux.
        let status = None;

        let stdout = if let Some(mut pipe) = child.stdout {
            let mut stdout = Vec::new();
            pipe.read_to_end(&mut stdout)?;
            String::from_utf8_lossy(&stdout).into_owned()
        } else {
            "".into()
        };

        let stderr = if let Some(mut pipe) = child.stderr {
            let mut stderr = Vec::new();
            pipe.read_to_end(&mut stderr)?;
            String::from_utf8_lossy(&stderr).into_owned()
        } else {
            "".into()
        };

        let output = Output {
            status,
            stderr,
            stdout,
        };

        Ok(output)
    }