• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* strace.c - Trace system calls.
2  *
3  * Copyright 2020 The Android Open Source Project
4  *
5  * See https://man7.org/linux/man-pages/man2/syscall.2.html
6 
7 USE_STRACE(NEWTOY(strace, "^p#s#v", TOYFLAG_USR|TOYFLAG_SBIN))
8 
9 config STRACE
10   bool "strace"
11   default n
12   help
13     usage: strace [-fv] [-p PID] [-s NUM] COMMAND [ARGS...]
14 
15     Trace systems calls made by a process.
16 
17     -s	String length limit.
18     -v	Dump all of large structs/arrays.
19 */
20 
21 #include <sys/ptrace.h>
22 #include <sys/syscall.h>
23 #include <sys/user.h>
24 
25 #define FOR_strace
26 #include "toys.h"
27 
28 GLOBALS(
29   long s, p;
30 
31   char ioctl[32], *fmt;
32   long regs[256/sizeof(long)], syscall;
33   pid_t pid;
34   int arg;
35 )
36 
37   struct user_regs_struct regs;
38 
39 
40 // Syscall args from https://man7.org/linux/man-pages/man2/syscall.2.html
41 // REG_ORDER is args 0-6, SYSCALL, RESULT
42 #if defined(__ARM_EABI__)
43 static const char REG_ORDER[] = {0,1,2,3,4,5,7,0};
44 #elif defined(__ARM_ARCH) && __ARM_ARCH == 8
45 static const char REG_ORDER[] = {0,1,2,3,4,5,8,0};
46 #elif defined(__i386__)
47 // ebx,ecx,edx,esi,edi,ebp,orig_eax,eax
48 static const char REG_ORDER[] = {0,1,2,3,4,5,11,6};
49 #elif defined(__m68k__)
50 // d1,d2,d3,d4,d5,a0,orig_d0,d0
51 static const char REG_ORDER[] = {0,1,2,3,4,7,16,14);
52 #elif defined(__PPC__) || defined(__PPC64__)
53 static const char REG_ORDER[] = {3,4,5,6,7,8,0,3};
54 #elif defined(__s390__) // also covers s390x
55 // r2,r3,r4,r5,r6,r7,r1,r2 but mask+addr before r0 so +2
56 static const char REG_ORDER[] = {4,5,6,7,8,9,3,4};
57 #elif defined(__sh__)
58 static const char REG_ORDER[] = {4,5,6,7,0,1,3,0};
59 #elif defined(__x86_64__)
60 // rdi,rsi,rdx,r10,r8,r9,orig_rax,rax
61 static const char REG_ORDER[] = {14,13,12,7,9,8,15,10};
62 #else
63 #error unsupported architecture
64 #endif
65 
66 #define C(x) case x: return #x
67 
68 #define FS_IOC_FSGETXATTR 0x801c581f
69 #define FS_IOC_FSSETXATTR 0x401c5820
70 #define FS_IOC_GETFLAGS 0x80086601
71 #define FS_IOC_SETFLAGS 0x40086602
72 #define FS_IOC_GETVERSION 0x80087601
73 #define FS_IOC_SETVERSION 0x40047602
74 struct fsxattr {
75   unsigned fsx_xflags;
76   unsigned fsx_extsize;
77   unsigned fsx_nextents;
78   unsigned fsx_projid;
79   unsigned fsx_cowextsize;
80   char fsx_pad[8];
81 };
82 
83 static char *strioctl(int i)
84 {
85   switch (i) {
86     C(FS_IOC_FSGETXATTR);
87     C(FS_IOC_FSSETXATTR);
88     C(FS_IOC_GETFLAGS);
89     C(FS_IOC_GETVERSION);
90     C(FS_IOC_SETFLAGS);
91     C(FS_IOC_SETVERSION);
92     C(SIOCGIFADDR);
93     C(SIOCGIFBRDADDR);
94     C(SIOCGIFCONF);
95     C(SIOCGIFDSTADDR);
96     C(SIOCGIFFLAGS);
97     C(SIOCGIFHWADDR);
98     C(SIOCGIFMAP);
99     C(SIOCGIFMTU);
100     C(SIOCGIFNETMASK);
101     C(SIOCGIFTXQLEN);
102     C(TCGETS);
103     C(TCSETS);
104     C(TIOCGWINSZ);
105     C(TIOCSWINSZ);
106   }
107   sprintf(toybuf, "%#x", i);
108   return toybuf;
109 }
110 
111 // TODO: move to lib, implement errno(1)?
112 static char *strerrno(int e)
113 {
114   switch (e) {
115     // uapi errno-base.h
116     C(EPERM);
117     C(ENOENT);
118     C(ESRCH);
119     C(EINTR);
120     C(EIO);
121     C(ENXIO);
122     C(E2BIG);
123     C(ENOEXEC);
124     C(EBADF);
125     C(ECHILD);
126     C(EAGAIN);
127     C(ENOMEM);
128     C(EACCES);
129     C(EFAULT);
130     C(ENOTBLK);
131     C(EBUSY);
132     C(EEXIST);
133     C(EXDEV);
134     C(ENODEV);
135     C(ENOTDIR);
136     C(EISDIR);
137     C(EINVAL);
138     C(ENFILE);
139     C(EMFILE);
140     C(ENOTTY);
141     C(ETXTBSY);
142     C(EFBIG);
143     C(ENOSPC);
144     C(ESPIPE);
145     C(EROFS);
146     C(EMLINK);
147     C(EPIPE);
148     C(EDOM);
149     C(ERANGE);
150     // uapi errno.h
151     C(EDEADLK);
152     C(ENAMETOOLONG);
153     C(ENOLCK);
154     C(ENOSYS);
155     C(ENOTEMPTY);
156     C(ELOOP);
157     C(ENOMSG);
158     // ...etc; fill in as we see them in practice?
159   }
160   sprintf(toybuf, "%d", e);
161   return toybuf;
162 }
163 
164 #undef C
165 
166 static void xptrace(int req, pid_t pid, void *addr, void *data)
167 {
168   if (ptrace(req, pid, addr, data)) perror_exit("ptrace pid %d", pid);
169 }
170 
171 static void get_regs()
172 {
173   xptrace(PTRACE_GETREGS, TT.pid, 0, TT.regs);
174 }
175 
176 static void ptrace_struct(long addr, void *dst, size_t bytes)
177 {
178   int offset = 0, i;
179   long v;
180 
181   for (i=0; i<bytes; i+=sizeof(long)) {
182     errno = 0;
183     v = ptrace(PTRACE_PEEKDATA, TT.pid, addr + offset);
184     if (errno) perror_exit("PTRACE_PEEKDATA failed");
185     memcpy(dst + offset, &v, sizeof(v));
186     offset += sizeof(long);
187   }
188 }
189 
190 // TODO: this all relies on having the libc structs match the kernel structs,
191 // which isn't always true for glibc...
192 static void print_struct(long addr)
193 {
194   if (!addr) { // All NULLs look the same...
195     fprintf(stderr, "NULL");
196     while (*TT.fmt != '}') ++TT.fmt;
197     ++TT.fmt;
198   } else if (strstart(&TT.fmt, "ifreq}")) {
199     struct ifreq ir;
200 
201     ptrace_struct(addr, &ir, sizeof(ir));
202     // TODO: is this always an ioctl? use TT.regs[REG_ORDER[1]] to work out what to show.
203     fprintf(stderr, "{...}");
204   } else if (strstart(&TT.fmt, "fsxattr}")) {
205     struct fsxattr fx;
206 
207     ptrace_struct(addr, &fx, sizeof(fx));
208     fprintf(stderr, "{fsx_xflags=%#x, fsx_extsize=%d, fsx_nextents=%d, "
209         "fsx_projid=%d, fsx_cowextsize=%d}", fx.fsx_xflags, fx.fsx_extsize,
210         fx.fsx_nextents, fx.fsx_projid, fx.fsx_cowextsize);
211   } else if (strstart(&TT.fmt, "long}")) {
212     long l;
213 
214     ptrace_struct(addr, &l, sizeof(l));
215     fprintf(stderr, "%ld", l);
216   } else if (strstart(&TT.fmt, "longx}")) {
217     long l;
218 
219     ptrace_struct(addr, &l, sizeof(l));
220     fprintf(stderr, "%#lx", l);
221   } else if (strstart(&TT.fmt, "rlimit}")) {
222     struct rlimit rl;
223 
224     ptrace_struct(addr, &rl, sizeof(rl));
225     fprintf(stderr, "{rlim_cur=%lld, rlim_max=%lld}",
226         (long long)rl.rlim_cur, (long long)rl.rlim_max);
227   } else if (strstart(&TT.fmt, "sigset}")) {
228     long long ss;
229     int i;
230 
231     ptrace_struct(addr, &ss, sizeof(ss));
232     fprintf(stderr, "[");
233     for (i=0; i<64;++i) {
234       // TODO: use signal names, fix spacing
235       if (ss & (1ULL<<i)) fprintf(stderr, "%d ", i);
236     }
237     fprintf(stderr, "]");
238   } else if (strstart(&TT.fmt, "stat}")) {
239     struct stat sb;
240 
241     ptrace_struct(addr, &sb, sizeof(sb));
242     // TODO: decode IFMT bits in st_mode
243     if (FLAG(v)) {
244       // TODO: full atime/mtime/ctime dump.
245       fprintf(stderr, "{st_dev=makedev(%#x, %#x), st_ino=%ld, st_mode=%o, "
246           "st_nlink=%ld, st_uid=%d, st_gid=%d, st_blksize=%ld, st_blocks=%ld, "
247           "st_size=%lld, st_atime=%ld, st_mtime=%ld, st_ctime=%ld}",
248           dev_major(sb.st_dev), dev_minor(sb.st_dev), sb.st_ino, sb.st_mode,
249           sb.st_nlink, sb.st_uid, sb.st_gid, sb.st_blksize, sb.st_blocks,
250           (long long)sb.st_size, sb.st_atime, sb.st_mtime, sb.st_ctime);
251     } else {
252       fprintf(stderr, "{st_mode=%o, st_size=%lld, ...}", sb.st_mode,
253         (long long)sb.st_size);
254     }
255   } else if (strstart(&TT.fmt, "termios}")) {
256     struct termios to;
257 
258     ptrace_struct(addr, &to, sizeof(to));
259     fprintf(stderr, "{c_iflag=%#lx, c_oflag=%#lx, c_cflag=%#lx, c_lflag=%#lx}",
260         (long)to.c_iflag, (long)to.c_oflag, (long)to.c_cflag, (long)to.c_lflag);
261   } else if (strstart(&TT.fmt, "timespec}")) {
262     struct timespec ts;
263 
264     ptrace_struct(addr, &ts, sizeof(ts));
265     fprintf(stderr, "{tv_sec=%lld, tv_nsec=%lld}",
266         (long long)ts.tv_sec, (long long)ts.tv_nsec);
267   } else if (strstart(&TT.fmt, "winsize}")) {
268     struct winsize ws;
269 
270     ptrace_struct(addr, &ws, sizeof(ws));
271     fprintf(stderr, "{ws_row=%hu, ws_col=%hu, ws_xpixel=%hu, ws_ypixel=%hu}",
272         ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
273   } else abort();
274 }
275 
276 static void print_ptr(long addr)
277 {
278   if (!addr) fprintf(stderr, "NULL");
279   else fprintf(stderr, "0x%lx", addr);
280 }
281 
282 static void print_string(long addr)
283 {
284   long offset = 0, total = 0;
285   int done = 0, i;
286 
287   fputc('"', stderr);
288   while (!done) {
289     errno = 0;
290     long v = ptrace(PTRACE_PEEKDATA, TT.pid, addr + offset);
291     if (errno) return;
292     memcpy(toybuf, &v, sizeof(v));
293     for (i=0; i<sizeof(v); ++i) {
294       if (!toybuf[i]) {
295         // TODO: handle the case of dumping n bytes (e.g. read()/write()), not
296         // just NUL-terminated strings.
297         done = 1;
298         break;
299       }
300       if (isprint(toybuf[i])) fputc(toybuf[i], stderr);
301       else {
302         // TODO: reuse an existing escape function.
303         fputc('\\', stderr);
304         if (toybuf[i] == '\n') fputc('n', stderr);
305         else if (toybuf[i] == '\r') fputc('r', stderr);
306         else if (toybuf[i] == '\t') fputc('t', stderr);
307         else fprintf(stderr, "x%2.2x", toybuf[i]);
308       }
309       if (++total >= TT.s) {
310         done = 1;
311         break;
312       }
313     }
314     offset += sizeof(v);
315   }
316   fputc('"', stderr);
317 }
318 
319 static void print_bitmask(int bitmask, long v, char *zero, ...)
320 {
321   va_list ap;
322   int first = 1;
323 
324   if (!v && zero) {
325     fprintf(stderr, "%s", zero);
326     return;
327   }
328 
329   va_start(ap, zero);
330   for (;;) {
331     int this = va_arg(ap, int);
332     char *name;
333 
334     if (!this) break;
335     name = va_arg(ap, char*);
336     if (bitmask) {
337       if (v & this) {
338         fprintf(stderr, "%s%s", first?"":"|", name);
339         first = 0;
340         v &= ~this;
341       }
342     } else {
343       if (v == this) {
344         fprintf(stderr, "%s", name);
345         v = 0;
346         break;
347       }
348     }
349   }
350   va_end(ap);
351   if (v) fprintf(stderr, "%s%#lx", first?"":"|", v);
352 }
353 
354 static void print_flags(long v)
355 {
356 #define C(n) n, #n
357   if (strstart(&TT.fmt, "access|")) {
358     print_bitmask(1, v, "F_OK", C(R_OK), C(W_OK), C(X_OK), 0);
359   } else if (strstart(&TT.fmt, "mmap|")) {
360     print_bitmask(1, v, 0, C(MAP_SHARED), C(MAP_PRIVATE), C(MAP_32BIT),
361         C(MAP_ANONYMOUS), C(MAP_FIXED), C(MAP_GROWSDOWN), C(MAP_HUGETLB),
362         C(MAP_DENYWRITE), 0);
363   } else if (strstart(&TT.fmt, "open|")) {
364     print_bitmask(1, v, "O_RDONLY", C(O_WRONLY), C(O_RDWR), C(O_CLOEXEC),
365         C(O_CREAT), C(O_DIRECTORY), C(O_EXCL), C(O_NOCTTY), C(O_NOFOLLOW),
366         C(O_TRUNC), C(O_ASYNC), C(O_APPEND), C(O_DSYNC), C(O_EXCL),
367         C(O_NOATIME), C(O_NONBLOCK), C(O_PATH), C(O_SYNC),
368         0x4000, "O_DIRECT", 0x8000, "O_LARGEFILE", 0x410000, "O_TMPFILE", 0);
369   } else if (strstart(&TT.fmt, "prot|")) {
370     print_bitmask(1,v,"PROT_NONE",C(PROT_READ),C(PROT_WRITE),C(PROT_EXEC),0);
371   } else abort();
372 }
373 
374 static void print_alternatives(long v)
375 {
376   if (strstart(&TT.fmt, "rlimit^")) {
377     print_bitmask(0, v, "RLIMIT_CPU", C(RLIMIT_FSIZE), C(RLIMIT_DATA),
378         C(RLIMIT_STACK), C(RLIMIT_CORE), C(RLIMIT_RSS), C(RLIMIT_NPROC),
379         C(RLIMIT_NOFILE), C(RLIMIT_MEMLOCK), C(RLIMIT_AS), C(RLIMIT_LOCKS),
380         C(RLIMIT_SIGPENDING), C(RLIMIT_MSGQUEUE), C(RLIMIT_NICE),
381         C(RLIMIT_RTPRIO), C(RLIMIT_RTTIME), 0);
382   } else if (strstart(&TT.fmt, "seek^")) {
383     print_bitmask(0, v, "SEEK_SET", C(SEEK_CUR), C(SEEK_END), C(SEEK_DATA),
384         C(SEEK_HOLE), 0);
385   } else if (strstart(&TT.fmt, "sig^")) {
386     print_bitmask(0, v, "SIG_BLOCK", C(SIG_UNBLOCK), C(SIG_SETMASK), 0);
387   } else abort();
388 }
389 
390 static void print_args()
391 {
392   int i;
393 
394   // Loop through arguments and print according to format string
395   for (i = 0; *TT.fmt; i++, TT.arg++) {
396     long v = TT.regs[REG_ORDER[TT.arg]];
397     char *s, ch;
398 
399     if (i) fprintf(stderr, ", ");
400     switch (ch = *TT.fmt++) {
401       case 'd': fprintf(stderr, "%ld", v); break; // decimal
402       case 'f': if ((int) v == AT_FDCWD) fprintf(stderr, "AT_FDCWD");
403                 else fprintf(stderr, "%ld", v);
404                 break;
405       case 'i': fprintf(stderr, "%s", strioctl(v)); break; // decimal
406       case 'm': fprintf(stderr, "%03o", (unsigned) v); break; // mode for open()
407       case 'o': fprintf(stderr, "%ld", v); break; // off_t
408       case 'p': print_ptr(v); break;
409       case 's': print_string(v); break;
410       case 'S': // The libc-reserved signals aren't known to num_to_sig().
411                 // TODO: use an strace-only routine for >= 32?
412                 if (!(s = num_to_sig(v))) fprintf(stderr, "%ld", v);
413                 else fprintf(stderr, "SIG%s", s);
414                 break;
415       case 'z': fprintf(stderr, "%zd", v); break; // size_t
416       case 'x': fprintf(stderr, "%lx", v); break; // hex
417 
418       case '{': print_struct(v); break;
419       case '|': print_flags(v); break;
420       case '^': print_alternatives(v); break;
421 
422       case '/': return; // Separates "enter" and "exit" arguments.
423 
424       default: fprintf(stderr, "?%c<0x%lx>", ch, v); break;
425     }
426   }
427 }
428 
429 static void print_enter(void)
430 {
431   char *name;
432 
433   get_regs();
434   TT.syscall = TT.regs[REG_ORDER[6]];
435   if (TT.syscall == __NR_ioctl) {
436     name = "ioctl";
437     switch (TT.regs[REG_ORDER[1]]) {
438       case FS_IOC_FSGETXATTR: TT.fmt = "fi/{fsxattr}"; break;
439       case FS_IOC_FSSETXATTR: TT.fmt = "fi{fsxattr}"; break;
440       case FS_IOC_GETFLAGS: TT.fmt = "fi/{longx}"; break;
441       case FS_IOC_GETVERSION: TT.fmt = "fi/{long}"; break;
442       case FS_IOC_SETFLAGS: TT.fmt = "fi{long}"; break;
443       case FS_IOC_SETVERSION: TT.fmt = "fi{long}"; break;
444       //case SIOCGIFCONF: struct ifconf
445       case SIOCGIFADDR:
446       case SIOCGIFBRDADDR:
447       case SIOCGIFDSTADDR:
448       case SIOCGIFFLAGS:
449       case SIOCGIFHWADDR:
450       case SIOCGIFMAP:
451       case SIOCGIFMTU:
452       case SIOCGIFNETMASK:
453       case SIOCGIFTXQLEN: TT.fmt = "fi/{ifreq}"; break;
454       case SIOCSIFADDR:
455       case SIOCSIFBRDADDR:
456       case SIOCSIFDSTADDR:
457       case SIOCSIFFLAGS:
458       case SIOCSIFHWADDR:
459       case SIOCSIFMAP:
460       case SIOCSIFMTU:
461       case SIOCSIFNETMASK:
462       case SIOCSIFTXQLEN: TT.fmt = "fi{ifreq}"; break;
463       case TCGETS: TT.fmt = "fi/{termios}"; break;
464       case TCSETS: TT.fmt = "fi{termios}"; break;
465       case TIOCGWINSZ: TT.fmt = "fi/{winsize}"; break;
466       case TIOCSWINSZ: TT.fmt = "fi{winsize}"; break;
467       default:
468         TT.fmt = (TT.regs[REG_ORDER[0]]&1) ? "fip" : "fi/p";
469         break;
470     }
471   } else switch (TT.syscall) {
472 #define SC(n,f) case __NR_ ## n: name = #n; TT.fmt = f; break
473     SC(access, "s|access|");
474     SC(arch_prctl, "dp");
475     SC(brk, "p");
476     SC(close, "d");
477     SC(connect, "fpd"); // TODO: sockaddr
478     SC(dup, "f");
479     SC(dup2, "ff");
480     SC(dup3, "ff|open|");
481     SC(execve, "spp");
482     SC(exit_group, "d");
483     SC(fcntl, "fdp"); // TODO: probably needs special case
484     SC(fstat, "f/{stat}");
485     SC(futex, "pdxppx");
486     SC(getdents64, "dpz");
487     SC(geteuid, "");
488     SC(getuid, "");
489 
490     SC(getxattr, "sspz");
491     SC(lgetxattr, "sspz");
492     SC(fgetxattr, "fspz");
493 
494     SC(lseek, "fo^seek^");
495     SC(lstat, "s/{stat}");
496     SC(mmap, "pz|prot||mmap|fx");
497     SC(mprotect, "pz|prot|");
498     SC(mremap, "pzzdp"); // TODO: flags
499     SC(munmap, "pz");
500     SC(nanosleep, "{timespec}/{timespec}");
501     SC(newfstatat, "fs/{stat}d");
502     SC(open, "sd|open|m");
503     SC(openat, "fs|open|m");
504     SC(poll, "pdd");
505     SC(prlimit64, "d^rlimit^{rlimit}/{rlimit}");
506     SC(read, "d/sz");
507     SC(readlinkat, "s/sz");
508     SC(rt_sigaction, "Sppz");
509     SC(rt_sigprocmask, "^sig^{sigset}/{sigset}z");
510     SC(set_robust_list, "pd");
511     SC(set_tid_address, "p");
512     SC(socket, "ddd"); // TODO: flags
513     SC(stat, "s/{stat}");
514     SC(statfs, "sp");
515     SC(sysinfo, "p");
516     SC(umask, "m");
517     SC(uname, "p");
518     SC(write, "dsz");
519     default:
520       sprintf(name = toybuf, "SYS_%ld", TT.syscall);
521       TT.fmt = "pppppp";
522       break;
523   }
524 
525   fprintf(stderr, "%s(", name);
526   TT.arg = 0;
527   print_args();
528 }
529 
530 static void print_exit(void)
531 {
532   long result;
533 
534   get_regs();
535   result = TT.regs[REG_ORDER[7]];
536   if (*TT.fmt) print_args();
537   fprintf(stderr, ") = ");
538   if (result >= -4095UL)
539     fprintf(stderr, "-1 %s (%s)", strerrno(-result), strerror(-result));
540   else if (TT.syscall==__NR_mmap || TT.syscall==__NR_brk) print_ptr(result);
541   else fprintf(stderr, "%ld", result);
542   fputc('\n', stderr);
543 }
544 
545 static int next(void)
546 {
547   int status;
548 
549   for (;;) {
550     ptrace(PTRACE_SYSCALL, TT.pid, 0, 0);
551     waitpid(TT.pid, &status, 0);
552     // PTRACE_O_TRACESYSGOOD sets bit 7 to indicate a syscall.
553     if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80) return 1;
554     if (WIFEXITED(status)) return 0;
555     fprintf(stderr, "[stopped %d (%x)]\n", status, WSTOPSIG(status));
556   }
557 }
558 
559 static void strace_detach(int s)
560 {
561   xptrace(PTRACE_DETACH, TT.pid, 0, 0);
562   exit(1);
563 }
564 
565 void strace_main(void)
566 {
567   int status;
568 
569   if (!FLAG(s)) TT.s = 32;
570 
571   if (FLAG(p)) {
572     if (*toys.optargs) help_exit("No arguments with -p");
573     TT.pid = TT.p;
574     signal(SIGINT, strace_detach);
575     // TODO: PTRACE_SEIZE instead?
576     xptrace(PTRACE_ATTACH, TT.pid, 0, 0);
577   } else {
578     if (!*toys.optargs) help_exit("Needs 1 argument");
579     TT.pid = xfork();
580     if (!TT.pid) {
581       errno = 0;
582       ptrace(PTRACE_TRACEME);
583       if (errno) perror_exit("PTRACE_TRACEME failed");
584       raise(SIGSTOP);
585       toys.stacktop = 0;
586       xexec(toys.optargs);
587     }
588   }
589 
590   do {
591     waitpid(TT.pid, &status, 0);
592   } while (!WIFSTOPPED(status));
593 
594   // TODO: PTRACE_O_TRACEEXIT
595   // TODO: PTRACE_O_TRACEFORK/PTRACE_O_TRACEVFORK/PTRACE_O_TRACECLONE for -f.
596   errno = 0;
597   ptrace(PTRACE_SETOPTIONS, TT.pid, 0, PTRACE_O_TRACESYSGOOD);
598   if (errno) perror_exit("PTRACE_SETOPTIONS PTRACE_O_TRACESYSGOOD failed");
599 
600   // TODO: real strace swallows the failed execve()s if it started the child
601 
602   for (;;) {
603     if (!next()) break;
604     print_enter();
605     if (!next()) break;
606     print_exit();
607   }
608 
609   // TODO: support -f and keep track of children.
610   waitpid(TT.pid, &status, 0);
611   if (WIFEXITED(status))
612     fprintf(stderr, "+++ exited with %d +++\n", WEXITSTATUS(status));
613   if (WIFSTOPPED(status))
614     fprintf(stderr, "+++ stopped with %d +++\n", WSTOPSIG(status));
615 }
616