1*521fa314SDavid van Moolenbroek /* trace(1) - the MINIX3 system call tracer - by D.C. van Moolenbroek */ 2*521fa314SDavid van Moolenbroek 3*521fa314SDavid van Moolenbroek #include "inc.h" 4*521fa314SDavid van Moolenbroek 5*521fa314SDavid van Moolenbroek #include <signal.h> 6*521fa314SDavid van Moolenbroek #include <sys/wait.h> 7*521fa314SDavid van Moolenbroek #include <unistd.h> 8*521fa314SDavid van Moolenbroek #include <err.h> 9*521fa314SDavid van Moolenbroek 10*521fa314SDavid van Moolenbroek /* Global variables, used only for a subset of the command line options. */ 11*521fa314SDavid van Moolenbroek int allnames; /* FALSE = structure field names, TRUE = all names */ 12*521fa314SDavid van Moolenbroek unsigned int valuesonly; /* 0 = normal, 1 = no symbols, 2 = no structures */ 13*521fa314SDavid van Moolenbroek unsigned int verbose; /* 0 = essentials, 1 = elaborate, 2 = everything */ 14*521fa314SDavid van Moolenbroek 15*521fa314SDavid van Moolenbroek /* Local variables, for signal handling. */ 16*521fa314SDavid van Moolenbroek static int got_signal, got_info; 17*521fa314SDavid van Moolenbroek 18*521fa314SDavid van Moolenbroek /* 19*521fa314SDavid van Moolenbroek * Signal handler for signals that are supposed to make us terminate. Let the 20*521fa314SDavid van Moolenbroek * main loop do the actual work, since it might be in the middle of processing 21*521fa314SDavid van Moolenbroek * a process status change right now. 22*521fa314SDavid van Moolenbroek */ 23*521fa314SDavid van Moolenbroek static void 24*521fa314SDavid van Moolenbroek sig_handler(int __unused sig) 25*521fa314SDavid van Moolenbroek { 26*521fa314SDavid van Moolenbroek 27*521fa314SDavid van Moolenbroek got_signal = TRUE; 28*521fa314SDavid van Moolenbroek 29*521fa314SDavid van Moolenbroek } 30*521fa314SDavid van Moolenbroek 31*521fa314SDavid van Moolenbroek /* 32*521fa314SDavid van Moolenbroek * Signal handler for the SIGINFO signal. Let the main loop report on all 33*521fa314SDavid van Moolenbroek * processes currenty being traced. Since SIGINFO is sent to the current 34*521fa314SDavid van Moolenbroek * process group, traced children may get the signal as well. This is both 35*521fa314SDavid van Moolenbroek * intentional and impossible to prevent. 36*521fa314SDavid van Moolenbroek */ 37*521fa314SDavid van Moolenbroek static void 38*521fa314SDavid van Moolenbroek info_handler(int __unused sig) 39*521fa314SDavid van Moolenbroek { 40*521fa314SDavid van Moolenbroek 41*521fa314SDavid van Moolenbroek got_info = TRUE; 42*521fa314SDavid van Moolenbroek } 43*521fa314SDavid van Moolenbroek 44*521fa314SDavid van Moolenbroek /* 45*521fa314SDavid van Moolenbroek * Print a list of traced processes and their call status. We must not 46*521fa314SDavid van Moolenbroek * interfere with actual process output, so perform out-of-band printing 47*521fa314SDavid van Moolenbroek * (with info lines rather than lines prefixed by each process's PID). 48*521fa314SDavid van Moolenbroek */ 49*521fa314SDavid van Moolenbroek static void 50*521fa314SDavid van Moolenbroek list_info(void) 51*521fa314SDavid van Moolenbroek { 52*521fa314SDavid van Moolenbroek struct trace_proc *proc; 53*521fa314SDavid van Moolenbroek int no_call, in_call; 54*521fa314SDavid van Moolenbroek 55*521fa314SDavid van Moolenbroek put_newline(); 56*521fa314SDavid van Moolenbroek 57*521fa314SDavid van Moolenbroek for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc)) { 58*521fa314SDavid van Moolenbroek /* 59*521fa314SDavid van Moolenbroek * When attaching to an existing process, there is no way to 60*521fa314SDavid van Moolenbroek * find out whether the process is in a system call or not. 61*521fa314SDavid van Moolenbroek */ 62*521fa314SDavid van Moolenbroek no_call = (proc->trace_flags & TF_NOCALL); 63*521fa314SDavid van Moolenbroek in_call = (proc->trace_flags & TF_INCALL); 64*521fa314SDavid van Moolenbroek assert(!in_call || !no_call); 65*521fa314SDavid van Moolenbroek 66*521fa314SDavid van Moolenbroek put_fmt(NULL, "Tracing %s (pid %d), %s%s%s", proc->name, 67*521fa314SDavid van Moolenbroek proc->pid, no_call ? "call status unknown" : 68*521fa314SDavid van Moolenbroek (in_call ? "in a " : "not in a call"), 69*521fa314SDavid van Moolenbroek in_call ? call_name(proc) : "", 70*521fa314SDavid van Moolenbroek in_call ? " call" : ""); 71*521fa314SDavid van Moolenbroek put_newline(); 72*521fa314SDavid van Moolenbroek } 73*521fa314SDavid van Moolenbroek } 74*521fa314SDavid van Moolenbroek 75*521fa314SDavid van Moolenbroek /* 76*521fa314SDavid van Moolenbroek * Either we have just started or attached to the given process, it the process 77*521fa314SDavid van Moolenbroek * has performed a successful execve() call. Obtain the new process name, and 78*521fa314SDavid van Moolenbroek * print a banner for it. 79*521fa314SDavid van Moolenbroek */ 80*521fa314SDavid van Moolenbroek static void 81*521fa314SDavid van Moolenbroek new_exec(struct trace_proc * proc) 82*521fa314SDavid van Moolenbroek { 83*521fa314SDavid van Moolenbroek 84*521fa314SDavid van Moolenbroek /* Failure to obtain the process name is worrisome, but not fatal.. */ 85*521fa314SDavid van Moolenbroek if (kernel_get_name(proc->pid, proc->name, sizeof(proc->name)) < 0) 86*521fa314SDavid van Moolenbroek strlcpy(proc->name, "<unknown>", sizeof(proc->name)); 87*521fa314SDavid van Moolenbroek 88*521fa314SDavid van Moolenbroek put_newline(); 89*521fa314SDavid van Moolenbroek put_fmt(proc, "Tracing %s (pid %d)", proc->name, proc->pid); 90*521fa314SDavid van Moolenbroek put_newline(); 91*521fa314SDavid van Moolenbroek } 92*521fa314SDavid van Moolenbroek 93*521fa314SDavid van Moolenbroek /* 94*521fa314SDavid van Moolenbroek * We have started or attached to a process. Set the appropriate flags, and 95*521fa314SDavid van Moolenbroek * print a banner showing that we are now tracing it. 96*521fa314SDavid van Moolenbroek */ 97*521fa314SDavid van Moolenbroek static void 98*521fa314SDavid van Moolenbroek new_proc(struct trace_proc * proc, int follow_fork) 99*521fa314SDavid van Moolenbroek { 100*521fa314SDavid van Moolenbroek int fl; 101*521fa314SDavid van Moolenbroek 102*521fa314SDavid van Moolenbroek /* Set the desired tracing options. */ 103*521fa314SDavid van Moolenbroek fl = TO_ALTEXEC; 104*521fa314SDavid van Moolenbroek if (follow_fork) fl |= TO_TRACEFORK; 105*521fa314SDavid van Moolenbroek 106*521fa314SDavid van Moolenbroek (void)ptrace(T_SETOPT, proc->pid, 0, fl); 107*521fa314SDavid van Moolenbroek 108*521fa314SDavid van Moolenbroek /* 109*521fa314SDavid van Moolenbroek * When attaching to an arbitrary process, this process might be in the 110*521fa314SDavid van Moolenbroek * middle of an execve(). Now that we have enabled TO_ALTEXEC, we may 111*521fa314SDavid van Moolenbroek * now get a SIGSTOP signal next. Guard against this by marking the 112*521fa314SDavid van Moolenbroek * first system call as a possible execve(). 113*521fa314SDavid van Moolenbroek */ 114*521fa314SDavid van Moolenbroek if ((proc->trace_flags & (TF_ATTACH | TF_STOPPING)) == TF_ATTACH) 115*521fa314SDavid van Moolenbroek proc->trace_flags |= TF_EXEC; 116*521fa314SDavid van Moolenbroek 117*521fa314SDavid van Moolenbroek new_exec(proc); 118*521fa314SDavid van Moolenbroek } 119*521fa314SDavid van Moolenbroek 120*521fa314SDavid van Moolenbroek /* 121*521fa314SDavid van Moolenbroek * A process has terminated or is being detached. Print the resulting status. 122*521fa314SDavid van Moolenbroek */ 123*521fa314SDavid van Moolenbroek static void 124*521fa314SDavid van Moolenbroek discard_proc(struct trace_proc * proc, int status) 125*521fa314SDavid van Moolenbroek { 126*521fa314SDavid van Moolenbroek const char *signame; 127*521fa314SDavid van Moolenbroek 128*521fa314SDavid van Moolenbroek /* 129*521fa314SDavid van Moolenbroek * The exit() calls are of type no-return, meaning they are expected 130*521fa314SDavid van Moolenbroek * not to return. However, calls of this type may in fact return an 131*521fa314SDavid van Moolenbroek * error, in which case the error must be printed. Thus, such calls 132*521fa314SDavid van Moolenbroek * are not actually finished until the end of the call-leave phase. 133*521fa314SDavid van Moolenbroek * For exit() calls, a successful call will never get to the call-leave 134*521fa314SDavid van Moolenbroek * phase. The result is that such calls will end up being shown as 135*521fa314SDavid van Moolenbroek * suspended, which is unintuitive. To counter this, we pretend that a 136*521fa314SDavid van Moolenbroek * clean process exit is in fact preceded by a call-leave event, thus 137*521fa314SDavid van Moolenbroek * allowing the call to be printed without suspension. An example: 138*521fa314SDavid van Moolenbroek * 139*521fa314SDavid van Moolenbroek * 3| exit(0) <..> 140*521fa314SDavid van Moolenbroek * 2| setsid() = 2 141*521fa314SDavid van Moolenbroek * [A] 3| exit(0) 142*521fa314SDavid van Moolenbroek * 3| Process exited normally with code 0 143*521fa314SDavid van Moolenbroek * 144*521fa314SDavid van Moolenbroek * The [A] line is the result of the following code. 145*521fa314SDavid van Moolenbroek */ 146*521fa314SDavid van Moolenbroek if (WIFEXITED(status) && (proc->trace_flags & TF_INCALL)) 147*521fa314SDavid van Moolenbroek call_leave(proc, TRUE /*skip*/); 148*521fa314SDavid van Moolenbroek 149*521fa314SDavid van Moolenbroek put_newline(); 150*521fa314SDavid van Moolenbroek if (WIFEXITED(status)) { 151*521fa314SDavid van Moolenbroek put_fmt(proc, "Process exited normally with code %d", 152*521fa314SDavid van Moolenbroek WEXITSTATUS(status)); 153*521fa314SDavid van Moolenbroek } else if (WIFSIGNALED(status)) { 154*521fa314SDavid van Moolenbroek if ((signame = get_signal_name(WTERMSIG(status))) != NULL) 155*521fa314SDavid van Moolenbroek put_fmt(proc, "Process terminated from signal %s", 156*521fa314SDavid van Moolenbroek signame); 157*521fa314SDavid van Moolenbroek else 158*521fa314SDavid van Moolenbroek put_fmt(proc, "Process terminated from signal %d", 159*521fa314SDavid van Moolenbroek WTERMSIG(status)); 160*521fa314SDavid van Moolenbroek } else if (WIFSTOPPED(status)) 161*521fa314SDavid van Moolenbroek put_text(proc, "Process detached"); 162*521fa314SDavid van Moolenbroek else 163*521fa314SDavid van Moolenbroek put_fmt(proc, "Bogus wait result (%04x)", status); 164*521fa314SDavid van Moolenbroek put_newline(); 165*521fa314SDavid van Moolenbroek 166*521fa314SDavid van Moolenbroek proc_del(proc); 167*521fa314SDavid van Moolenbroek } 168*521fa314SDavid van Moolenbroek 169*521fa314SDavid van Moolenbroek /* 170*521fa314SDavid van Moolenbroek * The given process has been stopped on a system call, either entering or 171*521fa314SDavid van Moolenbroek * leaving that call. 172*521fa314SDavid van Moolenbroek */ 173*521fa314SDavid van Moolenbroek static void 174*521fa314SDavid van Moolenbroek handle_call(struct trace_proc * proc, int show_stack) 175*521fa314SDavid van Moolenbroek { 176*521fa314SDavid van Moolenbroek reg_t pc, sp; 177*521fa314SDavid van Moolenbroek int class, skip, new_ctx; 178*521fa314SDavid van Moolenbroek 179*521fa314SDavid van Moolenbroek proc->trace_flags &= ~TF_NOCALL; 180*521fa314SDavid van Moolenbroek 181*521fa314SDavid van Moolenbroek if (proc->trace_flags & TF_SKIP) { 182*521fa314SDavid van Moolenbroek /* Skip the call leave phase after a successful execve(). */ 183*521fa314SDavid van Moolenbroek proc->trace_flags &= ~(TF_INCALL | TF_SKIP); 184*521fa314SDavid van Moolenbroek } else if (!(proc->trace_flags & TF_INCALL)) { 185*521fa314SDavid van Moolenbroek /* 186*521fa314SDavid van Moolenbroek * The call_enter call returns the class of the call: 187*521fa314SDavid van Moolenbroek * TC_NORMAL, TC_EXEC, or TC_SIGRET. TC_EXEC means that an 188*521fa314SDavid van Moolenbroek * execve() call is being performed. This means that if a 189*521fa314SDavid van Moolenbroek * SIGSTOP follows for the current process, the process has 190*521fa314SDavid van Moolenbroek * successfully started a different executable. TC_SIGRET 191*521fa314SDavid van Moolenbroek * means that if successful, the call will have a bogus return 192*521fa314SDavid van Moolenbroek * value. TC_NORMAL means that the call requires no exception. 193*521fa314SDavid van Moolenbroek */ 194*521fa314SDavid van Moolenbroek class = call_enter(proc, show_stack); 195*521fa314SDavid van Moolenbroek 196*521fa314SDavid van Moolenbroek switch (class) { 197*521fa314SDavid van Moolenbroek case TC_NORMAL: 198*521fa314SDavid van Moolenbroek break; 199*521fa314SDavid van Moolenbroek case TC_EXEC: 200*521fa314SDavid van Moolenbroek proc->trace_flags |= TF_EXEC; 201*521fa314SDavid van Moolenbroek break; 202*521fa314SDavid van Moolenbroek case TC_SIGRET: 203*521fa314SDavid van Moolenbroek proc->trace_flags |= TF_CTX_SKIP; 204*521fa314SDavid van Moolenbroek break; 205*521fa314SDavid van Moolenbroek default: 206*521fa314SDavid van Moolenbroek assert(0); 207*521fa314SDavid van Moolenbroek } 208*521fa314SDavid van Moolenbroek 209*521fa314SDavid van Moolenbroek /* Save the current program counter and stack pointer. */ 210*521fa314SDavid van Moolenbroek if (!kernel_get_context(proc->pid, &pc, &sp, NULL /*fp*/)) { 211*521fa314SDavid van Moolenbroek proc->last_pc = pc; 212*521fa314SDavid van Moolenbroek proc->last_sp = sp; 213*521fa314SDavid van Moolenbroek } else 214*521fa314SDavid van Moolenbroek proc->last_pc = proc->last_sp = 0; 215*521fa314SDavid van Moolenbroek 216*521fa314SDavid van Moolenbroek proc->trace_flags |= TF_INCALL; 217*521fa314SDavid van Moolenbroek } else { 218*521fa314SDavid van Moolenbroek /* 219*521fa314SDavid van Moolenbroek * Check if the program counter or stack pointer have changed 220*521fa314SDavid van Moolenbroek * during the system call. If so, this is a strong indication 221*521fa314SDavid van Moolenbroek * that a sigreturn call has succeeded, and thus its result 222*521fa314SDavid van Moolenbroek * must be skipped, since the result register will not contain 223*521fa314SDavid van Moolenbroek * the result of the call. 224*521fa314SDavid van Moolenbroek */ 225*521fa314SDavid van Moolenbroek new_ctx = (proc->last_pc != 0 && 226*521fa314SDavid van Moolenbroek !kernel_get_context(proc->pid, &pc, &sp, NULL /*fp*/) && 227*521fa314SDavid van Moolenbroek (pc != proc->last_pc || sp != proc->last_sp)); 228*521fa314SDavid van Moolenbroek 229*521fa314SDavid van Moolenbroek skip = ((proc->trace_flags & TF_CTX_SKIP) && new_ctx); 230*521fa314SDavid van Moolenbroek 231*521fa314SDavid van Moolenbroek call_leave(proc, skip); 232*521fa314SDavid van Moolenbroek 233*521fa314SDavid van Moolenbroek /* 234*521fa314SDavid van Moolenbroek * On such context changes, also print a short dashed line. 235*521fa314SDavid van Moolenbroek * This helps in identifying signal handler invocations, 236*521fa314SDavid van Moolenbroek * although it is not reliable for that purpose: no dashed line 237*521fa314SDavid van Moolenbroek * will be printed if a signal handler is invoked while the 238*521fa314SDavid van Moolenbroek * process is not making a system call. 239*521fa314SDavid van Moolenbroek */ 240*521fa314SDavid van Moolenbroek if (new_ctx) { 241*521fa314SDavid van Moolenbroek put_text(proc, "---"); 242*521fa314SDavid van Moolenbroek put_newline(); 243*521fa314SDavid van Moolenbroek } 244*521fa314SDavid van Moolenbroek 245*521fa314SDavid van Moolenbroek proc->trace_flags &= ~(TF_INCALL | TF_CTX_SKIP | TF_EXEC); 246*521fa314SDavid van Moolenbroek } 247*521fa314SDavid van Moolenbroek } 248*521fa314SDavid van Moolenbroek 249*521fa314SDavid van Moolenbroek /* 250*521fa314SDavid van Moolenbroek * The given process has received the given signal. Report the receipt. Due 251*521fa314SDavid van Moolenbroek * to the way that signal handling with traced processes works, the signal may 252*521fa314SDavid van Moolenbroek * in fact be delivered to the process much later, or never--a problem inherent 253*521fa314SDavid van Moolenbroek * to the way signals are handled in PM right now (namely, deferring signal 254*521fa314SDavid van Moolenbroek * delivery would let the traced process block signals meant for the tracer). 255*521fa314SDavid van Moolenbroek */ 256*521fa314SDavid van Moolenbroek static void 257*521fa314SDavid van Moolenbroek report_signal(struct trace_proc * proc, int sig, int show_stack) 258*521fa314SDavid van Moolenbroek { 259*521fa314SDavid van Moolenbroek const char *signame; 260*521fa314SDavid van Moolenbroek 261*521fa314SDavid van Moolenbroek /* 262*521fa314SDavid van Moolenbroek * Print a stack trace only if we are not in a call; otherwise, we 263*521fa314SDavid van Moolenbroek * would simply get the same stack trace twice and mess up the output 264*521fa314SDavid van Moolenbroek * in the process, because call suspension is not expected if we are 265*521fa314SDavid van Moolenbroek * tracing a single process only. 266*521fa314SDavid van Moolenbroek * FIXME: the check should be for whether we actually print the call.. 267*521fa314SDavid van Moolenbroek */ 268*521fa314SDavid van Moolenbroek if (show_stack && !(proc->trace_flags & TF_INCALL)) 269*521fa314SDavid van Moolenbroek kernel_put_stacktrace(proc); 270*521fa314SDavid van Moolenbroek 271*521fa314SDavid van Moolenbroek /* 272*521fa314SDavid van Moolenbroek * If this process is in the middle of a call, the signal will be 273*521fa314SDavid van Moolenbroek * printed within the call. This will always happen on the call split, 274*521fa314SDavid van Moolenbroek * that is, between the call's entering (out) and leaving (in) phases. 275*521fa314SDavid van Moolenbroek * This also means that the recording of the call-enter phase may be 276*521fa314SDavid van Moolenbroek * replayed more than once, and the call may be suspended more than 277*521fa314SDavid van Moolenbroek * once--after all, a signal is not necessarily followed immediately 278*521fa314SDavid van Moolenbroek * by the call result. If the process is not in the middle of a call, 279*521fa314SDavid van Moolenbroek * the signal will end up on a separate line. In both cases, multiple 280*521fa314SDavid van Moolenbroek * consecutive signals may be printed right after one another. The 281*521fa314SDavid van Moolenbroek * following scenario shows a number of possible combinations: 282*521fa314SDavid van Moolenbroek * 283*521fa314SDavid van Moolenbroek * 2| foo(<..> 284*521fa314SDavid van Moolenbroek * 3| ** SIGHUP ** ** SIGUSR1 ** 285*521fa314SDavid van Moolenbroek * 3| bar() = <..> 286*521fa314SDavid van Moolenbroek * 2|*foo(** SIGUSR1 ** ** SIGUSR2 ** <..> 287*521fa314SDavid van Moolenbroek * 3|*bar() = ** SIGCHLD ** 0 288*521fa314SDavid van Moolenbroek * 2|*foo(** SIGINT ** &0xef852000) = -1 [EINTR] 289*521fa314SDavid van Moolenbroek * 3| kill(3, SIGTERM) = ** SIGTERM ** <..> 290*521fa314SDavid van Moolenbroek * 3| Process terminated from signal SIGTERM 291*521fa314SDavid van Moolenbroek */ 292*521fa314SDavid van Moolenbroek 293*521fa314SDavid van Moolenbroek call_replay(proc); 294*521fa314SDavid van Moolenbroek 295*521fa314SDavid van Moolenbroek if (!valuesonly && (signame = get_signal_name(sig)) != NULL) 296*521fa314SDavid van Moolenbroek put_fmt(proc, "** %s **", signame); 297*521fa314SDavid van Moolenbroek else 298*521fa314SDavid van Moolenbroek put_fmt(proc, "** SIGNAL %d **", sig); 299*521fa314SDavid van Moolenbroek 300*521fa314SDavid van Moolenbroek put_space(proc); 301*521fa314SDavid van Moolenbroek 302*521fa314SDavid van Moolenbroek output_flush(); 303*521fa314SDavid van Moolenbroek } 304*521fa314SDavid van Moolenbroek 305*521fa314SDavid van Moolenbroek /* 306*521fa314SDavid van Moolenbroek * Wait for the given process ID to stop on the given signal. Upon success, 307*521fa314SDavid van Moolenbroek * the function will return zero. Upon failure, it will return -1, and errno 308*521fa314SDavid van Moolenbroek * will be either set to an error code, or to zero in order to indicate that 309*521fa314SDavid van Moolenbroek * the process exited instead. 310*521fa314SDavid van Moolenbroek */ 311*521fa314SDavid van Moolenbroek static int 312*521fa314SDavid van Moolenbroek wait_sig(pid_t pid, int sig) 313*521fa314SDavid van Moolenbroek { 314*521fa314SDavid van Moolenbroek int status; 315*521fa314SDavid van Moolenbroek 316*521fa314SDavid van Moolenbroek for (;;) { 317*521fa314SDavid van Moolenbroek if (waitpid(pid, &status, 0) == -1) { 318*521fa314SDavid van Moolenbroek if (errno == EINTR) continue; 319*521fa314SDavid van Moolenbroek 320*521fa314SDavid van Moolenbroek return -1; 321*521fa314SDavid van Moolenbroek } 322*521fa314SDavid van Moolenbroek 323*521fa314SDavid van Moolenbroek if (!WIFSTOPPED(status)) { 324*521fa314SDavid van Moolenbroek /* The process terminated just now. */ 325*521fa314SDavid van Moolenbroek errno = 0; 326*521fa314SDavid van Moolenbroek 327*521fa314SDavid van Moolenbroek return -1; 328*521fa314SDavid van Moolenbroek } 329*521fa314SDavid van Moolenbroek 330*521fa314SDavid van Moolenbroek if (WSTOPSIG(status) == sig) 331*521fa314SDavid van Moolenbroek break; 332*521fa314SDavid van Moolenbroek 333*521fa314SDavid van Moolenbroek (void)ptrace(T_RESUME, pid, 0, WSTOPSIG(status)); 334*521fa314SDavid van Moolenbroek } 335*521fa314SDavid van Moolenbroek 336*521fa314SDavid van Moolenbroek return 0; 337*521fa314SDavid van Moolenbroek } 338*521fa314SDavid van Moolenbroek 339*521fa314SDavid van Moolenbroek /* 340*521fa314SDavid van Moolenbroek * Attach to the given process, and wait for the resulting SIGSTOP signal. 341*521fa314SDavid van Moolenbroek * Other signals may arrive first; we pass these on to the process without 342*521fa314SDavid van Moolenbroek * reporting them, thus logically modelling them as having arrived before we 343*521fa314SDavid van Moolenbroek * attached to the process. The process might also exit in the meantime, 344*521fa314SDavid van Moolenbroek * typically as a result of a lethal signal; following the same logical model, 345*521fa314SDavid van Moolenbroek * we pretend the process did not exist in the first place. Since the SIGSTOP 346*521fa314SDavid van Moolenbroek * signal will be pending right after attaching to the process, this procedure 347*521fa314SDavid van Moolenbroek * will never block. 348*521fa314SDavid van Moolenbroek */ 349*521fa314SDavid van Moolenbroek static int 350*521fa314SDavid van Moolenbroek attach(pid_t pid) 351*521fa314SDavid van Moolenbroek { 352*521fa314SDavid van Moolenbroek 353*521fa314SDavid van Moolenbroek if (ptrace(T_ATTACH, pid, 0, 0) != 0) { 354*521fa314SDavid van Moolenbroek warn("Unable to attach to pid %d", pid); 355*521fa314SDavid van Moolenbroek 356*521fa314SDavid van Moolenbroek return -1; 357*521fa314SDavid van Moolenbroek } 358*521fa314SDavid van Moolenbroek 359*521fa314SDavid van Moolenbroek if (wait_sig(pid, SIGSTOP) != 0) { 360*521fa314SDavid van Moolenbroek /* If the process terminated, report it as not found. */ 361*521fa314SDavid van Moolenbroek if (errno == 0) 362*521fa314SDavid van Moolenbroek errno = ESRCH; 363*521fa314SDavid van Moolenbroek 364*521fa314SDavid van Moolenbroek warn("Unable to attach to pid %d", pid); 365*521fa314SDavid van Moolenbroek 366*521fa314SDavid van Moolenbroek return -1; 367*521fa314SDavid van Moolenbroek } 368*521fa314SDavid van Moolenbroek 369*521fa314SDavid van Moolenbroek /* Verify that we can read values from the kernel at all. */ 370*521fa314SDavid van Moolenbroek if (kernel_check(pid) == FALSE) { 371*521fa314SDavid van Moolenbroek (void)ptrace(T_DETACH, pid, 0, 0); 372*521fa314SDavid van Moolenbroek 373*521fa314SDavid van Moolenbroek warnx("Kernel magic check failed, recompile trace(1)"); 374*521fa314SDavid van Moolenbroek 375*521fa314SDavid van Moolenbroek return -1; 376*521fa314SDavid van Moolenbroek } 377*521fa314SDavid van Moolenbroek 378*521fa314SDavid van Moolenbroek /* 379*521fa314SDavid van Moolenbroek * System services are managed by RS, which prevents them from 380*521fa314SDavid van Moolenbroek * being traced properly by PM. Attaching to a service could 381*521fa314SDavid van Moolenbroek * therefore cause problems, so we should detach immediately. 382*521fa314SDavid van Moolenbroek */ 383*521fa314SDavid van Moolenbroek if (kernel_is_service(pid) == TRUE) { 384*521fa314SDavid van Moolenbroek (void)ptrace(T_DETACH, pid, 0, 0); 385*521fa314SDavid van Moolenbroek 386*521fa314SDavid van Moolenbroek warnx("Cannot attach to system services!"); 387*521fa314SDavid van Moolenbroek 388*521fa314SDavid van Moolenbroek return -1; 389*521fa314SDavid van Moolenbroek } 390*521fa314SDavid van Moolenbroek 391*521fa314SDavid van Moolenbroek return 0; 392*521fa314SDavid van Moolenbroek } 393*521fa314SDavid van Moolenbroek 394*521fa314SDavid van Moolenbroek /* 395*521fa314SDavid van Moolenbroek * Detach from all processes, knowning that they were all processes to which we 396*521fa314SDavid van Moolenbroek * attached explicitly (i.e., not started by us) and are all currently stopped. 397*521fa314SDavid van Moolenbroek */ 398*521fa314SDavid van Moolenbroek static void 399*521fa314SDavid van Moolenbroek detach_stopped(void) 400*521fa314SDavid van Moolenbroek { 401*521fa314SDavid van Moolenbroek struct trace_proc *proc; 402*521fa314SDavid van Moolenbroek 403*521fa314SDavid van Moolenbroek for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc)) 404*521fa314SDavid van Moolenbroek (void)ptrace(T_DETACH, proc->pid, 0, 0); 405*521fa314SDavid van Moolenbroek } 406*521fa314SDavid van Moolenbroek 407*521fa314SDavid van Moolenbroek /* 408*521fa314SDavid van Moolenbroek * Start detaching from all processes to which we previously attached. The 409*521fa314SDavid van Moolenbroek * function is expected to return before detaching is completed, and the caller 410*521fa314SDavid van Moolenbroek * must deal with the new situation appropriately. Do not touch any processes 411*521fa314SDavid van Moolenbroek * started by us (to allow graceful termination), unless force is set, in which 412*521fa314SDavid van Moolenbroek * case those processes are killed. 413*521fa314SDavid van Moolenbroek */ 414*521fa314SDavid van Moolenbroek static void 415*521fa314SDavid van Moolenbroek detach_running(int force) 416*521fa314SDavid van Moolenbroek { 417*521fa314SDavid van Moolenbroek struct trace_proc *proc; 418*521fa314SDavid van Moolenbroek 419*521fa314SDavid van Moolenbroek for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc)) { 420*521fa314SDavid van Moolenbroek if (proc->trace_flags & TF_ATTACH) { 421*521fa314SDavid van Moolenbroek /* Already detaching? Then do nothing. */ 422*521fa314SDavid van Moolenbroek if (proc->trace_flags & TF_DETACH) 423*521fa314SDavid van Moolenbroek continue; 424*521fa314SDavid van Moolenbroek 425*521fa314SDavid van Moolenbroek if (!(proc->trace_flags & TF_STOPPING)) 426*521fa314SDavid van Moolenbroek (void)kill(proc->pid, SIGSTOP); 427*521fa314SDavid van Moolenbroek 428*521fa314SDavid van Moolenbroek proc->trace_flags |= TF_DETACH | TF_STOPPING; 429*521fa314SDavid van Moolenbroek } else { 430*521fa314SDavid van Moolenbroek /* 431*521fa314SDavid van Moolenbroek * The child processes may be ignoring SIGINTs, so upon 432*521fa314SDavid van Moolenbroek * the second try, force them to terminate. 433*521fa314SDavid van Moolenbroek */ 434*521fa314SDavid van Moolenbroek if (force) 435*521fa314SDavid van Moolenbroek (void)kill(proc->pid, SIGKILL); 436*521fa314SDavid van Moolenbroek } 437*521fa314SDavid van Moolenbroek } 438*521fa314SDavid van Moolenbroek } 439*521fa314SDavid van Moolenbroek 440*521fa314SDavid van Moolenbroek /* 441*521fa314SDavid van Moolenbroek * Print command usage. 442*521fa314SDavid van Moolenbroek */ 443*521fa314SDavid van Moolenbroek static void __dead 444*521fa314SDavid van Moolenbroek usage(void) 445*521fa314SDavid van Moolenbroek { 446*521fa314SDavid van Moolenbroek 447*521fa314SDavid van Moolenbroek (void)fprintf(stderr, "usage: %s [-fgNsVv] [-o file] [-p pid] " 448*521fa314SDavid van Moolenbroek "[command]\n", getprogname()); 449*521fa314SDavid van Moolenbroek 450*521fa314SDavid van Moolenbroek exit(EXIT_FAILURE); 451*521fa314SDavid van Moolenbroek } 452*521fa314SDavid van Moolenbroek 453*521fa314SDavid van Moolenbroek /* 454*521fa314SDavid van Moolenbroek * The main function of the system call tracer. 455*521fa314SDavid van Moolenbroek */ 456*521fa314SDavid van Moolenbroek int 457*521fa314SDavid van Moolenbroek main(int argc, char * argv[]) 458*521fa314SDavid van Moolenbroek { 459*521fa314SDavid van Moolenbroek struct trace_proc *proc; 460*521fa314SDavid van Moolenbroek const char *output_file; 461*521fa314SDavid van Moolenbroek int status, sig, follow_fork, show_stack, grouping, first_signal; 462*521fa314SDavid van Moolenbroek pid_t pid, last_pid; 463*521fa314SDavid van Moolenbroek int c, error; 464*521fa314SDavid van Moolenbroek 465*521fa314SDavid van Moolenbroek setprogname(argv[0]); 466*521fa314SDavid van Moolenbroek 467*521fa314SDavid van Moolenbroek proc_init(); 468*521fa314SDavid van Moolenbroek 469*521fa314SDavid van Moolenbroek follow_fork = FALSE; 470*521fa314SDavid van Moolenbroek show_stack = FALSE; 471*521fa314SDavid van Moolenbroek grouping = FALSE; 472*521fa314SDavid van Moolenbroek output_file = NULL; 473*521fa314SDavid van Moolenbroek 474*521fa314SDavid van Moolenbroek allnames = FALSE; 475*521fa314SDavid van Moolenbroek verbose = 0; 476*521fa314SDavid van Moolenbroek valuesonly = 0; 477*521fa314SDavid van Moolenbroek 478*521fa314SDavid van Moolenbroek while ((c = getopt(argc, argv, "fgNsVvo:p:")) != -1) { 479*521fa314SDavid van Moolenbroek switch (c) { 480*521fa314SDavid van Moolenbroek case 'f': 481*521fa314SDavid van Moolenbroek follow_fork = TRUE; 482*521fa314SDavid van Moolenbroek break; 483*521fa314SDavid van Moolenbroek case 'g': 484*521fa314SDavid van Moolenbroek grouping = TRUE; 485*521fa314SDavid van Moolenbroek break; 486*521fa314SDavid van Moolenbroek case 'N': 487*521fa314SDavid van Moolenbroek allnames = TRUE; 488*521fa314SDavid van Moolenbroek break; 489*521fa314SDavid van Moolenbroek case 's': 490*521fa314SDavid van Moolenbroek show_stack = TRUE; 491*521fa314SDavid van Moolenbroek break; 492*521fa314SDavid van Moolenbroek case 'V': 493*521fa314SDavid van Moolenbroek valuesonly++; 494*521fa314SDavid van Moolenbroek break; 495*521fa314SDavid van Moolenbroek case 'v': 496*521fa314SDavid van Moolenbroek verbose++; 497*521fa314SDavid van Moolenbroek break; 498*521fa314SDavid van Moolenbroek case 'o': 499*521fa314SDavid van Moolenbroek output_file = optarg; 500*521fa314SDavid van Moolenbroek break; 501*521fa314SDavid van Moolenbroek case 'p': 502*521fa314SDavid van Moolenbroek pid = atoi(optarg); 503*521fa314SDavid van Moolenbroek if (pid <= 0) 504*521fa314SDavid van Moolenbroek usage(); 505*521fa314SDavid van Moolenbroek 506*521fa314SDavid van Moolenbroek if (proc_get(pid) == NULL && proc_add(pid) == NULL) 507*521fa314SDavid van Moolenbroek err(EXIT_FAILURE, NULL); 508*521fa314SDavid van Moolenbroek 509*521fa314SDavid van Moolenbroek break; 510*521fa314SDavid van Moolenbroek default: 511*521fa314SDavid van Moolenbroek usage(); 512*521fa314SDavid van Moolenbroek } 513*521fa314SDavid van Moolenbroek } 514*521fa314SDavid van Moolenbroek 515*521fa314SDavid van Moolenbroek argv += optind; 516*521fa314SDavid van Moolenbroek argc -= optind; 517*521fa314SDavid van Moolenbroek 518*521fa314SDavid van Moolenbroek first_signal = TRUE; 519*521fa314SDavid van Moolenbroek got_signal = FALSE; 520*521fa314SDavid van Moolenbroek got_info = FALSE; 521*521fa314SDavid van Moolenbroek 522*521fa314SDavid van Moolenbroek signal(SIGINT, sig_handler); 523*521fa314SDavid van Moolenbroek signal(SIGINFO, info_handler); 524*521fa314SDavid van Moolenbroek 525*521fa314SDavid van Moolenbroek /* Attach to any processes for which PIDs were given. */ 526*521fa314SDavid van Moolenbroek for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc)) { 527*521fa314SDavid van Moolenbroek if (attach(proc->pid) != 0) { 528*521fa314SDavid van Moolenbroek /* 529*521fa314SDavid van Moolenbroek * Detach from the processes that we have attached to 530*521fa314SDavid van Moolenbroek * so far, i.e. the ones with the TF_ATTACH flag. 531*521fa314SDavid van Moolenbroek */ 532*521fa314SDavid van Moolenbroek detach_stopped(); 533*521fa314SDavid van Moolenbroek 534*521fa314SDavid van Moolenbroek return EXIT_FAILURE; 535*521fa314SDavid van Moolenbroek } 536*521fa314SDavid van Moolenbroek 537*521fa314SDavid van Moolenbroek proc->trace_flags = TF_ATTACH | TF_NOCALL; 538*521fa314SDavid van Moolenbroek } 539*521fa314SDavid van Moolenbroek 540*521fa314SDavid van Moolenbroek /* If a command is given, start a child that executes the command. */ 541*521fa314SDavid van Moolenbroek if (argc >= 1) { 542*521fa314SDavid van Moolenbroek pid = fork(); 543*521fa314SDavid van Moolenbroek 544*521fa314SDavid van Moolenbroek switch (pid) { 545*521fa314SDavid van Moolenbroek case -1: 546*521fa314SDavid van Moolenbroek warn("Unable to fork"); 547*521fa314SDavid van Moolenbroek 548*521fa314SDavid van Moolenbroek detach_stopped(); 549*521fa314SDavid van Moolenbroek 550*521fa314SDavid van Moolenbroek return EXIT_FAILURE; 551*521fa314SDavid van Moolenbroek 552*521fa314SDavid van Moolenbroek case 0: 553*521fa314SDavid van Moolenbroek (void)ptrace(T_OK, 0, 0, 0); 554*521fa314SDavid van Moolenbroek 555*521fa314SDavid van Moolenbroek (void)execvp(argv[0], argv); 556*521fa314SDavid van Moolenbroek 557*521fa314SDavid van Moolenbroek err(EXIT_FAILURE, "Unable to start %s", argv[0]); 558*521fa314SDavid van Moolenbroek 559*521fa314SDavid van Moolenbroek default: 560*521fa314SDavid van Moolenbroek break; 561*521fa314SDavid van Moolenbroek } 562*521fa314SDavid van Moolenbroek 563*521fa314SDavid van Moolenbroek /* 564*521fa314SDavid van Moolenbroek * The first signal will now be SIGTRAP from the execvp(), 565*521fa314SDavid van Moolenbroek * unless that fails, in which case the child will terminate. 566*521fa314SDavid van Moolenbroek */ 567*521fa314SDavid van Moolenbroek if (wait_sig(pid, SIGTRAP) != 0) { 568*521fa314SDavid van Moolenbroek /* 569*521fa314SDavid van Moolenbroek * If the child exited, the most likely cause is a 570*521fa314SDavid van Moolenbroek * failure to execute the command. Let the child 571*521fa314SDavid van Moolenbroek * report the error, and do not say anything here. 572*521fa314SDavid van Moolenbroek */ 573*521fa314SDavid van Moolenbroek if (errno != 0) 574*521fa314SDavid van Moolenbroek warn("Unable to start process"); 575*521fa314SDavid van Moolenbroek 576*521fa314SDavid van Moolenbroek detach_stopped(); 577*521fa314SDavid van Moolenbroek 578*521fa314SDavid van Moolenbroek return EXIT_FAILURE; 579*521fa314SDavid van Moolenbroek } 580*521fa314SDavid van Moolenbroek 581*521fa314SDavid van Moolenbroek /* If we haven't already, perform the kernel magic check. */ 582*521fa314SDavid van Moolenbroek if (proc_count() == 0 && kernel_check(pid) == FALSE) { 583*521fa314SDavid van Moolenbroek warnx("Kernel magic check failed, recompile trace(1)"); 584*521fa314SDavid van Moolenbroek 585*521fa314SDavid van Moolenbroek (void)kill(pid, SIGKILL); 586*521fa314SDavid van Moolenbroek 587*521fa314SDavid van Moolenbroek detach_stopped(); 588*521fa314SDavid van Moolenbroek 589*521fa314SDavid van Moolenbroek return EXIT_FAILURE; 590*521fa314SDavid van Moolenbroek } 591*521fa314SDavid van Moolenbroek 592*521fa314SDavid van Moolenbroek if ((proc = proc_add(pid)) == NULL) { 593*521fa314SDavid van Moolenbroek warn(NULL); 594*521fa314SDavid van Moolenbroek 595*521fa314SDavid van Moolenbroek (void)kill(pid, SIGKILL); 596*521fa314SDavid van Moolenbroek 597*521fa314SDavid van Moolenbroek detach_stopped(); 598*521fa314SDavid van Moolenbroek 599*521fa314SDavid van Moolenbroek return EXIT_FAILURE; 600*521fa314SDavid van Moolenbroek } 601*521fa314SDavid van Moolenbroek 602*521fa314SDavid van Moolenbroek proc->trace_flags = 0; 603*521fa314SDavid van Moolenbroek } else 604*521fa314SDavid van Moolenbroek pid = -1; 605*521fa314SDavid van Moolenbroek 606*521fa314SDavid van Moolenbroek /* The user will have to give us at least one process to trace. */ 607*521fa314SDavid van Moolenbroek if (proc_count() == 0) 608*521fa314SDavid van Moolenbroek usage(); 609*521fa314SDavid van Moolenbroek 610*521fa314SDavid van Moolenbroek /* 611*521fa314SDavid van Moolenbroek * Open an alternative output file if needed. After that, standard 612*521fa314SDavid van Moolenbroek * error should no longer be used directly, and all output has to go 613*521fa314SDavid van Moolenbroek * through the output module. 614*521fa314SDavid van Moolenbroek */ 615*521fa314SDavid van Moolenbroek if (output_init(output_file) < 0) { 616*521fa314SDavid van Moolenbroek warn("Unable to open output file"); 617*521fa314SDavid van Moolenbroek 618*521fa314SDavid van Moolenbroek if (pid > 0) 619*521fa314SDavid van Moolenbroek (void)kill(pid, SIGKILL); 620*521fa314SDavid van Moolenbroek 621*521fa314SDavid van Moolenbroek detach_stopped(); 622*521fa314SDavid van Moolenbroek 623*521fa314SDavid van Moolenbroek return EXIT_FAILURE; 624*521fa314SDavid van Moolenbroek } 625*521fa314SDavid van Moolenbroek 626*521fa314SDavid van Moolenbroek /* 627*521fa314SDavid van Moolenbroek * All the traced processes are currently stopped. Initialize, report, 628*521fa314SDavid van Moolenbroek * and resume them. 629*521fa314SDavid van Moolenbroek */ 630*521fa314SDavid van Moolenbroek for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc)) { 631*521fa314SDavid van Moolenbroek new_proc(proc, follow_fork); 632*521fa314SDavid van Moolenbroek 633*521fa314SDavid van Moolenbroek (void)ptrace(T_SYSCALL, proc->pid, 0, 0); 634*521fa314SDavid van Moolenbroek } 635*521fa314SDavid van Moolenbroek 636*521fa314SDavid van Moolenbroek /* 637*521fa314SDavid van Moolenbroek * Handle events until there are no traced processes left. 638*521fa314SDavid van Moolenbroek */ 639*521fa314SDavid van Moolenbroek last_pid = 0; 640*521fa314SDavid van Moolenbroek error = FALSE; 641*521fa314SDavid van Moolenbroek 642*521fa314SDavid van Moolenbroek for (;;) { 643*521fa314SDavid van Moolenbroek /* If an output error occurred, exit as soon as possible. */ 644*521fa314SDavid van Moolenbroek if (!error && output_error()) { 645*521fa314SDavid van Moolenbroek detach_running(TRUE /*force*/); 646*521fa314SDavid van Moolenbroek 647*521fa314SDavid van Moolenbroek error = TRUE; 648*521fa314SDavid van Moolenbroek } 649*521fa314SDavid van Moolenbroek 650*521fa314SDavid van Moolenbroek /* 651*521fa314SDavid van Moolenbroek * If the user pressed ^C once, start detaching the processes 652*521fa314SDavid van Moolenbroek * that we did not start, if any. If the user pressed ^C 653*521fa314SDavid van Moolenbroek * twice, kill the process that we did start, if any. 654*521fa314SDavid van Moolenbroek */ 655*521fa314SDavid van Moolenbroek if (got_signal) { 656*521fa314SDavid van Moolenbroek detach_running(!first_signal); 657*521fa314SDavid van Moolenbroek 658*521fa314SDavid van Moolenbroek got_signal = FALSE; 659*521fa314SDavid van Moolenbroek first_signal = FALSE; 660*521fa314SDavid van Moolenbroek } 661*521fa314SDavid van Moolenbroek 662*521fa314SDavid van Moolenbroek /* Upon getting SIGINFO, print a list of traced processes. */ 663*521fa314SDavid van Moolenbroek if (got_info) { 664*521fa314SDavid van Moolenbroek list_info(); 665*521fa314SDavid van Moolenbroek 666*521fa314SDavid van Moolenbroek got_info = FALSE; 667*521fa314SDavid van Moolenbroek } 668*521fa314SDavid van Moolenbroek 669*521fa314SDavid van Moolenbroek /* 670*521fa314SDavid van Moolenbroek * Block until something happens to a traced process. If 671*521fa314SDavid van Moolenbroek * enabled from the command line, first try waiting for the 672*521fa314SDavid van Moolenbroek * last process for which we got results, so as to reduce call 673*521fa314SDavid van Moolenbroek * suspensions a bit. 674*521fa314SDavid van Moolenbroek */ 675*521fa314SDavid van Moolenbroek if (grouping && last_pid > 0 && 676*521fa314SDavid van Moolenbroek waitpid(last_pid, &status, WNOHANG) > 0) 677*521fa314SDavid van Moolenbroek pid = last_pid; 678*521fa314SDavid van Moolenbroek else 679*521fa314SDavid van Moolenbroek if ((pid = waitpid(-1, &status, 0)) <= 0) { 680*521fa314SDavid van Moolenbroek if (pid == -1 && errno == EINTR) continue; 681*521fa314SDavid van Moolenbroek if (pid == -1 && errno == ECHILD) break; /* all done */ 682*521fa314SDavid van Moolenbroek 683*521fa314SDavid van Moolenbroek put_fmt(NULL, "Unexpected waitpid failure: %s", 684*521fa314SDavid van Moolenbroek (pid == 0) ? "No result" : strerror(errno)); 685*521fa314SDavid van Moolenbroek put_newline(); 686*521fa314SDavid van Moolenbroek 687*521fa314SDavid van Moolenbroek /* 688*521fa314SDavid van Moolenbroek * We need waitpid to function correctly in order to 689*521fa314SDavid van Moolenbroek * detach from any attached processes, so we can do 690*521fa314SDavid van Moolenbroek * little more than just exit, effectively killing all 691*521fa314SDavid van Moolenbroek * traced processes. 692*521fa314SDavid van Moolenbroek */ 693*521fa314SDavid van Moolenbroek return EXIT_FAILURE; 694*521fa314SDavid van Moolenbroek } 695*521fa314SDavid van Moolenbroek 696*521fa314SDavid van Moolenbroek last_pid = 0; 697*521fa314SDavid van Moolenbroek 698*521fa314SDavid van Moolenbroek /* Get the trace data structure for the process. */ 699*521fa314SDavid van Moolenbroek if ((proc = proc_get(pid)) == NULL) { 700*521fa314SDavid van Moolenbroek /* 701*521fa314SDavid van Moolenbroek * The waitpid() call returned the status of a process 702*521fa314SDavid van Moolenbroek * that we have not yet seen. This must be a newly 703*521fa314SDavid van Moolenbroek * forked child. If it is not stopped, it must have 704*521fa314SDavid van Moolenbroek * died immediately, and we choose not to report it. 705*521fa314SDavid van Moolenbroek */ 706*521fa314SDavid van Moolenbroek if (!WIFSTOPPED(status)) 707*521fa314SDavid van Moolenbroek continue; 708*521fa314SDavid van Moolenbroek 709*521fa314SDavid van Moolenbroek if ((proc = proc_add(pid)) == NULL) { 710*521fa314SDavid van Moolenbroek put_fmt(NULL, 711*521fa314SDavid van Moolenbroek "Error attaching to new child %d: %s", 712*521fa314SDavid van Moolenbroek pid, strerror(errno)); 713*521fa314SDavid van Moolenbroek put_newline(); 714*521fa314SDavid van Moolenbroek 715*521fa314SDavid van Moolenbroek /* 716*521fa314SDavid van Moolenbroek * Out of memory allocating a new child object! 717*521fa314SDavid van Moolenbroek * We can not trace this child, so just let it 718*521fa314SDavid van Moolenbroek * run free by detaching from it. 719*521fa314SDavid van Moolenbroek */ 720*521fa314SDavid van Moolenbroek if (WSTOPSIG(status) != SIGSTOP) { 721*521fa314SDavid van Moolenbroek (void)ptrace(T_RESUME, pid, 0, 722*521fa314SDavid van Moolenbroek WSTOPSIG(status)); 723*521fa314SDavid van Moolenbroek 724*521fa314SDavid van Moolenbroek if (wait_sig(pid, SIGSTOP) != 0) 725*521fa314SDavid van Moolenbroek continue; /* it died.. */ 726*521fa314SDavid van Moolenbroek } 727*521fa314SDavid van Moolenbroek 728*521fa314SDavid van Moolenbroek (void)ptrace(T_DETACH, pid, 0, 0); 729*521fa314SDavid van Moolenbroek 730*521fa314SDavid van Moolenbroek continue; 731*521fa314SDavid van Moolenbroek } 732*521fa314SDavid van Moolenbroek 733*521fa314SDavid van Moolenbroek /* 734*521fa314SDavid van Moolenbroek * We must specify TF_ATTACH here, even though it may 735*521fa314SDavid van Moolenbroek * be a child of a process we started, in which case it 736*521fa314SDavid van Moolenbroek * should be killed when we exit. We do not keep track 737*521fa314SDavid van Moolenbroek * of ancestry though, so better safe than sorry. 738*521fa314SDavid van Moolenbroek */ 739*521fa314SDavid van Moolenbroek proc->trace_flags = TF_ATTACH | TF_STOPPING; 740*521fa314SDavid van Moolenbroek 741*521fa314SDavid van Moolenbroek new_proc(proc, follow_fork); 742*521fa314SDavid van Moolenbroek 743*521fa314SDavid van Moolenbroek /* Repeat entering the fork call for the child. */ 744*521fa314SDavid van Moolenbroek handle_call(proc, show_stack); 745*521fa314SDavid van Moolenbroek } 746*521fa314SDavid van Moolenbroek 747*521fa314SDavid van Moolenbroek /* If the process died, report its status and clean it up. */ 748*521fa314SDavid van Moolenbroek if (!WIFSTOPPED(status)) { 749*521fa314SDavid van Moolenbroek discard_proc(proc, status); 750*521fa314SDavid van Moolenbroek 751*521fa314SDavid van Moolenbroek continue; 752*521fa314SDavid van Moolenbroek } 753*521fa314SDavid van Moolenbroek 754*521fa314SDavid van Moolenbroek sig = WSTOPSIG(status); 755*521fa314SDavid van Moolenbroek 756*521fa314SDavid van Moolenbroek if (sig == SIGSTOP && (proc->trace_flags & TF_STOPPING)) { 757*521fa314SDavid van Moolenbroek /* We expected the process to be stopped; now it is. */ 758*521fa314SDavid van Moolenbroek proc->trace_flags &= ~TF_STOPPING; 759*521fa314SDavid van Moolenbroek 760*521fa314SDavid van Moolenbroek if (proc->trace_flags & TF_DETACH) { 761*521fa314SDavid van Moolenbroek if (ptrace(T_DETACH, proc->pid, 0, 0) == 0) 762*521fa314SDavid van Moolenbroek discard_proc(proc, status); 763*521fa314SDavid van Moolenbroek 764*521fa314SDavid van Moolenbroek /* 765*521fa314SDavid van Moolenbroek * If detaching failed, the process must have 766*521fa314SDavid van Moolenbroek * died, and we'll get notified through wait(). 767*521fa314SDavid van Moolenbroek */ 768*521fa314SDavid van Moolenbroek continue; 769*521fa314SDavid van Moolenbroek } 770*521fa314SDavid van Moolenbroek 771*521fa314SDavid van Moolenbroek sig = 0; 772*521fa314SDavid van Moolenbroek } else if (sig == SIGSTOP && (proc->trace_flags & TF_EXEC)) { 773*521fa314SDavid van Moolenbroek /* The process has performed a successful execve(). */ 774*521fa314SDavid van Moolenbroek call_leave(proc, TRUE /*skip*/); 775*521fa314SDavid van Moolenbroek 776*521fa314SDavid van Moolenbroek put_text(proc, "---"); 777*521fa314SDavid van Moolenbroek 778*521fa314SDavid van Moolenbroek new_exec(proc); 779*521fa314SDavid van Moolenbroek 780*521fa314SDavid van Moolenbroek /* 781*521fa314SDavid van Moolenbroek * A successful execve() has no result, in the sense 782*521fa314SDavid van Moolenbroek * that there is no reply message. We should therefore 783*521fa314SDavid van Moolenbroek * not even try to copy in the reply message from the 784*521fa314SDavid van Moolenbroek * original location, because it will be invalid. 785*521fa314SDavid van Moolenbroek * Thus, we skip the exec's call leave phase entirely. 786*521fa314SDavid van Moolenbroek */ 787*521fa314SDavid van Moolenbroek proc->trace_flags &= ~TF_EXEC; 788*521fa314SDavid van Moolenbroek proc->trace_flags |= TF_SKIP; 789*521fa314SDavid van Moolenbroek 790*521fa314SDavid van Moolenbroek sig = 0; 791*521fa314SDavid van Moolenbroek } else if (sig == SIGTRAP) { 792*521fa314SDavid van Moolenbroek /* The process is entering or leaving a system call. */ 793*521fa314SDavid van Moolenbroek if (!(proc->trace_flags & TF_DETACH)) 794*521fa314SDavid van Moolenbroek handle_call(proc, show_stack); 795*521fa314SDavid van Moolenbroek 796*521fa314SDavid van Moolenbroek sig = 0; 797*521fa314SDavid van Moolenbroek } else { 798*521fa314SDavid van Moolenbroek /* The process has received a signal. */ 799*521fa314SDavid van Moolenbroek report_signal(proc, sig, show_stack); 800*521fa314SDavid van Moolenbroek 801*521fa314SDavid van Moolenbroek /* 802*521fa314SDavid van Moolenbroek * Only in this case do we pass the signal to the 803*521fa314SDavid van Moolenbroek * traced process. 804*521fa314SDavid van Moolenbroek */ 805*521fa314SDavid van Moolenbroek } 806*521fa314SDavid van Moolenbroek 807*521fa314SDavid van Moolenbroek /* 808*521fa314SDavid van Moolenbroek * Resume process execution. If this call fails, the process 809*521fa314SDavid van Moolenbroek * has probably died. We will find out soon enough. 810*521fa314SDavid van Moolenbroek */ 811*521fa314SDavid van Moolenbroek (void)ptrace(T_SYSCALL, proc->pid, 0, sig); 812*521fa314SDavid van Moolenbroek 813*521fa314SDavid van Moolenbroek last_pid = proc->pid; 814*521fa314SDavid van Moolenbroek } 815*521fa314SDavid van Moolenbroek 816*521fa314SDavid van Moolenbroek return (error) ? EXIT_FAILURE : EXIT_SUCCESS; 817*521fa314SDavid van Moolenbroek } 818