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