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