xref: /minix3/minix/usr.bin/trace/trace.c (revision 10a44c0ee2297b10de47c333d88880b5ccc8208e)
1521fa314SDavid van Moolenbroek /* trace(1) - the MINIX3 system call tracer - by D.C. van Moolenbroek */
2521fa314SDavid van Moolenbroek 
3521fa314SDavid van Moolenbroek #include "inc.h"
4521fa314SDavid van Moolenbroek 
5521fa314SDavid van Moolenbroek #include <signal.h>
6521fa314SDavid van Moolenbroek #include <sys/wait.h>
7521fa314SDavid van Moolenbroek #include <unistd.h>
8521fa314SDavid van Moolenbroek #include <err.h>
9521fa314SDavid van Moolenbroek 
10521fa314SDavid van Moolenbroek /* Global variables, used only for a subset of the command line options. */
11*10a44c0eSDavid van Moolenbroek int timestamps;		 /* 0 = none, 1 = time w/o usecs, 2 = time w/usecs */
12521fa314SDavid van Moolenbroek int allnames;		 /* FALSE = structure field names, TRUE = all names */
13521fa314SDavid van Moolenbroek unsigned int valuesonly; /* 0 = normal, 1 = no symbols, 2 = no structures */
14521fa314SDavid van Moolenbroek unsigned int verbose;	 /* 0 = essentials, 1 = elaborate, 2 = everything */
15521fa314SDavid van Moolenbroek 
16521fa314SDavid van Moolenbroek /* Local variables, for signal handling. */
17521fa314SDavid van Moolenbroek static int got_signal, got_info;
18521fa314SDavid van Moolenbroek 
19521fa314SDavid van Moolenbroek /*
20521fa314SDavid van Moolenbroek  * Signal handler for signals that are supposed to make us terminate.  Let the
21521fa314SDavid van Moolenbroek  * main loop do the actual work, since it might be in the middle of processing
22521fa314SDavid van Moolenbroek  * a process status change right now.
23521fa314SDavid van Moolenbroek  */
24521fa314SDavid van Moolenbroek static void
sig_handler(int __unused sig)25521fa314SDavid van Moolenbroek sig_handler(int __unused sig)
26521fa314SDavid van Moolenbroek {
27521fa314SDavid van Moolenbroek 
28521fa314SDavid van Moolenbroek 	got_signal = TRUE;
29521fa314SDavid van Moolenbroek 
30521fa314SDavid van Moolenbroek }
31521fa314SDavid van Moolenbroek 
32521fa314SDavid van Moolenbroek /*
33521fa314SDavid van Moolenbroek  * Signal handler for the SIGINFO signal.  Let the main loop report on all
34521fa314SDavid van Moolenbroek  * processes currenty being traced.  Since SIGINFO is sent to the current
35521fa314SDavid van Moolenbroek  * process group, traced children may get the signal as well.  This is both
36521fa314SDavid van Moolenbroek  * intentional and impossible to prevent.
37521fa314SDavid van Moolenbroek  */
38521fa314SDavid van Moolenbroek static void
info_handler(int __unused sig)39521fa314SDavid van Moolenbroek info_handler(int __unused sig)
40521fa314SDavid van Moolenbroek {
41521fa314SDavid van Moolenbroek 
42521fa314SDavid van Moolenbroek 	got_info = TRUE;
43521fa314SDavid van Moolenbroek }
44521fa314SDavid van Moolenbroek 
45521fa314SDavid van Moolenbroek /*
46521fa314SDavid van Moolenbroek  * Print a list of traced processes and their call status.  We must not
47521fa314SDavid van Moolenbroek  * interfere with actual process output, so perform out-of-band printing
48521fa314SDavid van Moolenbroek  * (with info lines rather than lines prefixed by each process's PID).
49521fa314SDavid van Moolenbroek  */
50521fa314SDavid van Moolenbroek static void
list_info(void)51521fa314SDavid van Moolenbroek list_info(void)
52521fa314SDavid van Moolenbroek {
53521fa314SDavid van Moolenbroek 	struct trace_proc *proc;
54521fa314SDavid van Moolenbroek 	int no_call, in_call;
55521fa314SDavid van Moolenbroek 
56521fa314SDavid van Moolenbroek 	put_newline();
57521fa314SDavid van Moolenbroek 
58521fa314SDavid van Moolenbroek 	for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc)) {
59521fa314SDavid van Moolenbroek 		/*
60521fa314SDavid van Moolenbroek 		 * When attaching to an existing process, there is no way to
61521fa314SDavid van Moolenbroek 		 * find out whether the process is in a system call or not.
62521fa314SDavid van Moolenbroek 		 */
63521fa314SDavid van Moolenbroek 		no_call = (proc->trace_flags & TF_NOCALL);
64521fa314SDavid van Moolenbroek 		in_call = (proc->trace_flags & TF_INCALL);
65521fa314SDavid van Moolenbroek 		assert(!in_call || !no_call);
66521fa314SDavid van Moolenbroek 
67521fa314SDavid van Moolenbroek 		put_fmt(NULL, "Tracing %s (pid %d), %s%s%s", proc->name,
68521fa314SDavid van Moolenbroek 		    proc->pid, no_call ? "call status unknown" :
69521fa314SDavid van Moolenbroek 		    (in_call ? "in a " : "not in a call"),
70521fa314SDavid van Moolenbroek 		    in_call ? call_name(proc) : "",
71521fa314SDavid van Moolenbroek 		    in_call ? " call" : "");
72521fa314SDavid van Moolenbroek 		put_newline();
73521fa314SDavid van Moolenbroek 	}
74521fa314SDavid van Moolenbroek }
75521fa314SDavid van Moolenbroek 
76521fa314SDavid van Moolenbroek /*
77521fa314SDavid van Moolenbroek  * Either we have just started or attached to the given process, it the process
78521fa314SDavid van Moolenbroek  * has performed a successful execve() call.  Obtain the new process name, and
79521fa314SDavid van Moolenbroek  * print a banner for it.
80521fa314SDavid van Moolenbroek  */
81521fa314SDavid van Moolenbroek static void
new_exec(struct trace_proc * proc)82521fa314SDavid van Moolenbroek new_exec(struct trace_proc * proc)
83521fa314SDavid van Moolenbroek {
84521fa314SDavid van Moolenbroek 
85521fa314SDavid van Moolenbroek 	/* Failure to obtain the process name is worrisome, but not fatal.. */
86521fa314SDavid van Moolenbroek 	if (kernel_get_name(proc->pid, proc->name, sizeof(proc->name)) < 0)
87521fa314SDavid van Moolenbroek 		strlcpy(proc->name, "<unknown>", sizeof(proc->name));
88521fa314SDavid van Moolenbroek 
89521fa314SDavid van Moolenbroek 	put_newline();
90521fa314SDavid van Moolenbroek 	put_fmt(proc, "Tracing %s (pid %d)", proc->name, proc->pid);
91521fa314SDavid van Moolenbroek 	put_newline();
92521fa314SDavid van Moolenbroek }
93521fa314SDavid van Moolenbroek 
94521fa314SDavid van Moolenbroek /*
95521fa314SDavid van Moolenbroek  * We have started or attached to a process.  Set the appropriate flags, and
96521fa314SDavid van Moolenbroek  * print a banner showing that we are now tracing it.
97521fa314SDavid van Moolenbroek  */
98521fa314SDavid van Moolenbroek static void
new_proc(struct trace_proc * proc,int follow_fork)99521fa314SDavid van Moolenbroek new_proc(struct trace_proc * proc, int follow_fork)
100521fa314SDavid van Moolenbroek {
101521fa314SDavid van Moolenbroek 	int fl;
102521fa314SDavid van Moolenbroek 
103521fa314SDavid van Moolenbroek 	/* Set the desired tracing options. */
104521fa314SDavid van Moolenbroek 	fl = TO_ALTEXEC;
105521fa314SDavid van Moolenbroek 	if (follow_fork) fl |= TO_TRACEFORK;
106521fa314SDavid van Moolenbroek 
107521fa314SDavid van Moolenbroek 	(void)ptrace(T_SETOPT, proc->pid, 0, fl);
108521fa314SDavid van Moolenbroek 
109521fa314SDavid van Moolenbroek 	/*
110521fa314SDavid van Moolenbroek 	 * When attaching to an arbitrary process, this process might be in the
111521fa314SDavid van Moolenbroek 	 * middle of an execve().  Now that we have enabled TO_ALTEXEC, we may
112521fa314SDavid van Moolenbroek 	 * now get a SIGSTOP signal next.  Guard against this by marking the
113521fa314SDavid van Moolenbroek 	 * first system call as a possible execve().
114521fa314SDavid van Moolenbroek 	 */
115521fa314SDavid van Moolenbroek 	if ((proc->trace_flags & (TF_ATTACH | TF_STOPPING)) == TF_ATTACH)
116521fa314SDavid van Moolenbroek 		proc->trace_flags |= TF_EXEC;
117521fa314SDavid van Moolenbroek 
118521fa314SDavid van Moolenbroek 	new_exec(proc);
119521fa314SDavid van Moolenbroek }
120521fa314SDavid van Moolenbroek 
121521fa314SDavid van Moolenbroek /*
122521fa314SDavid van Moolenbroek  * A process has terminated or is being detached.  Print the resulting status.
123521fa314SDavid van Moolenbroek  */
124521fa314SDavid van Moolenbroek static void
discard_proc(struct trace_proc * proc,int status)125521fa314SDavid van Moolenbroek discard_proc(struct trace_proc * proc, int status)
126521fa314SDavid van Moolenbroek {
127521fa314SDavid van Moolenbroek 	const char *signame;
128521fa314SDavid van Moolenbroek 
129521fa314SDavid van Moolenbroek 	/*
130521fa314SDavid van Moolenbroek 	 * The exit() calls are of type no-return, meaning they are expected
131521fa314SDavid van Moolenbroek 	 * not to return.  However, calls of this type may in fact return an
132521fa314SDavid van Moolenbroek 	 * error, in which case the error must be printed.  Thus, such calls
133521fa314SDavid van Moolenbroek 	 * are not actually finished until the end of the call-leave phase.
134521fa314SDavid van Moolenbroek 	 * For exit() calls, a successful call will never get to the call-leave
135521fa314SDavid van Moolenbroek 	 * phase.  The result is that such calls will end up being shown as
136521fa314SDavid van Moolenbroek 	 * suspended, which is unintuitive.  To counter this, we pretend that a
137521fa314SDavid van Moolenbroek 	 * clean process exit is in fact preceded by a call-leave event, thus
138521fa314SDavid van Moolenbroek 	 * allowing the call to be printed without suspension.  An example:
139521fa314SDavid van Moolenbroek 	 *
140521fa314SDavid van Moolenbroek 	 *        3| exit(0) <..>
141521fa314SDavid van Moolenbroek 	 *        2| setsid() = 2
142521fa314SDavid van Moolenbroek 	 * [A]    3| exit(0)
143521fa314SDavid van Moolenbroek 	 *        3| Process exited normally with code 0
144521fa314SDavid van Moolenbroek 	 *
145521fa314SDavid van Moolenbroek 	 * The [A] line is the result of the following code.
146521fa314SDavid van Moolenbroek 	 */
147521fa314SDavid van Moolenbroek 	if (WIFEXITED(status) && (proc->trace_flags & TF_INCALL))
148521fa314SDavid van Moolenbroek 		call_leave(proc, TRUE /*skip*/);
149521fa314SDavid van Moolenbroek 
150521fa314SDavid van Moolenbroek 	put_newline();
151521fa314SDavid van Moolenbroek 	if (WIFEXITED(status)) {
152521fa314SDavid van Moolenbroek 		put_fmt(proc, "Process exited normally with code %d",
153521fa314SDavid van Moolenbroek 		    WEXITSTATUS(status));
154521fa314SDavid van Moolenbroek 	} else if (WIFSIGNALED(status)) {
155521fa314SDavid van Moolenbroek 		if ((signame = get_signal_name(WTERMSIG(status))) != NULL)
156521fa314SDavid van Moolenbroek 			put_fmt(proc, "Process terminated from signal %s",
157521fa314SDavid van Moolenbroek 			    signame);
158521fa314SDavid van Moolenbroek 		else
159521fa314SDavid van Moolenbroek 			put_fmt(proc, "Process terminated from signal %d",
160521fa314SDavid van Moolenbroek 			    WTERMSIG(status));
161521fa314SDavid van Moolenbroek 	} else if (WIFSTOPPED(status))
162521fa314SDavid van Moolenbroek 		put_text(proc, "Process detached");
163521fa314SDavid van Moolenbroek 	else
164521fa314SDavid van Moolenbroek 		put_fmt(proc, "Bogus wait result (%04x)", status);
165521fa314SDavid van Moolenbroek 	put_newline();
166521fa314SDavid van Moolenbroek 
167521fa314SDavid van Moolenbroek 	proc_del(proc);
168521fa314SDavid van Moolenbroek }
169521fa314SDavid van Moolenbroek 
170521fa314SDavid van Moolenbroek /*
171521fa314SDavid van Moolenbroek  * The given process has been stopped on a system call, either entering or
172521fa314SDavid van Moolenbroek  * leaving that call.
173521fa314SDavid van Moolenbroek  */
174521fa314SDavid van Moolenbroek static void
handle_call(struct trace_proc * proc,int show_stack)175521fa314SDavid van Moolenbroek handle_call(struct trace_proc * proc, int show_stack)
176521fa314SDavid van Moolenbroek {
177521fa314SDavid van Moolenbroek 	reg_t pc, sp;
178521fa314SDavid van Moolenbroek 	int class, skip, new_ctx;
179521fa314SDavid van Moolenbroek 
180521fa314SDavid van Moolenbroek 	proc->trace_flags &= ~TF_NOCALL;
181521fa314SDavid van Moolenbroek 
182521fa314SDavid van Moolenbroek 	if (proc->trace_flags & TF_SKIP) {
183521fa314SDavid van Moolenbroek 		/* Skip the call leave phase after a successful execve(). */
184521fa314SDavid van Moolenbroek 		proc->trace_flags &= ~(TF_INCALL | TF_SKIP);
185521fa314SDavid van Moolenbroek 	} else if (!(proc->trace_flags & TF_INCALL)) {
186521fa314SDavid van Moolenbroek 		/*
187521fa314SDavid van Moolenbroek 		 * The call_enter call returns the class of the call:
188521fa314SDavid van Moolenbroek 		 * TC_NORMAL, TC_EXEC, or TC_SIGRET.  TC_EXEC means that an
189521fa314SDavid van Moolenbroek 		 * execve() call is being performed.  This means that if a
190521fa314SDavid van Moolenbroek 		 * SIGSTOP follows for the current process, the process has
191521fa314SDavid van Moolenbroek 		 * successfully started a different executable.  TC_SIGRET
192521fa314SDavid van Moolenbroek 		 * means that if successful, the call will have a bogus return
193521fa314SDavid van Moolenbroek 		 * value.  TC_NORMAL means that the call requires no exception.
194521fa314SDavid van Moolenbroek 		 */
195521fa314SDavid van Moolenbroek 		class = call_enter(proc, show_stack);
196521fa314SDavid van Moolenbroek 
197521fa314SDavid van Moolenbroek 		switch (class) {
198521fa314SDavid van Moolenbroek 		case TC_NORMAL:
199521fa314SDavid van Moolenbroek 			break;
200521fa314SDavid van Moolenbroek 		case TC_EXEC:
201521fa314SDavid van Moolenbroek 			proc->trace_flags |= TF_EXEC;
202521fa314SDavid van Moolenbroek 			break;
203521fa314SDavid van Moolenbroek 		case TC_SIGRET:
204521fa314SDavid van Moolenbroek 			proc->trace_flags |= TF_CTX_SKIP;
205521fa314SDavid van Moolenbroek 			break;
206521fa314SDavid van Moolenbroek 		default:
207521fa314SDavid van Moolenbroek 			assert(0);
208521fa314SDavid van Moolenbroek 		}
209521fa314SDavid van Moolenbroek 
210521fa314SDavid van Moolenbroek 		/* Save the current program counter and stack pointer. */
211521fa314SDavid van Moolenbroek 		if (!kernel_get_context(proc->pid, &pc, &sp, NULL /*fp*/)) {
212521fa314SDavid van Moolenbroek 			proc->last_pc = pc;
213521fa314SDavid van Moolenbroek 			proc->last_sp = sp;
214521fa314SDavid van Moolenbroek 		} else
215521fa314SDavid van Moolenbroek 			proc->last_pc = proc->last_sp = 0;
216521fa314SDavid van Moolenbroek 
217521fa314SDavid van Moolenbroek 		proc->trace_flags |= TF_INCALL;
218521fa314SDavid van Moolenbroek 	} else {
219521fa314SDavid van Moolenbroek 		/*
220521fa314SDavid van Moolenbroek 		 * Check if the program counter or stack pointer have changed
221521fa314SDavid van Moolenbroek 		 * during the system call.  If so, this is a strong indication
222521fa314SDavid van Moolenbroek 		 * that a sigreturn call has succeeded, and thus its result
223521fa314SDavid van Moolenbroek 		 * must be skipped, since the result register will not contain
224521fa314SDavid van Moolenbroek 		 * the result of the call.
225521fa314SDavid van Moolenbroek 		 */
226521fa314SDavid van Moolenbroek 		new_ctx = (proc->last_pc != 0 &&
227521fa314SDavid van Moolenbroek 		    !kernel_get_context(proc->pid, &pc, &sp, NULL /*fp*/) &&
228521fa314SDavid van Moolenbroek 		    (pc != proc->last_pc || sp != proc->last_sp));
229521fa314SDavid van Moolenbroek 
230521fa314SDavid van Moolenbroek 		skip = ((proc->trace_flags & TF_CTX_SKIP) && new_ctx);
231521fa314SDavid van Moolenbroek 
232521fa314SDavid van Moolenbroek 		call_leave(proc, skip);
233521fa314SDavid van Moolenbroek 
234521fa314SDavid van Moolenbroek 		/*
235521fa314SDavid van Moolenbroek 		 * On such context changes, also print a short dashed line.
236521fa314SDavid van Moolenbroek 		 * This helps in identifying signal handler invocations,
237521fa314SDavid van Moolenbroek 		 * although it is not reliable for that purpose: no dashed line
238521fa314SDavid van Moolenbroek 		 * will be printed if a signal handler is invoked while the
239521fa314SDavid van Moolenbroek 		 * process is not making a system call.
240521fa314SDavid van Moolenbroek 		 */
241521fa314SDavid van Moolenbroek 		if (new_ctx) {
242521fa314SDavid van Moolenbroek 			put_text(proc, "---");
243521fa314SDavid van Moolenbroek 			put_newline();
244521fa314SDavid van Moolenbroek 		}
245521fa314SDavid van Moolenbroek 
246521fa314SDavid van Moolenbroek 		proc->trace_flags &= ~(TF_INCALL | TF_CTX_SKIP | TF_EXEC);
247521fa314SDavid van Moolenbroek 	}
248521fa314SDavid van Moolenbroek }
249521fa314SDavid van Moolenbroek 
250521fa314SDavid van Moolenbroek /*
251521fa314SDavid van Moolenbroek  * The given process has received the given signal.  Report the receipt.  Due
252521fa314SDavid van Moolenbroek  * to the way that signal handling with traced processes works, the signal may
253521fa314SDavid van Moolenbroek  * in fact be delivered to the process much later, or never--a problem inherent
254521fa314SDavid van Moolenbroek  * to the way signals are handled in PM right now (namely, deferring signal
255521fa314SDavid van Moolenbroek  * delivery would let the traced process block signals meant for the tracer).
256521fa314SDavid van Moolenbroek  */
257521fa314SDavid van Moolenbroek static void
report_signal(struct trace_proc * proc,int sig,int show_stack)258521fa314SDavid van Moolenbroek report_signal(struct trace_proc * proc, int sig, int show_stack)
259521fa314SDavid van Moolenbroek {
260521fa314SDavid van Moolenbroek 	const char *signame;
261521fa314SDavid van Moolenbroek 
262521fa314SDavid van Moolenbroek 	/*
263521fa314SDavid van Moolenbroek 	 * Print a stack trace only if we are not in a call; otherwise, we
264521fa314SDavid van Moolenbroek 	 * would simply get the same stack trace twice and mess up the output
265521fa314SDavid van Moolenbroek 	 * in the process, because call suspension is not expected if we are
266521fa314SDavid van Moolenbroek 	 * tracing a single process only.
267521fa314SDavid van Moolenbroek 	 * FIXME: the check should be for whether we actually print the call..
268521fa314SDavid van Moolenbroek 	 */
269521fa314SDavid van Moolenbroek 	if (show_stack && !(proc->trace_flags & TF_INCALL))
270521fa314SDavid van Moolenbroek 		kernel_put_stacktrace(proc);
271521fa314SDavid van Moolenbroek 
272521fa314SDavid van Moolenbroek 	/*
273521fa314SDavid van Moolenbroek 	 * If this process is in the middle of a call, the signal will be
274521fa314SDavid van Moolenbroek 	 * printed within the call.  This will always happen on the call split,
275521fa314SDavid van Moolenbroek 	 * that is, between the call's entering (out) and leaving (in) phases.
276521fa314SDavid van Moolenbroek 	 * This also means that the recording of the call-enter phase may be
277521fa314SDavid van Moolenbroek 	 * replayed more than once, and the call may be suspended more than
278521fa314SDavid van Moolenbroek 	 * once--after all, a signal is not necessarily followed immediately
279521fa314SDavid van Moolenbroek 	 * by the call result.  If the process is not in the middle of a call,
280521fa314SDavid van Moolenbroek 	 * the signal will end up on a separate line.  In both cases, multiple
281521fa314SDavid van Moolenbroek 	 * consecutive signals may be printed right after one another.  The
282521fa314SDavid van Moolenbroek 	 * following scenario shows a number of possible combinations:
283521fa314SDavid van Moolenbroek 	 *
284521fa314SDavid van Moolenbroek 	 *       2| foo(<..>
285521fa314SDavid van Moolenbroek 	 *       3| ** SIGHUP ** ** SIGUSR1 **
286521fa314SDavid van Moolenbroek 	 *       3| bar() = <..>
287521fa314SDavid van Moolenbroek 	 *       2|*foo(** SIGUSR1 ** ** SIGUSR2 ** <..>
288521fa314SDavid van Moolenbroek 	 *       3|*bar() = ** SIGCHLD ** 0
289521fa314SDavid van Moolenbroek 	 *       2|*foo(** SIGINT ** &0xef852000) = -1 [EINTR]
290521fa314SDavid van Moolenbroek 	 *       3| kill(3, SIGTERM) = ** SIGTERM ** <..>
291521fa314SDavid van Moolenbroek 	 *       3| Process terminated from signal SIGTERM
292521fa314SDavid van Moolenbroek 	 */
293521fa314SDavid van Moolenbroek 
294521fa314SDavid van Moolenbroek 	call_replay(proc);
295521fa314SDavid van Moolenbroek 
296521fa314SDavid van Moolenbroek 	if (!valuesonly && (signame = get_signal_name(sig)) != NULL)
297521fa314SDavid van Moolenbroek 		put_fmt(proc, "** %s **", signame);
298521fa314SDavid van Moolenbroek 	else
299521fa314SDavid van Moolenbroek 		put_fmt(proc, "** SIGNAL %d **", sig);
300521fa314SDavid van Moolenbroek 
301521fa314SDavid van Moolenbroek 	put_space(proc);
302521fa314SDavid van Moolenbroek 
303521fa314SDavid van Moolenbroek 	output_flush();
304521fa314SDavid van Moolenbroek }
305521fa314SDavid van Moolenbroek 
306521fa314SDavid van Moolenbroek /*
307521fa314SDavid van Moolenbroek  * Wait for the given process ID to stop on the given signal.  Upon success,
308521fa314SDavid van Moolenbroek  * the function will return zero.  Upon failure, it will return -1, and errno
309521fa314SDavid van Moolenbroek  * will be either set to an error code, or to zero in order to indicate that
310521fa314SDavid van Moolenbroek  * the process exited instead.
311521fa314SDavid van Moolenbroek  */
312521fa314SDavid van Moolenbroek static int
wait_sig(pid_t pid,int sig)313521fa314SDavid van Moolenbroek wait_sig(pid_t pid, int sig)
314521fa314SDavid van Moolenbroek {
315521fa314SDavid van Moolenbroek 	int status;
316521fa314SDavid van Moolenbroek 
317521fa314SDavid van Moolenbroek 	for (;;) {
318521fa314SDavid van Moolenbroek 		if (waitpid(pid, &status, 0) == -1) {
319521fa314SDavid van Moolenbroek 			if (errno == EINTR) continue;
320521fa314SDavid van Moolenbroek 
321521fa314SDavid van Moolenbroek 			return -1;
322521fa314SDavid van Moolenbroek 		}
323521fa314SDavid van Moolenbroek 
324521fa314SDavid van Moolenbroek 		if (!WIFSTOPPED(status)) {
325521fa314SDavid van Moolenbroek 			/* The process terminated just now. */
326521fa314SDavid van Moolenbroek 			errno = 0;
327521fa314SDavid van Moolenbroek 
328521fa314SDavid van Moolenbroek 			return -1;
329521fa314SDavid van Moolenbroek 		}
330521fa314SDavid van Moolenbroek 
331521fa314SDavid van Moolenbroek 		if (WSTOPSIG(status) == sig)
332521fa314SDavid van Moolenbroek 			break;
333521fa314SDavid van Moolenbroek 
334521fa314SDavid van Moolenbroek 		(void)ptrace(T_RESUME, pid, 0, WSTOPSIG(status));
335521fa314SDavid van Moolenbroek 	}
336521fa314SDavid van Moolenbroek 
337521fa314SDavid van Moolenbroek 	return 0;
338521fa314SDavid van Moolenbroek }
339521fa314SDavid van Moolenbroek 
340521fa314SDavid van Moolenbroek /*
341521fa314SDavid van Moolenbroek  * Attach to the given process, and wait for the resulting SIGSTOP signal.
342521fa314SDavid van Moolenbroek  * Other signals may arrive first; we pass these on to the process without
343521fa314SDavid van Moolenbroek  * reporting them, thus logically modelling them as having arrived before we
344521fa314SDavid van Moolenbroek  * attached to the process.  The process might also exit in the meantime,
345521fa314SDavid van Moolenbroek  * typically as a result of a lethal signal; following the same logical model,
346521fa314SDavid van Moolenbroek  * we pretend the process did not exist in the first place.  Since the SIGSTOP
347521fa314SDavid van Moolenbroek  * signal will be pending right after attaching to the process, this procedure
348521fa314SDavid van Moolenbroek  * will never block.
349521fa314SDavid van Moolenbroek  */
350521fa314SDavid van Moolenbroek static int
attach(pid_t pid)351521fa314SDavid van Moolenbroek attach(pid_t pid)
352521fa314SDavid van Moolenbroek {
353521fa314SDavid van Moolenbroek 
354521fa314SDavid van Moolenbroek 	if (ptrace(T_ATTACH, pid, 0, 0) != 0) {
355521fa314SDavid van Moolenbroek 		warn("Unable to attach to pid %d", pid);
356521fa314SDavid van Moolenbroek 
357521fa314SDavid van Moolenbroek 		return -1;
358521fa314SDavid van Moolenbroek 	}
359521fa314SDavid van Moolenbroek 
360521fa314SDavid van Moolenbroek 	if (wait_sig(pid, SIGSTOP) != 0) {
361521fa314SDavid van Moolenbroek 		/* If the process terminated, report it as not found. */
362521fa314SDavid van Moolenbroek 		if (errno == 0)
363521fa314SDavid van Moolenbroek 			errno = ESRCH;
364521fa314SDavid van Moolenbroek 
365521fa314SDavid van Moolenbroek 		warn("Unable to attach to pid %d", pid);
366521fa314SDavid van Moolenbroek 
367521fa314SDavid van Moolenbroek 		return -1;
368521fa314SDavid van Moolenbroek 	}
369521fa314SDavid van Moolenbroek 
370521fa314SDavid van Moolenbroek 	/* Verify that we can read values from the kernel at all. */
371521fa314SDavid van Moolenbroek 	if (kernel_check(pid) == FALSE) {
372521fa314SDavid van Moolenbroek 		(void)ptrace(T_DETACH, pid, 0, 0);
373521fa314SDavid van Moolenbroek 
374521fa314SDavid van Moolenbroek 		warnx("Kernel magic check failed, recompile trace(1)");
375521fa314SDavid van Moolenbroek 
376521fa314SDavid van Moolenbroek 		return -1;
377521fa314SDavid van Moolenbroek 	}
378521fa314SDavid van Moolenbroek 
379521fa314SDavid van Moolenbroek 	/*
380521fa314SDavid van Moolenbroek 	 * System services are managed by RS, which prevents them from
381521fa314SDavid van Moolenbroek 	 * being traced properly by PM.  Attaching to a service could
382521fa314SDavid van Moolenbroek 	 * therefore cause problems, so we should detach immediately.
383521fa314SDavid van Moolenbroek 	 */
384521fa314SDavid van Moolenbroek 	if (kernel_is_service(pid) == TRUE) {
385521fa314SDavid van Moolenbroek 		(void)ptrace(T_DETACH, pid, 0, 0);
386521fa314SDavid van Moolenbroek 
387521fa314SDavid van Moolenbroek 		warnx("Cannot attach to system services!");
388521fa314SDavid van Moolenbroek 
389521fa314SDavid van Moolenbroek 		return -1;
390521fa314SDavid van Moolenbroek 	}
391521fa314SDavid van Moolenbroek 
392521fa314SDavid van Moolenbroek 	return 0;
393521fa314SDavid van Moolenbroek }
394521fa314SDavid van Moolenbroek 
395521fa314SDavid van Moolenbroek /*
396521fa314SDavid van Moolenbroek  * Detach from all processes, knowning that they were all processes to which we
397521fa314SDavid van Moolenbroek  * attached explicitly (i.e., not started by us) and are all currently stopped.
398521fa314SDavid van Moolenbroek  */
399521fa314SDavid van Moolenbroek static void
detach_stopped(void)400521fa314SDavid van Moolenbroek detach_stopped(void)
401521fa314SDavid van Moolenbroek {
402521fa314SDavid van Moolenbroek 	struct trace_proc *proc;
403521fa314SDavid van Moolenbroek 
404521fa314SDavid van Moolenbroek 	for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc))
405521fa314SDavid van Moolenbroek 		(void)ptrace(T_DETACH, proc->pid, 0, 0);
406521fa314SDavid van Moolenbroek }
407521fa314SDavid van Moolenbroek 
408521fa314SDavid van Moolenbroek /*
409521fa314SDavid van Moolenbroek  * Start detaching from all processes to which we previously attached.  The
410521fa314SDavid van Moolenbroek  * function is expected to return before detaching is completed, and the caller
411521fa314SDavid van Moolenbroek  * must deal with the new situation appropriately.  Do not touch any processes
412521fa314SDavid van Moolenbroek  * started by us (to allow graceful termination), unless force is set, in which
413521fa314SDavid van Moolenbroek  * case those processes are killed.
414521fa314SDavid van Moolenbroek  */
415521fa314SDavid van Moolenbroek static void
detach_running(int force)416521fa314SDavid van Moolenbroek detach_running(int force)
417521fa314SDavid van Moolenbroek {
418521fa314SDavid van Moolenbroek 	struct trace_proc *proc;
419521fa314SDavid van Moolenbroek 
420521fa314SDavid van Moolenbroek 	for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc)) {
421521fa314SDavid van Moolenbroek 		if (proc->trace_flags & TF_ATTACH) {
422521fa314SDavid van Moolenbroek 			/* Already detaching?  Then do nothing. */
423521fa314SDavid van Moolenbroek 			if (proc->trace_flags & TF_DETACH)
424521fa314SDavid van Moolenbroek 				continue;
425521fa314SDavid van Moolenbroek 
426521fa314SDavid van Moolenbroek 			if (!(proc->trace_flags & TF_STOPPING))
427521fa314SDavid van Moolenbroek 				(void)kill(proc->pid, SIGSTOP);
428521fa314SDavid van Moolenbroek 
429521fa314SDavid van Moolenbroek 			proc->trace_flags |= TF_DETACH | TF_STOPPING;
430521fa314SDavid van Moolenbroek 		} else {
431521fa314SDavid van Moolenbroek 			/*
432521fa314SDavid van Moolenbroek 			 * The child processes may be ignoring SIGINTs, so upon
433521fa314SDavid van Moolenbroek 			 * the second try, force them to terminate.
434521fa314SDavid van Moolenbroek 			 */
435521fa314SDavid van Moolenbroek 			if (force)
436521fa314SDavid van Moolenbroek 				(void)kill(proc->pid, SIGKILL);
437521fa314SDavid van Moolenbroek 		}
438521fa314SDavid van Moolenbroek 	}
439521fa314SDavid van Moolenbroek }
440521fa314SDavid van Moolenbroek 
441521fa314SDavid van Moolenbroek /*
442521fa314SDavid van Moolenbroek  * Print command usage.
443521fa314SDavid van Moolenbroek  */
444521fa314SDavid van Moolenbroek static void __dead
usage(void)445521fa314SDavid van Moolenbroek usage(void)
446521fa314SDavid van Moolenbroek {
447521fa314SDavid van Moolenbroek 
448*10a44c0eSDavid van Moolenbroek 	(void)fprintf(stderr, "usage: %s [-fgNstVv] [-o file] [-p pid] "
449521fa314SDavid van Moolenbroek 	    "[command]\n", getprogname());
450521fa314SDavid van Moolenbroek 
451521fa314SDavid van Moolenbroek 	exit(EXIT_FAILURE);
452521fa314SDavid van Moolenbroek }
453521fa314SDavid van Moolenbroek 
454521fa314SDavid van Moolenbroek /*
455521fa314SDavid van Moolenbroek  * The main function of the system call tracer.
456521fa314SDavid van Moolenbroek  */
457521fa314SDavid van Moolenbroek int
main(int argc,char * argv[])458521fa314SDavid van Moolenbroek main(int argc, char * argv[])
459521fa314SDavid van Moolenbroek {
460521fa314SDavid van Moolenbroek 	struct trace_proc *proc;
461521fa314SDavid van Moolenbroek 	const char *output_file;
462521fa314SDavid van Moolenbroek 	int status, sig, follow_fork, show_stack, grouping, first_signal;
463521fa314SDavid van Moolenbroek 	pid_t pid, last_pid;
464521fa314SDavid van Moolenbroek 	int c, error;
465521fa314SDavid van Moolenbroek 
466521fa314SDavid van Moolenbroek 	setprogname(argv[0]);
467521fa314SDavid van Moolenbroek 
468521fa314SDavid van Moolenbroek 	proc_init();
469521fa314SDavid van Moolenbroek 
470521fa314SDavid van Moolenbroek 	follow_fork = FALSE;
471521fa314SDavid van Moolenbroek 	show_stack = FALSE;
472521fa314SDavid van Moolenbroek 	grouping = FALSE;
473521fa314SDavid van Moolenbroek 	output_file = NULL;
474521fa314SDavid van Moolenbroek 
475*10a44c0eSDavid van Moolenbroek 	timestamps = 0;
476521fa314SDavid van Moolenbroek 	allnames = FALSE;
477521fa314SDavid van Moolenbroek 	verbose = 0;
478521fa314SDavid van Moolenbroek 	valuesonly = 0;
479521fa314SDavid van Moolenbroek 
480*10a44c0eSDavid van Moolenbroek 	while ((c = getopt(argc, argv, "fgNstVvo:p:")) != -1) {
481521fa314SDavid van Moolenbroek 		switch (c) {
482521fa314SDavid van Moolenbroek 		case 'f':
483521fa314SDavid van Moolenbroek 			follow_fork = TRUE;
484521fa314SDavid van Moolenbroek 			break;
485521fa314SDavid van Moolenbroek 		case 'g':
486521fa314SDavid van Moolenbroek 			grouping = TRUE;
487521fa314SDavid van Moolenbroek 			break;
488521fa314SDavid van Moolenbroek 		case 'N':
489521fa314SDavid van Moolenbroek 			allnames = TRUE;
490521fa314SDavid van Moolenbroek 			break;
491521fa314SDavid van Moolenbroek 		case 's':
492521fa314SDavid van Moolenbroek 			show_stack = TRUE;
493521fa314SDavid van Moolenbroek 			break;
494*10a44c0eSDavid van Moolenbroek 		case 't':
495*10a44c0eSDavid van Moolenbroek 			timestamps++;
496*10a44c0eSDavid van Moolenbroek 			break;
497521fa314SDavid van Moolenbroek 		case 'V':
498521fa314SDavid van Moolenbroek 			valuesonly++;
499521fa314SDavid van Moolenbroek 			break;
500521fa314SDavid van Moolenbroek 		case 'v':
501521fa314SDavid van Moolenbroek 			verbose++;
502521fa314SDavid van Moolenbroek 			break;
503521fa314SDavid van Moolenbroek 		case 'o':
504521fa314SDavid van Moolenbroek 			output_file = optarg;
505521fa314SDavid van Moolenbroek 			break;
506521fa314SDavid van Moolenbroek 		case 'p':
507521fa314SDavid van Moolenbroek 			pid = atoi(optarg);
508521fa314SDavid van Moolenbroek 			if (pid <= 0)
509521fa314SDavid van Moolenbroek 				usage();
510521fa314SDavid van Moolenbroek 
511521fa314SDavid van Moolenbroek 			if (proc_get(pid) == NULL && proc_add(pid) == NULL)
512521fa314SDavid van Moolenbroek 				err(EXIT_FAILURE, NULL);
513521fa314SDavid van Moolenbroek 
514521fa314SDavid van Moolenbroek 			break;
515521fa314SDavid van Moolenbroek 		default:
516521fa314SDavid van Moolenbroek 			usage();
517521fa314SDavid van Moolenbroek 		}
518521fa314SDavid van Moolenbroek 	}
519521fa314SDavid van Moolenbroek 
520521fa314SDavid van Moolenbroek 	argv += optind;
521521fa314SDavid van Moolenbroek 	argc -= optind;
522521fa314SDavid van Moolenbroek 
523521fa314SDavid van Moolenbroek 	first_signal = TRUE;
524521fa314SDavid van Moolenbroek 	got_signal = FALSE;
525521fa314SDavid van Moolenbroek 	got_info = FALSE;
526521fa314SDavid van Moolenbroek 
527521fa314SDavid van Moolenbroek 	signal(SIGINT, sig_handler);
528521fa314SDavid van Moolenbroek 	signal(SIGINFO, info_handler);
529521fa314SDavid van Moolenbroek 
530521fa314SDavid van Moolenbroek 	/* Attach to any processes for which PIDs were given. */
531521fa314SDavid van Moolenbroek 	for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc)) {
532521fa314SDavid van Moolenbroek 		if (attach(proc->pid) != 0) {
533521fa314SDavid van Moolenbroek 			/*
534521fa314SDavid van Moolenbroek 			 * Detach from the processes that we have attached to
535521fa314SDavid van Moolenbroek 			 * so far, i.e. the ones with the TF_ATTACH flag.
536521fa314SDavid van Moolenbroek 			 */
537521fa314SDavid van Moolenbroek 			detach_stopped();
538521fa314SDavid van Moolenbroek 
539521fa314SDavid van Moolenbroek 			return EXIT_FAILURE;
540521fa314SDavid van Moolenbroek 		}
541521fa314SDavid van Moolenbroek 
542521fa314SDavid van Moolenbroek 		proc->trace_flags = TF_ATTACH | TF_NOCALL;
543521fa314SDavid van Moolenbroek 	}
544521fa314SDavid van Moolenbroek 
545521fa314SDavid van Moolenbroek 	/* If a command is given, start a child that executes the command. */
546521fa314SDavid van Moolenbroek 	if (argc >= 1) {
547521fa314SDavid van Moolenbroek 		pid = fork();
548521fa314SDavid van Moolenbroek 
549521fa314SDavid van Moolenbroek 		switch (pid) {
550521fa314SDavid van Moolenbroek 		case -1:
551521fa314SDavid van Moolenbroek 			warn("Unable to fork");
552521fa314SDavid van Moolenbroek 
553521fa314SDavid van Moolenbroek 			detach_stopped();
554521fa314SDavid van Moolenbroek 
555521fa314SDavid van Moolenbroek 			return EXIT_FAILURE;
556521fa314SDavid van Moolenbroek 
557521fa314SDavid van Moolenbroek 		case 0:
558521fa314SDavid van Moolenbroek 			(void)ptrace(T_OK, 0, 0, 0);
559521fa314SDavid van Moolenbroek 
560521fa314SDavid van Moolenbroek 			(void)execvp(argv[0], argv);
561521fa314SDavid van Moolenbroek 
562521fa314SDavid van Moolenbroek 			err(EXIT_FAILURE, "Unable to start %s", argv[0]);
563521fa314SDavid van Moolenbroek 
564521fa314SDavid van Moolenbroek 		default:
565521fa314SDavid van Moolenbroek 			break;
566521fa314SDavid van Moolenbroek 		}
567521fa314SDavid van Moolenbroek 
568521fa314SDavid van Moolenbroek 		/*
569521fa314SDavid van Moolenbroek 		 * The first signal will now be SIGTRAP from the execvp(),
570521fa314SDavid van Moolenbroek 		 * unless that fails, in which case the child will terminate.
571521fa314SDavid van Moolenbroek 		 */
572521fa314SDavid van Moolenbroek 		if (wait_sig(pid, SIGTRAP) != 0) {
573521fa314SDavid van Moolenbroek 			/*
574521fa314SDavid van Moolenbroek 			 * If the child exited, the most likely cause is a
575521fa314SDavid van Moolenbroek 			 * failure to execute the command.  Let the child
576521fa314SDavid van Moolenbroek 			 * report the error, and do not say anything here.
577521fa314SDavid van Moolenbroek 			 */
578521fa314SDavid van Moolenbroek 			if (errno != 0)
579521fa314SDavid van Moolenbroek 				warn("Unable to start process");
580521fa314SDavid van Moolenbroek 
581521fa314SDavid van Moolenbroek 			detach_stopped();
582521fa314SDavid van Moolenbroek 
583521fa314SDavid van Moolenbroek 			return EXIT_FAILURE;
584521fa314SDavid van Moolenbroek 		}
585521fa314SDavid van Moolenbroek 
586521fa314SDavid van Moolenbroek 		/* If we haven't already, perform the kernel magic check. */
587521fa314SDavid van Moolenbroek 		if (proc_count() == 0 && kernel_check(pid) == FALSE) {
588521fa314SDavid van Moolenbroek 			warnx("Kernel magic check failed, recompile trace(1)");
589521fa314SDavid van Moolenbroek 
590521fa314SDavid van Moolenbroek 			(void)kill(pid, SIGKILL);
591521fa314SDavid van Moolenbroek 
592521fa314SDavid van Moolenbroek 			detach_stopped();
593521fa314SDavid van Moolenbroek 
594521fa314SDavid van Moolenbroek 			return EXIT_FAILURE;
595521fa314SDavid van Moolenbroek 		}
596521fa314SDavid van Moolenbroek 
597521fa314SDavid van Moolenbroek 		if ((proc = proc_add(pid)) == NULL) {
598521fa314SDavid van Moolenbroek 			warn(NULL);
599521fa314SDavid van Moolenbroek 
600521fa314SDavid van Moolenbroek 			(void)kill(pid, SIGKILL);
601521fa314SDavid van Moolenbroek 
602521fa314SDavid van Moolenbroek 			detach_stopped();
603521fa314SDavid van Moolenbroek 
604521fa314SDavid van Moolenbroek 			return EXIT_FAILURE;
605521fa314SDavid van Moolenbroek 		}
606521fa314SDavid van Moolenbroek 
607521fa314SDavid van Moolenbroek 		proc->trace_flags = 0;
608521fa314SDavid van Moolenbroek 	} else
609521fa314SDavid van Moolenbroek 		pid = -1;
610521fa314SDavid van Moolenbroek 
611521fa314SDavid van Moolenbroek 	/* The user will have to give us at least one process to trace. */
612521fa314SDavid van Moolenbroek 	if (proc_count() == 0)
613521fa314SDavid van Moolenbroek 		usage();
614521fa314SDavid van Moolenbroek 
615521fa314SDavid van Moolenbroek 	/*
616521fa314SDavid van Moolenbroek 	 * Open an alternative output file if needed.  After that, standard
617521fa314SDavid van Moolenbroek 	 * error should no longer be used directly, and all output has to go
618521fa314SDavid van Moolenbroek 	 * through the output module.
619521fa314SDavid van Moolenbroek 	 */
620521fa314SDavid van Moolenbroek 	if (output_init(output_file) < 0) {
621521fa314SDavid van Moolenbroek 		warn("Unable to open output file");
622521fa314SDavid van Moolenbroek 
623521fa314SDavid van Moolenbroek 		if (pid > 0)
624521fa314SDavid van Moolenbroek 			(void)kill(pid, SIGKILL);
625521fa314SDavid van Moolenbroek 
626521fa314SDavid van Moolenbroek 		detach_stopped();
627521fa314SDavid van Moolenbroek 
628521fa314SDavid van Moolenbroek 		return EXIT_FAILURE;
629521fa314SDavid van Moolenbroek 	}
630521fa314SDavid van Moolenbroek 
631521fa314SDavid van Moolenbroek 	/*
632521fa314SDavid van Moolenbroek 	 * All the traced processes are currently stopped.  Initialize, report,
633521fa314SDavid van Moolenbroek 	 * and resume them.
634521fa314SDavid van Moolenbroek 	 */
635521fa314SDavid van Moolenbroek 	for (proc = proc_next(NULL); proc != NULL; proc = proc_next(proc)) {
636521fa314SDavid van Moolenbroek 		new_proc(proc, follow_fork);
637521fa314SDavid van Moolenbroek 
638521fa314SDavid van Moolenbroek 		(void)ptrace(T_SYSCALL, proc->pid, 0, 0);
639521fa314SDavid van Moolenbroek 	}
640521fa314SDavid van Moolenbroek 
641521fa314SDavid van Moolenbroek 	/*
642521fa314SDavid van Moolenbroek 	 * Handle events until there are no traced processes left.
643521fa314SDavid van Moolenbroek 	 */
644521fa314SDavid van Moolenbroek 	last_pid = 0;
645521fa314SDavid van Moolenbroek 	error = FALSE;
646521fa314SDavid van Moolenbroek 
647521fa314SDavid van Moolenbroek 	for (;;) {
648521fa314SDavid van Moolenbroek 		/* If an output error occurred, exit as soon as possible. */
649521fa314SDavid van Moolenbroek 		if (!error && output_error()) {
650521fa314SDavid van Moolenbroek 			detach_running(TRUE /*force*/);
651521fa314SDavid van Moolenbroek 
652521fa314SDavid van Moolenbroek 			error = TRUE;
653521fa314SDavid van Moolenbroek 		}
654521fa314SDavid van Moolenbroek 
655521fa314SDavid van Moolenbroek 		/*
656521fa314SDavid van Moolenbroek 		 * If the user pressed ^C once, start detaching the processes
657521fa314SDavid van Moolenbroek 		 * that we did not start, if any.  If the user pressed ^C
658521fa314SDavid van Moolenbroek 		 * twice, kill the process that we did start, if any.
659521fa314SDavid van Moolenbroek 		 */
660521fa314SDavid van Moolenbroek 		if (got_signal) {
661521fa314SDavid van Moolenbroek 			detach_running(!first_signal);
662521fa314SDavid van Moolenbroek 
663521fa314SDavid van Moolenbroek 			got_signal = FALSE;
664521fa314SDavid van Moolenbroek 			first_signal = FALSE;
665521fa314SDavid van Moolenbroek 		}
666521fa314SDavid van Moolenbroek 
667521fa314SDavid van Moolenbroek 		/* Upon getting SIGINFO, print a list of traced processes. */
668521fa314SDavid van Moolenbroek 		if (got_info) {
669521fa314SDavid van Moolenbroek 			list_info();
670521fa314SDavid van Moolenbroek 
671521fa314SDavid van Moolenbroek 			got_info = FALSE;
672521fa314SDavid van Moolenbroek 		}
673521fa314SDavid van Moolenbroek 
674521fa314SDavid van Moolenbroek 		/*
675521fa314SDavid van Moolenbroek 		 * Block until something happens to a traced process.  If
676521fa314SDavid van Moolenbroek 		 * enabled from the command line, first try waiting for the
677521fa314SDavid van Moolenbroek 		 * last process for which we got results, so as to reduce call
678521fa314SDavid van Moolenbroek 		 * suspensions a bit.
679521fa314SDavid van Moolenbroek 		 */
680521fa314SDavid van Moolenbroek 		if (grouping && last_pid > 0 &&
681521fa314SDavid van Moolenbroek 		    waitpid(last_pid, &status, WNOHANG) > 0)
682521fa314SDavid van Moolenbroek 			pid = last_pid;
683521fa314SDavid van Moolenbroek 		else
684521fa314SDavid van Moolenbroek 		    if ((pid = waitpid(-1, &status, 0)) <= 0) {
685521fa314SDavid van Moolenbroek 			if (pid == -1 && errno == EINTR) continue;
686521fa314SDavid van Moolenbroek 			if (pid == -1 && errno == ECHILD) break; /* all done */
687521fa314SDavid van Moolenbroek 
688521fa314SDavid van Moolenbroek 			put_fmt(NULL, "Unexpected waitpid failure: %s",
689521fa314SDavid van Moolenbroek 			    (pid == 0) ? "No result" : strerror(errno));
690521fa314SDavid van Moolenbroek 			put_newline();
691521fa314SDavid van Moolenbroek 
692521fa314SDavid van Moolenbroek 			/*
693521fa314SDavid van Moolenbroek 			 * We need waitpid to function correctly in order to
694521fa314SDavid van Moolenbroek 			 * detach from any attached processes, so we can do
695521fa314SDavid van Moolenbroek 			 * little more than just exit, effectively killing all
696521fa314SDavid van Moolenbroek 			 * traced processes.
697521fa314SDavid van Moolenbroek 			 */
698521fa314SDavid van Moolenbroek 			return EXIT_FAILURE;
699521fa314SDavid van Moolenbroek 		}
700521fa314SDavid van Moolenbroek 
701521fa314SDavid van Moolenbroek 		last_pid = 0;
702521fa314SDavid van Moolenbroek 
703521fa314SDavid van Moolenbroek 		/* Get the trace data structure for the process. */
704521fa314SDavid van Moolenbroek 		if ((proc = proc_get(pid)) == NULL) {
705521fa314SDavid van Moolenbroek 			/*
706521fa314SDavid van Moolenbroek 			 * The waitpid() call returned the status of a process
707521fa314SDavid van Moolenbroek 			 * that we have not yet seen.  This must be a newly
708521fa314SDavid van Moolenbroek 			 * forked child.  If it is not stopped, it must have
709521fa314SDavid van Moolenbroek 			 * died immediately, and we choose not to report it.
710521fa314SDavid van Moolenbroek 			 */
711521fa314SDavid van Moolenbroek 			if (!WIFSTOPPED(status))
712521fa314SDavid van Moolenbroek 				continue;
713521fa314SDavid van Moolenbroek 
714521fa314SDavid van Moolenbroek 			if ((proc = proc_add(pid)) == NULL) {
715521fa314SDavid van Moolenbroek 				put_fmt(NULL,
716521fa314SDavid van Moolenbroek 				    "Error attaching to new child %d: %s",
717521fa314SDavid van Moolenbroek 				    pid, strerror(errno));
718521fa314SDavid van Moolenbroek 				put_newline();
719521fa314SDavid van Moolenbroek 
720521fa314SDavid van Moolenbroek 				/*
721521fa314SDavid van Moolenbroek 				 * Out of memory allocating a new child object!
722521fa314SDavid van Moolenbroek 				 * We can not trace this child, so just let it
723521fa314SDavid van Moolenbroek 				 * run free by detaching from it.
724521fa314SDavid van Moolenbroek 				 */
725521fa314SDavid van Moolenbroek 				if (WSTOPSIG(status) != SIGSTOP) {
726521fa314SDavid van Moolenbroek 					(void)ptrace(T_RESUME, pid, 0,
727521fa314SDavid van Moolenbroek 					    WSTOPSIG(status));
728521fa314SDavid van Moolenbroek 
729521fa314SDavid van Moolenbroek 					if (wait_sig(pid, SIGSTOP) != 0)
730521fa314SDavid van Moolenbroek 						continue; /* it died.. */
731521fa314SDavid van Moolenbroek 				}
732521fa314SDavid van Moolenbroek 
733521fa314SDavid van Moolenbroek 				(void)ptrace(T_DETACH, pid, 0, 0);
734521fa314SDavid van Moolenbroek 
735521fa314SDavid van Moolenbroek 				continue;
736521fa314SDavid van Moolenbroek 			}
737521fa314SDavid van Moolenbroek 
738521fa314SDavid van Moolenbroek 			/*
739521fa314SDavid van Moolenbroek 			 * We must specify TF_ATTACH here, even though it may
740521fa314SDavid van Moolenbroek 			 * be a child of a process we started, in which case it
741521fa314SDavid van Moolenbroek 			 * should be killed when we exit.  We do not keep track
742521fa314SDavid van Moolenbroek 			 * of ancestry though, so better safe than sorry.
743521fa314SDavid van Moolenbroek 			 */
744521fa314SDavid van Moolenbroek 			proc->trace_flags = TF_ATTACH | TF_STOPPING;
745521fa314SDavid van Moolenbroek 
746521fa314SDavid van Moolenbroek 			new_proc(proc, follow_fork);
747521fa314SDavid van Moolenbroek 
748521fa314SDavid van Moolenbroek 			/* Repeat entering the fork call for the child. */
749521fa314SDavid van Moolenbroek 			handle_call(proc, show_stack);
750521fa314SDavid van Moolenbroek 		}
751521fa314SDavid van Moolenbroek 
752521fa314SDavid van Moolenbroek 		/* If the process died, report its status and clean it up. */
753521fa314SDavid van Moolenbroek 		if (!WIFSTOPPED(status)) {
754521fa314SDavid van Moolenbroek 			discard_proc(proc, status);
755521fa314SDavid van Moolenbroek 
756521fa314SDavid van Moolenbroek 			continue;
757521fa314SDavid van Moolenbroek 		}
758521fa314SDavid van Moolenbroek 
759521fa314SDavid van Moolenbroek 		sig = WSTOPSIG(status);
760521fa314SDavid van Moolenbroek 
761521fa314SDavid van Moolenbroek 		if (sig == SIGSTOP && (proc->trace_flags & TF_STOPPING)) {
762521fa314SDavid van Moolenbroek 			/* We expected the process to be stopped; now it is. */
763521fa314SDavid van Moolenbroek 			proc->trace_flags &= ~TF_STOPPING;
764521fa314SDavid van Moolenbroek 
765521fa314SDavid van Moolenbroek 			if (proc->trace_flags & TF_DETACH) {
766521fa314SDavid van Moolenbroek 				if (ptrace(T_DETACH, proc->pid, 0, 0) == 0)
767521fa314SDavid van Moolenbroek 					discard_proc(proc, status);
768521fa314SDavid van Moolenbroek 
769521fa314SDavid van Moolenbroek 				/*
770521fa314SDavid van Moolenbroek 				 * If detaching failed, the process must have
771521fa314SDavid van Moolenbroek 				 * died, and we'll get notified through wait().
772521fa314SDavid van Moolenbroek 				 */
773521fa314SDavid van Moolenbroek 				continue;
774521fa314SDavid van Moolenbroek 			}
775521fa314SDavid van Moolenbroek 
776521fa314SDavid van Moolenbroek 			sig = 0;
777521fa314SDavid van Moolenbroek 		} else if (sig == SIGSTOP && (proc->trace_flags & TF_EXEC)) {
778521fa314SDavid van Moolenbroek 			/* The process has performed a successful execve(). */
779521fa314SDavid van Moolenbroek 			call_leave(proc, TRUE /*skip*/);
780521fa314SDavid van Moolenbroek 
781521fa314SDavid van Moolenbroek 			put_text(proc, "---");
782521fa314SDavid van Moolenbroek 
783521fa314SDavid van Moolenbroek 			new_exec(proc);
784521fa314SDavid van Moolenbroek 
785521fa314SDavid van Moolenbroek 			/*
786521fa314SDavid van Moolenbroek 			 * A successful execve() has no result, in the sense
787521fa314SDavid van Moolenbroek 			 * that there is no reply message.  We should therefore
788521fa314SDavid van Moolenbroek 			 * not even try to copy in the reply message from the
789521fa314SDavid van Moolenbroek 			 * original location, because it will be invalid.
790521fa314SDavid van Moolenbroek 			 * Thus, we skip the exec's call leave phase entirely.
791521fa314SDavid van Moolenbroek 			 */
792521fa314SDavid van Moolenbroek 			proc->trace_flags &= ~TF_EXEC;
793521fa314SDavid van Moolenbroek 			proc->trace_flags |= TF_SKIP;
794521fa314SDavid van Moolenbroek 
795521fa314SDavid van Moolenbroek 			sig = 0;
796521fa314SDavid van Moolenbroek 		} else if (sig == SIGTRAP) {
797521fa314SDavid van Moolenbroek 			/* The process is entering or leaving a system call. */
798521fa314SDavid van Moolenbroek 			if (!(proc->trace_flags & TF_DETACH))
799521fa314SDavid van Moolenbroek 				handle_call(proc, show_stack);
800521fa314SDavid van Moolenbroek 
801521fa314SDavid van Moolenbroek 			sig = 0;
802521fa314SDavid van Moolenbroek 		} else {
803521fa314SDavid van Moolenbroek 			/* The process has received a signal. */
804521fa314SDavid van Moolenbroek 			report_signal(proc, sig, show_stack);
805521fa314SDavid van Moolenbroek 
806521fa314SDavid van Moolenbroek 			/*
807521fa314SDavid van Moolenbroek 			 * Only in this case do we pass the signal to the
808521fa314SDavid van Moolenbroek 			 * traced process.
809521fa314SDavid van Moolenbroek 			 */
810521fa314SDavid van Moolenbroek 		}
811521fa314SDavid van Moolenbroek 
812521fa314SDavid van Moolenbroek 		/*
813521fa314SDavid van Moolenbroek 		 * Resume process execution.  If this call fails, the process
814521fa314SDavid van Moolenbroek 		 * has probably died.  We will find out soon enough.
815521fa314SDavid van Moolenbroek 		 */
816521fa314SDavid van Moolenbroek 		(void)ptrace(T_SYSCALL, proc->pid, 0, sig);
817521fa314SDavid van Moolenbroek 
818521fa314SDavid van Moolenbroek 		last_pid = proc->pid;
819521fa314SDavid van Moolenbroek 	}
820521fa314SDavid van Moolenbroek 
821521fa314SDavid van Moolenbroek 	return (error) ? EXIT_FAILURE : EXIT_SUCCESS;
822521fa314SDavid van Moolenbroek }
823