xref: /third_party/toybox/toys/pending/strace.c (revision 0f66f451)
10f66f451Sopenharmony_ci/* strace.c - Trace system calls.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2020 The Android Open Source Project
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * See https://man7.org/linux/man-pages/man2/syscall.2.html
60f66f451Sopenharmony_ci
70f66f451Sopenharmony_ciUSE_STRACE(NEWTOY(strace, "^p#s#v", TOYFLAG_USR|TOYFLAG_SBIN))
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciconfig STRACE
100f66f451Sopenharmony_ci  bool "strace"
110f66f451Sopenharmony_ci  default n
120f66f451Sopenharmony_ci  help
130f66f451Sopenharmony_ci    usage: strace [-fv] [-p PID] [-s NUM] COMMAND [ARGS...]
140f66f451Sopenharmony_ci
150f66f451Sopenharmony_ci    Trace systems calls made by a process.
160f66f451Sopenharmony_ci
170f66f451Sopenharmony_ci    -s	String length limit.
180f66f451Sopenharmony_ci    -v	Dump all of large structs/arrays.
190f66f451Sopenharmony_ci*/
200f66f451Sopenharmony_ci
210f66f451Sopenharmony_ci#include <sys/ptrace.h>
220f66f451Sopenharmony_ci#include <sys/syscall.h>
230f66f451Sopenharmony_ci#include <sys/user.h>
240f66f451Sopenharmony_ci
250f66f451Sopenharmony_ci#define FOR_strace
260f66f451Sopenharmony_ci#include "toys.h"
270f66f451Sopenharmony_ci
280f66f451Sopenharmony_ciGLOBALS(
290f66f451Sopenharmony_ci  long s, p;
300f66f451Sopenharmony_ci
310f66f451Sopenharmony_ci  char ioctl[32], *fmt;
320f66f451Sopenharmony_ci  long regs[256/sizeof(long)], syscall;
330f66f451Sopenharmony_ci  pid_t pid;
340f66f451Sopenharmony_ci  int arg;
350f66f451Sopenharmony_ci)
360f66f451Sopenharmony_ci
370f66f451Sopenharmony_ci  struct user_regs_struct regs;
380f66f451Sopenharmony_ci
390f66f451Sopenharmony_ci
400f66f451Sopenharmony_ci// Syscall args from https://man7.org/linux/man-pages/man2/syscall.2.html
410f66f451Sopenharmony_ci// REG_ORDER is args 0-6, SYSCALL, RESULT
420f66f451Sopenharmony_ci#if defined(__ARM_EABI__)
430f66f451Sopenharmony_cistatic const char REG_ORDER[] = {0,1,2,3,4,5,7,0};
440f66f451Sopenharmony_ci#elif defined(__ARM_ARCH) && __ARM_ARCH == 8
450f66f451Sopenharmony_cistatic const char REG_ORDER[] = {0,1,2,3,4,5,8,0};
460f66f451Sopenharmony_ci#elif defined(__i386__)
470f66f451Sopenharmony_ci// ebx,ecx,edx,esi,edi,ebp,orig_eax,eax
480f66f451Sopenharmony_cistatic const char REG_ORDER[] = {0,1,2,3,4,5,11,6};
490f66f451Sopenharmony_ci#elif defined(__m68k__)
500f66f451Sopenharmony_ci// d1,d2,d3,d4,d5,a0,orig_d0,d0
510f66f451Sopenharmony_cistatic const char REG_ORDER[] = {0,1,2,3,4,7,16,14);
520f66f451Sopenharmony_ci#elif defined(__PPC__) || defined(__PPC64__)
530f66f451Sopenharmony_cistatic const char REG_ORDER[] = {3,4,5,6,7,8,0,3};
540f66f451Sopenharmony_ci#elif defined(__s390__) // also covers s390x
550f66f451Sopenharmony_ci// r2,r3,r4,r5,r6,r7,r1,r2 but mask+addr before r0 so +2
560f66f451Sopenharmony_cistatic const char REG_ORDER[] = {4,5,6,7,8,9,3,4};
570f66f451Sopenharmony_ci#elif defined(__sh__)
580f66f451Sopenharmony_cistatic const char REG_ORDER[] = {4,5,6,7,0,1,3,0};
590f66f451Sopenharmony_ci#elif defined(__x86_64__)
600f66f451Sopenharmony_ci// rdi,rsi,rdx,r10,r8,r9,orig_rax,rax
610f66f451Sopenharmony_cistatic const char REG_ORDER[] = {14,13,12,7,9,8,15,10};
620f66f451Sopenharmony_ci#else
630f66f451Sopenharmony_ci#error unsupported architecture
640f66f451Sopenharmony_ci#endif
650f66f451Sopenharmony_ci
660f66f451Sopenharmony_ci#define C(x) case x: return #x
670f66f451Sopenharmony_ci
680f66f451Sopenharmony_ci#define FS_IOC_FSGETXATTR 0x801c581f
690f66f451Sopenharmony_ci#define FS_IOC_FSSETXATTR 0x401c5820
700f66f451Sopenharmony_ci#define FS_IOC_GETFLAGS 0x80086601
710f66f451Sopenharmony_ci#define FS_IOC_SETFLAGS 0x40086602
720f66f451Sopenharmony_ci#define FS_IOC_GETVERSION 0x80087601
730f66f451Sopenharmony_ci#define FS_IOC_SETVERSION 0x40047602
740f66f451Sopenharmony_cistruct fsxattr {
750f66f451Sopenharmony_ci  unsigned fsx_xflags;
760f66f451Sopenharmony_ci  unsigned fsx_extsize;
770f66f451Sopenharmony_ci  unsigned fsx_nextents;
780f66f451Sopenharmony_ci  unsigned fsx_projid;
790f66f451Sopenharmony_ci  unsigned fsx_cowextsize;
800f66f451Sopenharmony_ci  char fsx_pad[8];
810f66f451Sopenharmony_ci};
820f66f451Sopenharmony_ci
830f66f451Sopenharmony_cistatic char *strioctl(int i)
840f66f451Sopenharmony_ci{
850f66f451Sopenharmony_ci  switch (i) {
860f66f451Sopenharmony_ci    C(FS_IOC_FSGETXATTR);
870f66f451Sopenharmony_ci    C(FS_IOC_FSSETXATTR);
880f66f451Sopenharmony_ci    C(FS_IOC_GETFLAGS);
890f66f451Sopenharmony_ci    C(FS_IOC_GETVERSION);
900f66f451Sopenharmony_ci    C(FS_IOC_SETFLAGS);
910f66f451Sopenharmony_ci    C(FS_IOC_SETVERSION);
920f66f451Sopenharmony_ci    C(SIOCGIFADDR);
930f66f451Sopenharmony_ci    C(SIOCGIFBRDADDR);
940f66f451Sopenharmony_ci    C(SIOCGIFCONF);
950f66f451Sopenharmony_ci    C(SIOCGIFDSTADDR);
960f66f451Sopenharmony_ci    C(SIOCGIFFLAGS);
970f66f451Sopenharmony_ci    C(SIOCGIFHWADDR);
980f66f451Sopenharmony_ci    C(SIOCGIFMAP);
990f66f451Sopenharmony_ci    C(SIOCGIFMTU);
1000f66f451Sopenharmony_ci    C(SIOCGIFNETMASK);
1010f66f451Sopenharmony_ci    C(SIOCGIFTXQLEN);
1020f66f451Sopenharmony_ci    C(TCGETS);
1030f66f451Sopenharmony_ci    C(TCSETS);
1040f66f451Sopenharmony_ci    C(TIOCGWINSZ);
1050f66f451Sopenharmony_ci    C(TIOCSWINSZ);
1060f66f451Sopenharmony_ci  }
1070f66f451Sopenharmony_ci  sprintf(toybuf, "%#x", i);
1080f66f451Sopenharmony_ci  return toybuf;
1090f66f451Sopenharmony_ci}
1100f66f451Sopenharmony_ci
1110f66f451Sopenharmony_ci// TODO: move to lib, implement errno(1)?
1120f66f451Sopenharmony_cistatic char *strerrno(int e)
1130f66f451Sopenharmony_ci{
1140f66f451Sopenharmony_ci  switch (e) {
1150f66f451Sopenharmony_ci    // uapi errno-base.h
1160f66f451Sopenharmony_ci    C(EPERM);
1170f66f451Sopenharmony_ci    C(ENOENT);
1180f66f451Sopenharmony_ci    C(ESRCH);
1190f66f451Sopenharmony_ci    C(EINTR);
1200f66f451Sopenharmony_ci    C(EIO);
1210f66f451Sopenharmony_ci    C(ENXIO);
1220f66f451Sopenharmony_ci    C(E2BIG);
1230f66f451Sopenharmony_ci    C(ENOEXEC);
1240f66f451Sopenharmony_ci    C(EBADF);
1250f66f451Sopenharmony_ci    C(ECHILD);
1260f66f451Sopenharmony_ci    C(EAGAIN);
1270f66f451Sopenharmony_ci    C(ENOMEM);
1280f66f451Sopenharmony_ci    C(EACCES);
1290f66f451Sopenharmony_ci    C(EFAULT);
1300f66f451Sopenharmony_ci    C(ENOTBLK);
1310f66f451Sopenharmony_ci    C(EBUSY);
1320f66f451Sopenharmony_ci    C(EEXIST);
1330f66f451Sopenharmony_ci    C(EXDEV);
1340f66f451Sopenharmony_ci    C(ENODEV);
1350f66f451Sopenharmony_ci    C(ENOTDIR);
1360f66f451Sopenharmony_ci    C(EISDIR);
1370f66f451Sopenharmony_ci    C(EINVAL);
1380f66f451Sopenharmony_ci    C(ENFILE);
1390f66f451Sopenharmony_ci    C(EMFILE);
1400f66f451Sopenharmony_ci    C(ENOTTY);
1410f66f451Sopenharmony_ci    C(ETXTBSY);
1420f66f451Sopenharmony_ci    C(EFBIG);
1430f66f451Sopenharmony_ci    C(ENOSPC);
1440f66f451Sopenharmony_ci    C(ESPIPE);
1450f66f451Sopenharmony_ci    C(EROFS);
1460f66f451Sopenharmony_ci    C(EMLINK);
1470f66f451Sopenharmony_ci    C(EPIPE);
1480f66f451Sopenharmony_ci    C(EDOM);
1490f66f451Sopenharmony_ci    C(ERANGE);
1500f66f451Sopenharmony_ci    // uapi errno.h
1510f66f451Sopenharmony_ci    C(EDEADLK);
1520f66f451Sopenharmony_ci    C(ENAMETOOLONG);
1530f66f451Sopenharmony_ci    C(ENOLCK);
1540f66f451Sopenharmony_ci    C(ENOSYS);
1550f66f451Sopenharmony_ci    C(ENOTEMPTY);
1560f66f451Sopenharmony_ci    C(ELOOP);
1570f66f451Sopenharmony_ci    C(ENOMSG);
1580f66f451Sopenharmony_ci    // ...etc; fill in as we see them in practice?
1590f66f451Sopenharmony_ci  }
1600f66f451Sopenharmony_ci  sprintf(toybuf, "%d", e);
1610f66f451Sopenharmony_ci  return toybuf;
1620f66f451Sopenharmony_ci}
1630f66f451Sopenharmony_ci
1640f66f451Sopenharmony_ci#undef C
1650f66f451Sopenharmony_ci
1660f66f451Sopenharmony_cistatic void xptrace(int req, pid_t pid, void *addr, void *data)
1670f66f451Sopenharmony_ci{
1680f66f451Sopenharmony_ci  if (ptrace(req, pid, addr, data)) perror_exit("ptrace pid %d", pid);
1690f66f451Sopenharmony_ci}
1700f66f451Sopenharmony_ci
1710f66f451Sopenharmony_cistatic void get_regs()
1720f66f451Sopenharmony_ci{
1730f66f451Sopenharmony_ci  xptrace(PTRACE_GETREGS, TT.pid, 0, TT.regs);
1740f66f451Sopenharmony_ci}
1750f66f451Sopenharmony_ci
1760f66f451Sopenharmony_cistatic void ptrace_struct(long addr, void *dst, size_t bytes)
1770f66f451Sopenharmony_ci{
1780f66f451Sopenharmony_ci  int offset = 0, i;
1790f66f451Sopenharmony_ci  long v;
1800f66f451Sopenharmony_ci
1810f66f451Sopenharmony_ci  for (i=0; i<bytes; i+=sizeof(long)) {
1820f66f451Sopenharmony_ci    errno = 0;
1830f66f451Sopenharmony_ci    v = ptrace(PTRACE_PEEKDATA, TT.pid, addr + offset);
1840f66f451Sopenharmony_ci    if (errno) perror_exit("PTRACE_PEEKDATA failed");
1850f66f451Sopenharmony_ci    memcpy(dst + offset, &v, sizeof(v));
1860f66f451Sopenharmony_ci    offset += sizeof(long);
1870f66f451Sopenharmony_ci  }
1880f66f451Sopenharmony_ci}
1890f66f451Sopenharmony_ci
1900f66f451Sopenharmony_ci// TODO: this all relies on having the libc structs match the kernel structs,
1910f66f451Sopenharmony_ci// which isn't always true for glibc...
1920f66f451Sopenharmony_cistatic void print_struct(long addr)
1930f66f451Sopenharmony_ci{
1940f66f451Sopenharmony_ci  if (!addr) { // All NULLs look the same...
1950f66f451Sopenharmony_ci    fprintf(stderr, "NULL");
1960f66f451Sopenharmony_ci    while (*TT.fmt != '}') ++TT.fmt;
1970f66f451Sopenharmony_ci    ++TT.fmt;
1980f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "ifreq}")) {
1990f66f451Sopenharmony_ci    struct ifreq ir;
2000f66f451Sopenharmony_ci
2010f66f451Sopenharmony_ci    ptrace_struct(addr, &ir, sizeof(ir));
2020f66f451Sopenharmony_ci    // TODO: is this always an ioctl? use TT.regs[REG_ORDER[1]] to work out what to show.
2030f66f451Sopenharmony_ci    fprintf(stderr, "{...}");
2040f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "fsxattr}")) {
2050f66f451Sopenharmony_ci    struct fsxattr fx;
2060f66f451Sopenharmony_ci
2070f66f451Sopenharmony_ci    ptrace_struct(addr, &fx, sizeof(fx));
2080f66f451Sopenharmony_ci    fprintf(stderr, "{fsx_xflags=%#x, fsx_extsize=%d, fsx_nextents=%d, "
2090f66f451Sopenharmony_ci        "fsx_projid=%d, fsx_cowextsize=%d}", fx.fsx_xflags, fx.fsx_extsize,
2100f66f451Sopenharmony_ci        fx.fsx_nextents, fx.fsx_projid, fx.fsx_cowextsize);
2110f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "long}")) {
2120f66f451Sopenharmony_ci    long l;
2130f66f451Sopenharmony_ci
2140f66f451Sopenharmony_ci    ptrace_struct(addr, &l, sizeof(l));
2150f66f451Sopenharmony_ci    fprintf(stderr, "%ld", l);
2160f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "longx}")) {
2170f66f451Sopenharmony_ci    long l;
2180f66f451Sopenharmony_ci
2190f66f451Sopenharmony_ci    ptrace_struct(addr, &l, sizeof(l));
2200f66f451Sopenharmony_ci    fprintf(stderr, "%#lx", l);
2210f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "rlimit}")) {
2220f66f451Sopenharmony_ci    struct rlimit rl;
2230f66f451Sopenharmony_ci
2240f66f451Sopenharmony_ci    ptrace_struct(addr, &rl, sizeof(rl));
2250f66f451Sopenharmony_ci    fprintf(stderr, "{rlim_cur=%lld, rlim_max=%lld}",
2260f66f451Sopenharmony_ci        (long long)rl.rlim_cur, (long long)rl.rlim_max);
2270f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "sigset}")) {
2280f66f451Sopenharmony_ci    long long ss;
2290f66f451Sopenharmony_ci    int i;
2300f66f451Sopenharmony_ci
2310f66f451Sopenharmony_ci    ptrace_struct(addr, &ss, sizeof(ss));
2320f66f451Sopenharmony_ci    fprintf(stderr, "[");
2330f66f451Sopenharmony_ci    for (i=0; i<64;++i) {
2340f66f451Sopenharmony_ci      // TODO: use signal names, fix spacing
2350f66f451Sopenharmony_ci      if (ss & (1ULL<<i)) fprintf(stderr, "%d ", i);
2360f66f451Sopenharmony_ci    }
2370f66f451Sopenharmony_ci    fprintf(stderr, "]");
2380f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "stat}")) {
2390f66f451Sopenharmony_ci    struct stat sb;
2400f66f451Sopenharmony_ci
2410f66f451Sopenharmony_ci    ptrace_struct(addr, &sb, sizeof(sb));
2420f66f451Sopenharmony_ci    // TODO: decode IFMT bits in st_mode
2430f66f451Sopenharmony_ci    if (FLAG(v)) {
2440f66f451Sopenharmony_ci      // TODO: full atime/mtime/ctime dump.
2450f66f451Sopenharmony_ci      fprintf(stderr, "{st_dev=makedev(%#x, %#x), st_ino=%ld, st_mode=%o, "
2460f66f451Sopenharmony_ci          "st_nlink=%ld, st_uid=%d, st_gid=%d, st_blksize=%ld, st_blocks=%ld, "
2470f66f451Sopenharmony_ci          "st_size=%lld, st_atime=%ld, st_mtime=%ld, st_ctime=%ld}",
2480f66f451Sopenharmony_ci          dev_major(sb.st_dev), dev_minor(sb.st_dev), sb.st_ino, sb.st_mode,
2490f66f451Sopenharmony_ci          sb.st_nlink, sb.st_uid, sb.st_gid, sb.st_blksize, sb.st_blocks,
2500f66f451Sopenharmony_ci          (long long)sb.st_size, sb.st_atime, sb.st_mtime, sb.st_ctime);
2510f66f451Sopenharmony_ci    } else {
2520f66f451Sopenharmony_ci      fprintf(stderr, "{st_mode=%o, st_size=%lld, ...}", sb.st_mode,
2530f66f451Sopenharmony_ci        (long long)sb.st_size);
2540f66f451Sopenharmony_ci    }
2550f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "termios}")) {
2560f66f451Sopenharmony_ci    struct termios to;
2570f66f451Sopenharmony_ci
2580f66f451Sopenharmony_ci    ptrace_struct(addr, &to, sizeof(to));
2590f66f451Sopenharmony_ci    fprintf(stderr, "{c_iflag=%#lx, c_oflag=%#lx, c_cflag=%#lx, c_lflag=%#lx}",
2600f66f451Sopenharmony_ci        (long)to.c_iflag, (long)to.c_oflag, (long)to.c_cflag, (long)to.c_lflag);
2610f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "timespec}")) {
2620f66f451Sopenharmony_ci    struct timespec ts;
2630f66f451Sopenharmony_ci
2640f66f451Sopenharmony_ci    ptrace_struct(addr, &ts, sizeof(ts));
2650f66f451Sopenharmony_ci    fprintf(stderr, "{tv_sec=%lld, tv_nsec=%lld}",
2660f66f451Sopenharmony_ci        (long long)ts.tv_sec, (long long)ts.tv_nsec);
2670f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "winsize}")) {
2680f66f451Sopenharmony_ci    struct winsize ws;
2690f66f451Sopenharmony_ci
2700f66f451Sopenharmony_ci    ptrace_struct(addr, &ws, sizeof(ws));
2710f66f451Sopenharmony_ci    fprintf(stderr, "{ws_row=%hu, ws_col=%hu, ws_xpixel=%hu, ws_ypixel=%hu}",
2720f66f451Sopenharmony_ci        ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
2730f66f451Sopenharmony_ci  } else abort();
2740f66f451Sopenharmony_ci}
2750f66f451Sopenharmony_ci
2760f66f451Sopenharmony_cistatic void print_ptr(long addr)
2770f66f451Sopenharmony_ci{
2780f66f451Sopenharmony_ci  if (!addr) fprintf(stderr, "NULL");
2790f66f451Sopenharmony_ci  else fprintf(stderr, "0x%lx", addr);
2800f66f451Sopenharmony_ci}
2810f66f451Sopenharmony_ci
2820f66f451Sopenharmony_cistatic void print_string(long addr)
2830f66f451Sopenharmony_ci{
2840f66f451Sopenharmony_ci  long offset = 0, total = 0;
2850f66f451Sopenharmony_ci  int done = 0, i;
2860f66f451Sopenharmony_ci
2870f66f451Sopenharmony_ci  fputc('"', stderr);
2880f66f451Sopenharmony_ci  while (!done) {
2890f66f451Sopenharmony_ci    errno = 0;
2900f66f451Sopenharmony_ci    long v = ptrace(PTRACE_PEEKDATA, TT.pid, addr + offset);
2910f66f451Sopenharmony_ci    if (errno) return;
2920f66f451Sopenharmony_ci    memcpy(toybuf, &v, sizeof(v));
2930f66f451Sopenharmony_ci    for (i=0; i<sizeof(v); ++i) {
2940f66f451Sopenharmony_ci      if (!toybuf[i]) {
2950f66f451Sopenharmony_ci        // TODO: handle the case of dumping n bytes (e.g. read()/write()), not
2960f66f451Sopenharmony_ci        // just NUL-terminated strings.
2970f66f451Sopenharmony_ci        done = 1;
2980f66f451Sopenharmony_ci        break;
2990f66f451Sopenharmony_ci      }
3000f66f451Sopenharmony_ci      if (isprint(toybuf[i])) fputc(toybuf[i], stderr);
3010f66f451Sopenharmony_ci      else {
3020f66f451Sopenharmony_ci        // TODO: reuse an existing escape function.
3030f66f451Sopenharmony_ci        fputc('\\', stderr);
3040f66f451Sopenharmony_ci        if (toybuf[i] == '\n') fputc('n', stderr);
3050f66f451Sopenharmony_ci        else if (toybuf[i] == '\r') fputc('r', stderr);
3060f66f451Sopenharmony_ci        else if (toybuf[i] == '\t') fputc('t', stderr);
3070f66f451Sopenharmony_ci        else fprintf(stderr, "x%2.2x", toybuf[i]);
3080f66f451Sopenharmony_ci      }
3090f66f451Sopenharmony_ci      if (++total >= TT.s) {
3100f66f451Sopenharmony_ci        done = 1;
3110f66f451Sopenharmony_ci        break;
3120f66f451Sopenharmony_ci      }
3130f66f451Sopenharmony_ci    }
3140f66f451Sopenharmony_ci    offset += sizeof(v);
3150f66f451Sopenharmony_ci  }
3160f66f451Sopenharmony_ci  fputc('"', stderr);
3170f66f451Sopenharmony_ci}
3180f66f451Sopenharmony_ci
3190f66f451Sopenharmony_cistatic void print_bitmask(int bitmask, long v, char *zero, ...)
3200f66f451Sopenharmony_ci{
3210f66f451Sopenharmony_ci  va_list ap;
3220f66f451Sopenharmony_ci  int first = 1;
3230f66f451Sopenharmony_ci
3240f66f451Sopenharmony_ci  if (!v && zero) {
3250f66f451Sopenharmony_ci    fprintf(stderr, "%s", zero);
3260f66f451Sopenharmony_ci    return;
3270f66f451Sopenharmony_ci  }
3280f66f451Sopenharmony_ci
3290f66f451Sopenharmony_ci  va_start(ap, zero);
3300f66f451Sopenharmony_ci  for (;;) {
3310f66f451Sopenharmony_ci    int this = va_arg(ap, int);
3320f66f451Sopenharmony_ci    char *name;
3330f66f451Sopenharmony_ci
3340f66f451Sopenharmony_ci    if (!this) break;
3350f66f451Sopenharmony_ci    name = va_arg(ap, char*);
3360f66f451Sopenharmony_ci    if (bitmask) {
3370f66f451Sopenharmony_ci      if (v & this) {
3380f66f451Sopenharmony_ci        fprintf(stderr, "%s%s", first?"":"|", name);
3390f66f451Sopenharmony_ci        first = 0;
3400f66f451Sopenharmony_ci        v &= ~this;
3410f66f451Sopenharmony_ci      }
3420f66f451Sopenharmony_ci    } else {
3430f66f451Sopenharmony_ci      if (v == this) {
3440f66f451Sopenharmony_ci        fprintf(stderr, "%s", name);
3450f66f451Sopenharmony_ci        v = 0;
3460f66f451Sopenharmony_ci        break;
3470f66f451Sopenharmony_ci      }
3480f66f451Sopenharmony_ci    }
3490f66f451Sopenharmony_ci  }
3500f66f451Sopenharmony_ci  va_end(ap);
3510f66f451Sopenharmony_ci  if (v) fprintf(stderr, "%s%#lx", first?"":"|", v);
3520f66f451Sopenharmony_ci}
3530f66f451Sopenharmony_ci
3540f66f451Sopenharmony_cistatic void print_flags(long v)
3550f66f451Sopenharmony_ci{
3560f66f451Sopenharmony_ci#define C(n) n, #n
3570f66f451Sopenharmony_ci  if (strstart(&TT.fmt, "access|")) {
3580f66f451Sopenharmony_ci    print_bitmask(1, v, "F_OK", C(R_OK), C(W_OK), C(X_OK), 0);
3590f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "mmap|")) {
3600f66f451Sopenharmony_ci    print_bitmask(1, v, 0, C(MAP_SHARED), C(MAP_PRIVATE), C(MAP_32BIT),
3610f66f451Sopenharmony_ci        C(MAP_ANONYMOUS), C(MAP_FIXED), C(MAP_GROWSDOWN), C(MAP_HUGETLB),
3620f66f451Sopenharmony_ci        C(MAP_DENYWRITE), 0);
3630f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "open|")) {
3640f66f451Sopenharmony_ci    print_bitmask(1, v, "O_RDONLY", C(O_WRONLY), C(O_RDWR), C(O_CLOEXEC),
3650f66f451Sopenharmony_ci        C(O_CREAT), C(O_DIRECTORY), C(O_EXCL), C(O_NOCTTY), C(O_NOFOLLOW),
3660f66f451Sopenharmony_ci        C(O_TRUNC), C(O_ASYNC), C(O_APPEND), C(O_DSYNC), C(O_EXCL),
3670f66f451Sopenharmony_ci        C(O_NOATIME), C(O_NONBLOCK), C(O_PATH), C(O_SYNC),
3680f66f451Sopenharmony_ci        0x4000, "O_DIRECT", 0x8000, "O_LARGEFILE", 0x410000, "O_TMPFILE", 0);
3690f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "prot|")) {
3700f66f451Sopenharmony_ci    print_bitmask(1,v,"PROT_NONE",C(PROT_READ),C(PROT_WRITE),C(PROT_EXEC),0);
3710f66f451Sopenharmony_ci  } else abort();
3720f66f451Sopenharmony_ci}
3730f66f451Sopenharmony_ci
3740f66f451Sopenharmony_cistatic void print_alternatives(long v)
3750f66f451Sopenharmony_ci{
3760f66f451Sopenharmony_ci  if (strstart(&TT.fmt, "rlimit^")) {
3770f66f451Sopenharmony_ci    print_bitmask(0, v, "RLIMIT_CPU", C(RLIMIT_FSIZE), C(RLIMIT_DATA),
3780f66f451Sopenharmony_ci        C(RLIMIT_STACK), C(RLIMIT_CORE), C(RLIMIT_RSS), C(RLIMIT_NPROC),
3790f66f451Sopenharmony_ci        C(RLIMIT_NOFILE), C(RLIMIT_MEMLOCK), C(RLIMIT_AS), C(RLIMIT_LOCKS),
3800f66f451Sopenharmony_ci        C(RLIMIT_SIGPENDING), C(RLIMIT_MSGQUEUE), C(RLIMIT_NICE),
3810f66f451Sopenharmony_ci        C(RLIMIT_RTPRIO), C(RLIMIT_RTTIME), 0);
3820f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "seek^")) {
3830f66f451Sopenharmony_ci    print_bitmask(0, v, "SEEK_SET", C(SEEK_CUR), C(SEEK_END), C(SEEK_DATA),
3840f66f451Sopenharmony_ci        C(SEEK_HOLE), 0);
3850f66f451Sopenharmony_ci  } else if (strstart(&TT.fmt, "sig^")) {
3860f66f451Sopenharmony_ci    print_bitmask(0, v, "SIG_BLOCK", C(SIG_UNBLOCK), C(SIG_SETMASK), 0);
3870f66f451Sopenharmony_ci  } else abort();
3880f66f451Sopenharmony_ci}
3890f66f451Sopenharmony_ci
3900f66f451Sopenharmony_cistatic void print_args()
3910f66f451Sopenharmony_ci{
3920f66f451Sopenharmony_ci  int i;
3930f66f451Sopenharmony_ci
3940f66f451Sopenharmony_ci  // Loop through arguments and print according to format string
3950f66f451Sopenharmony_ci  for (i = 0; *TT.fmt; i++, TT.arg++) {
3960f66f451Sopenharmony_ci    long v = TT.regs[REG_ORDER[TT.arg]];
3970f66f451Sopenharmony_ci    char *s, ch;
3980f66f451Sopenharmony_ci
3990f66f451Sopenharmony_ci    if (i) fprintf(stderr, ", ");
4000f66f451Sopenharmony_ci    switch (ch = *TT.fmt++) {
4010f66f451Sopenharmony_ci      case 'd': fprintf(stderr, "%ld", v); break; // decimal
4020f66f451Sopenharmony_ci      case 'f': if ((int) v == AT_FDCWD) fprintf(stderr, "AT_FDCWD");
4030f66f451Sopenharmony_ci                else fprintf(stderr, "%ld", v);
4040f66f451Sopenharmony_ci                break;
4050f66f451Sopenharmony_ci      case 'i': fprintf(stderr, "%s", strioctl(v)); break; // decimal
4060f66f451Sopenharmony_ci      case 'm': fprintf(stderr, "%03o", (unsigned) v); break; // mode for open()
4070f66f451Sopenharmony_ci      case 'o': fprintf(stderr, "%ld", v); break; // off_t
4080f66f451Sopenharmony_ci      case 'p': print_ptr(v); break;
4090f66f451Sopenharmony_ci      case 's': print_string(v); break;
4100f66f451Sopenharmony_ci      case 'S': // The libc-reserved signals aren't known to num_to_sig().
4110f66f451Sopenharmony_ci                // TODO: use an strace-only routine for >= 32?
4120f66f451Sopenharmony_ci                if (!(s = num_to_sig(v))) fprintf(stderr, "%ld", v);
4130f66f451Sopenharmony_ci                else fprintf(stderr, "SIG%s", s);
4140f66f451Sopenharmony_ci                break;
4150f66f451Sopenharmony_ci      case 'z': fprintf(stderr, "%zd", v); break; // size_t
4160f66f451Sopenharmony_ci      case 'x': fprintf(stderr, "%lx", v); break; // hex
4170f66f451Sopenharmony_ci
4180f66f451Sopenharmony_ci      case '{': print_struct(v); break;
4190f66f451Sopenharmony_ci      case '|': print_flags(v); break;
4200f66f451Sopenharmony_ci      case '^': print_alternatives(v); break;
4210f66f451Sopenharmony_ci
4220f66f451Sopenharmony_ci      case '/': return; // Separates "enter" and "exit" arguments.
4230f66f451Sopenharmony_ci
4240f66f451Sopenharmony_ci      default: fprintf(stderr, "?%c<0x%lx>", ch, v); break;
4250f66f451Sopenharmony_ci    }
4260f66f451Sopenharmony_ci  }
4270f66f451Sopenharmony_ci}
4280f66f451Sopenharmony_ci
4290f66f451Sopenharmony_cistatic void print_enter(void)
4300f66f451Sopenharmony_ci{
4310f66f451Sopenharmony_ci  char *name;
4320f66f451Sopenharmony_ci
4330f66f451Sopenharmony_ci  get_regs();
4340f66f451Sopenharmony_ci  TT.syscall = TT.regs[REG_ORDER[6]];
4350f66f451Sopenharmony_ci  if (TT.syscall == __NR_ioctl) {
4360f66f451Sopenharmony_ci    name = "ioctl";
4370f66f451Sopenharmony_ci    switch (TT.regs[REG_ORDER[1]]) {
4380f66f451Sopenharmony_ci      case FS_IOC_FSGETXATTR: TT.fmt = "fi/{fsxattr}"; break;
4390f66f451Sopenharmony_ci      case FS_IOC_FSSETXATTR: TT.fmt = "fi{fsxattr}"; break;
4400f66f451Sopenharmony_ci      case FS_IOC_GETFLAGS: TT.fmt = "fi/{longx}"; break;
4410f66f451Sopenharmony_ci      case FS_IOC_GETVERSION: TT.fmt = "fi/{long}"; break;
4420f66f451Sopenharmony_ci      case FS_IOC_SETFLAGS: TT.fmt = "fi{long}"; break;
4430f66f451Sopenharmony_ci      case FS_IOC_SETVERSION: TT.fmt = "fi{long}"; break;
4440f66f451Sopenharmony_ci      //case SIOCGIFCONF: struct ifconf
4450f66f451Sopenharmony_ci      case SIOCGIFADDR:
4460f66f451Sopenharmony_ci      case SIOCGIFBRDADDR:
4470f66f451Sopenharmony_ci      case SIOCGIFDSTADDR:
4480f66f451Sopenharmony_ci      case SIOCGIFFLAGS:
4490f66f451Sopenharmony_ci      case SIOCGIFHWADDR:
4500f66f451Sopenharmony_ci      case SIOCGIFMAP:
4510f66f451Sopenharmony_ci      case SIOCGIFMTU:
4520f66f451Sopenharmony_ci      case SIOCGIFNETMASK:
4530f66f451Sopenharmony_ci      case SIOCGIFTXQLEN: TT.fmt = "fi/{ifreq}"; break;
4540f66f451Sopenharmony_ci      case SIOCSIFADDR:
4550f66f451Sopenharmony_ci      case SIOCSIFBRDADDR:
4560f66f451Sopenharmony_ci      case SIOCSIFDSTADDR:
4570f66f451Sopenharmony_ci      case SIOCSIFFLAGS:
4580f66f451Sopenharmony_ci      case SIOCSIFHWADDR:
4590f66f451Sopenharmony_ci      case SIOCSIFMAP:
4600f66f451Sopenharmony_ci      case SIOCSIFMTU:
4610f66f451Sopenharmony_ci      case SIOCSIFNETMASK:
4620f66f451Sopenharmony_ci      case SIOCSIFTXQLEN: TT.fmt = "fi{ifreq}"; break;
4630f66f451Sopenharmony_ci      case TCGETS: TT.fmt = "fi/{termios}"; break;
4640f66f451Sopenharmony_ci      case TCSETS: TT.fmt = "fi{termios}"; break;
4650f66f451Sopenharmony_ci      case TIOCGWINSZ: TT.fmt = "fi/{winsize}"; break;
4660f66f451Sopenharmony_ci      case TIOCSWINSZ: TT.fmt = "fi{winsize}"; break;
4670f66f451Sopenharmony_ci      default:
4680f66f451Sopenharmony_ci        TT.fmt = (TT.regs[REG_ORDER[0]]&1) ? "fip" : "fi/p";
4690f66f451Sopenharmony_ci        break;
4700f66f451Sopenharmony_ci    }
4710f66f451Sopenharmony_ci  } else switch (TT.syscall) {
4720f66f451Sopenharmony_ci#define SC(n,f) case __NR_ ## n: name = #n; TT.fmt = f; break
4730f66f451Sopenharmony_ci    SC(access, "s|access|");
4740f66f451Sopenharmony_ci    SC(arch_prctl, "dp");
4750f66f451Sopenharmony_ci    SC(brk, "p");
4760f66f451Sopenharmony_ci    SC(close, "d");
4770f66f451Sopenharmony_ci    SC(connect, "fpd"); // TODO: sockaddr
4780f66f451Sopenharmony_ci    SC(dup, "f");
4790f66f451Sopenharmony_ci    SC(dup2, "ff");
4800f66f451Sopenharmony_ci    SC(dup3, "ff|open|");
4810f66f451Sopenharmony_ci    SC(execve, "spp");
4820f66f451Sopenharmony_ci    SC(exit_group, "d");
4830f66f451Sopenharmony_ci    SC(fcntl, "fdp"); // TODO: probably needs special case
4840f66f451Sopenharmony_ci    SC(fstat, "f/{stat}");
4850f66f451Sopenharmony_ci    SC(futex, "pdxppx");
4860f66f451Sopenharmony_ci    SC(getdents64, "dpz");
4870f66f451Sopenharmony_ci    SC(geteuid, "");
4880f66f451Sopenharmony_ci    SC(getuid, "");
4890f66f451Sopenharmony_ci
4900f66f451Sopenharmony_ci    SC(getxattr, "sspz");
4910f66f451Sopenharmony_ci    SC(lgetxattr, "sspz");
4920f66f451Sopenharmony_ci    SC(fgetxattr, "fspz");
4930f66f451Sopenharmony_ci
4940f66f451Sopenharmony_ci    SC(lseek, "fo^seek^");
4950f66f451Sopenharmony_ci    SC(lstat, "s/{stat}");
4960f66f451Sopenharmony_ci    SC(mmap, "pz|prot||mmap|fx");
4970f66f451Sopenharmony_ci    SC(mprotect, "pz|prot|");
4980f66f451Sopenharmony_ci    SC(mremap, "pzzdp"); // TODO: flags
4990f66f451Sopenharmony_ci    SC(munmap, "pz");
5000f66f451Sopenharmony_ci    SC(nanosleep, "{timespec}/{timespec}");
5010f66f451Sopenharmony_ci    SC(newfstatat, "fs/{stat}d");
5020f66f451Sopenharmony_ci    SC(open, "sd|open|m");
5030f66f451Sopenharmony_ci    SC(openat, "fs|open|m");
5040f66f451Sopenharmony_ci    SC(poll, "pdd");
5050f66f451Sopenharmony_ci    SC(prlimit64, "d^rlimit^{rlimit}/{rlimit}");
5060f66f451Sopenharmony_ci    SC(read, "d/sz");
5070f66f451Sopenharmony_ci    SC(readlinkat, "s/sz");
5080f66f451Sopenharmony_ci    SC(rt_sigaction, "Sppz");
5090f66f451Sopenharmony_ci    SC(rt_sigprocmask, "^sig^{sigset}/{sigset}z");
5100f66f451Sopenharmony_ci    SC(set_robust_list, "pd");
5110f66f451Sopenharmony_ci    SC(set_tid_address, "p");
5120f66f451Sopenharmony_ci    SC(socket, "ddd"); // TODO: flags
5130f66f451Sopenharmony_ci    SC(stat, "s/{stat}");
5140f66f451Sopenharmony_ci    SC(statfs, "sp");
5150f66f451Sopenharmony_ci    SC(sysinfo, "p");
5160f66f451Sopenharmony_ci    SC(umask, "m");
5170f66f451Sopenharmony_ci    SC(uname, "p");
5180f66f451Sopenharmony_ci    SC(write, "dsz");
5190f66f451Sopenharmony_ci    default:
5200f66f451Sopenharmony_ci      sprintf(name = toybuf, "SYS_%ld", TT.syscall);
5210f66f451Sopenharmony_ci      TT.fmt = "pppppp";
5220f66f451Sopenharmony_ci      break;
5230f66f451Sopenharmony_ci  }
5240f66f451Sopenharmony_ci
5250f66f451Sopenharmony_ci  fprintf(stderr, "%s(", name);
5260f66f451Sopenharmony_ci  TT.arg = 0;
5270f66f451Sopenharmony_ci  print_args();
5280f66f451Sopenharmony_ci}
5290f66f451Sopenharmony_ci
5300f66f451Sopenharmony_cistatic void print_exit(void)
5310f66f451Sopenharmony_ci{
5320f66f451Sopenharmony_ci  long result;
5330f66f451Sopenharmony_ci
5340f66f451Sopenharmony_ci  get_regs();
5350f66f451Sopenharmony_ci  result = TT.regs[REG_ORDER[7]];
5360f66f451Sopenharmony_ci  if (*TT.fmt) print_args();
5370f66f451Sopenharmony_ci  fprintf(stderr, ") = ");
5380f66f451Sopenharmony_ci  if (result >= -4095UL)
5390f66f451Sopenharmony_ci    fprintf(stderr, "-1 %s (%s)", strerrno(-result), strerror(-result));
5400f66f451Sopenharmony_ci  else if (TT.syscall==__NR_mmap || TT.syscall==__NR_brk) print_ptr(result);
5410f66f451Sopenharmony_ci  else fprintf(stderr, "%ld", result);
5420f66f451Sopenharmony_ci  fputc('\n', stderr);
5430f66f451Sopenharmony_ci}
5440f66f451Sopenharmony_ci
5450f66f451Sopenharmony_cistatic int next(void)
5460f66f451Sopenharmony_ci{
5470f66f451Sopenharmony_ci  int status;
5480f66f451Sopenharmony_ci
5490f66f451Sopenharmony_ci  for (;;) {
5500f66f451Sopenharmony_ci    ptrace(PTRACE_SYSCALL, TT.pid, 0, 0);
5510f66f451Sopenharmony_ci    waitpid(TT.pid, &status, 0);
5520f66f451Sopenharmony_ci    // PTRACE_O_TRACESYSGOOD sets bit 7 to indicate a syscall.
5530f66f451Sopenharmony_ci    if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80) return 1;
5540f66f451Sopenharmony_ci    if (WIFEXITED(status)) return 0;
5550f66f451Sopenharmony_ci    fprintf(stderr, "[stopped %d (%x)]\n", status, WSTOPSIG(status));
5560f66f451Sopenharmony_ci  }
5570f66f451Sopenharmony_ci}
5580f66f451Sopenharmony_ci
5590f66f451Sopenharmony_cistatic void strace_detach(int s)
5600f66f451Sopenharmony_ci{
5610f66f451Sopenharmony_ci  xptrace(PTRACE_DETACH, TT.pid, 0, 0);
5620f66f451Sopenharmony_ci  exit(1);
5630f66f451Sopenharmony_ci}
5640f66f451Sopenharmony_ci
5650f66f451Sopenharmony_civoid strace_main(void)
5660f66f451Sopenharmony_ci{
5670f66f451Sopenharmony_ci  int status;
5680f66f451Sopenharmony_ci
5690f66f451Sopenharmony_ci  if (!FLAG(s)) TT.s = 32;
5700f66f451Sopenharmony_ci
5710f66f451Sopenharmony_ci  if (FLAG(p)) {
5720f66f451Sopenharmony_ci    if (*toys.optargs) help_exit("No arguments with -p");
5730f66f451Sopenharmony_ci    TT.pid = TT.p;
5740f66f451Sopenharmony_ci    signal(SIGINT, strace_detach);
5750f66f451Sopenharmony_ci    // TODO: PTRACE_SEIZE instead?
5760f66f451Sopenharmony_ci    xptrace(PTRACE_ATTACH, TT.pid, 0, 0);
5770f66f451Sopenharmony_ci  } else {
5780f66f451Sopenharmony_ci    if (!*toys.optargs) help_exit("Needs 1 argument");
5790f66f451Sopenharmony_ci    TT.pid = xfork();
5800f66f451Sopenharmony_ci    if (!TT.pid) {
5810f66f451Sopenharmony_ci      errno = 0;
5820f66f451Sopenharmony_ci      ptrace(PTRACE_TRACEME);
5830f66f451Sopenharmony_ci      if (errno) perror_exit("PTRACE_TRACEME failed");
5840f66f451Sopenharmony_ci      raise(SIGSTOP);
5850f66f451Sopenharmony_ci      toys.stacktop = 0;
5860f66f451Sopenharmony_ci      xexec(toys.optargs);
5870f66f451Sopenharmony_ci    }
5880f66f451Sopenharmony_ci  }
5890f66f451Sopenharmony_ci
5900f66f451Sopenharmony_ci  do {
5910f66f451Sopenharmony_ci    waitpid(TT.pid, &status, 0);
5920f66f451Sopenharmony_ci  } while (!WIFSTOPPED(status));
5930f66f451Sopenharmony_ci
5940f66f451Sopenharmony_ci  // TODO: PTRACE_O_TRACEEXIT
5950f66f451Sopenharmony_ci  // TODO: PTRACE_O_TRACEFORK/PTRACE_O_TRACEVFORK/PTRACE_O_TRACECLONE for -f.
5960f66f451Sopenharmony_ci  errno = 0;
5970f66f451Sopenharmony_ci  ptrace(PTRACE_SETOPTIONS, TT.pid, 0, PTRACE_O_TRACESYSGOOD);
5980f66f451Sopenharmony_ci  if (errno) perror_exit("PTRACE_SETOPTIONS PTRACE_O_TRACESYSGOOD failed");
5990f66f451Sopenharmony_ci
6000f66f451Sopenharmony_ci  // TODO: real strace swallows the failed execve()s if it started the child
6010f66f451Sopenharmony_ci
6020f66f451Sopenharmony_ci  for (;;) {
6030f66f451Sopenharmony_ci    if (!next()) break;
6040f66f451Sopenharmony_ci    print_enter();
6050f66f451Sopenharmony_ci    if (!next()) break;
6060f66f451Sopenharmony_ci    print_exit();
6070f66f451Sopenharmony_ci  }
6080f66f451Sopenharmony_ci
6090f66f451Sopenharmony_ci  // TODO: support -f and keep track of children.
6100f66f451Sopenharmony_ci  waitpid(TT.pid, &status, 0);
6110f66f451Sopenharmony_ci  if (WIFEXITED(status))
6120f66f451Sopenharmony_ci    fprintf(stderr, "+++ exited with %d +++\n", WEXITSTATUS(status));
6130f66f451Sopenharmony_ci  if (WIFSTOPPED(status))
6140f66f451Sopenharmony_ci    fprintf(stderr, "+++ stopped with %d +++\n", WSTOPSIG(status));
6150f66f451Sopenharmony_ci}
616