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
strioctl(int i)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)?
strerrno(int e)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
xptrace(int req, pid_t pid, void *addr, void *data)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
get_regsnull171 static void get_regs()
172 {
173 xptrace(PTRACE_GETREGS, TT.pid, 0, TT.regs);
174 }
175
ptrace_struct(long addr, void *dst, size_t bytes)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...
print_struct(long addr)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
print_ptr(long addr)276 static void print_ptr(long addr)
277 {
278 if (!addr) fprintf(stderr, "NULL");
279 else fprintf(stderr, "0x%lx", addr);
280 }
281
print_string(long addr)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
print_bitmask(int bitmask, long v, char *zero, ...)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
print_flags(long v)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
print_alternatives(long v)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
print_argsnull390 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
print_enter(void)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
print_exit(void)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
next(void)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
strace_detach(int s)559 static void strace_detach(int s)
560 {
561 xptrace(PTRACE_DETACH, TT.pid, 0, 0);
562 exit(1);
563 }
564
strace_main(void)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