strace.c 17 KB

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