xref: /minix3/minix/usr.bin/trace/trace.c (revision 521fa314e2aaec3c192c15f2aaa4c677a544e62a)
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