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