Stopping at program at exit

Today I wanted a way to run a program to completion but stop it just before it is destroyed, therefore allowing me to inspect some /proc variables I was interested in.

As it happens the ptrace option PTRACE_O_TRACEEXIT is designed for just that. To use it, you first either attach to the process, or if forking a new process call PTRACE_TRACEME and exec as usual.

Then you use the ptrace call PTRACE_SETOPTIONS with the PTRACE_O_TRACEEXIT option in the data pointer, and wait for the traced process as usual (unfortunately due to a bug you will have to clag the #defines from the kernel header for now). There are now two conditions to handle:

  1. The child received a SIGTRAP with the PTRACE_EVENT_EXIT status set, signalling to us that it has exited. This is a terminal state; you can inspect the registers, etc, but the process can not come back to life now. This status is "overloaded" in the status returned by wait, e.g.

    /* wait for signal from child */
    waitpid(pid, &status, 0);
    
    /* any signal stops the child, so check that the incoming signal is a
    SIGTRAP and the event_exit flag is set */
    
    if ((WSTOPSIG(status) == SIGTRAP) &&
        (status & (PTRACE_EVENT_EXIT << 8)))
         ...
    
  2. Any other signal will have also caused the tracing parent to be woken, so we need to pass that signal through to the child process with a PTRACE_CONT call specifing the signal in the data pointer; e.g.

    /* if not, pass the original signal onto the child */
    if (ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status))) {
           perror("stopper: ptrace(PTRACE_CONT, ...)");
               exit(1);
    }
    

A straight forward example is stopper.c. The program allows you to run a program (or attach to a running program) and effecitvley pause it just before it exits, allowing you to inspect it and then manually dispose of it once you have the data you need.