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__)
39 static const char REG_ORDER[] = {0,1,2,3,4,5,7,0};
40 #elif defined(__aarch64__)
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
ptrace_struct(long addr,void * dst,size_t bytes)170 static void ptrace_struct(long addr, void *dst, size_t bytes)
171 {
172 int offset = 0, i;
173 long v;
174
175 for (i=0; i<bytes; i+=sizeof(long)) {
176 errno = 0;
177 v = ptrace(PTRACE_PEEKDATA, TT.pid, addr + offset);
178 if (errno) perror_exit("PTRACE_PEEKDATA failed");
179 memcpy(dst + offset, &v, sizeof(v));
180 offset += sizeof(long);
181 }
182 }
183
184 #define C(n) n, #n
185
print_bits(int bitmask,long v,char * zero,...)186 static void print_bits(int bitmask, long v, char *zero, ...)
187 {
188 va_list ap;
189 int first = 1;
190
191 if (!v && zero) {
192 fprintf(stderr, "%s", zero);
193 return;
194 }
195 va_start(ap, zero);
196 for (;;) {
197 int this = va_arg(ap, int);
198 char *name;
199
200 if (!this) break;
201 name = va_arg(ap, char*);
202 if (bitmask) {
203 if (v & this) {
204 fprintf(stderr, "%s%s", first?"":"|", name);
205 first = 0;
206 v &= ~this;
207 }
208 } else {
209 if (v == this) {
210 fprintf(stderr, "%s", name);
211 v = 0;
212 break;
213 }
214 }
215 }
216 va_end(ap);
217 if (v) fprintf(stderr, "%s%#lx", first?"":"|", v);
218 }
219
print_mode(unsigned m)220 static void print_mode(unsigned m)
221 {
222 if (m & S_IFMT) {
223 print_bits(0, m & S_IFMT, "", C(S_IFREG), C(S_IFDIR), C(S_IFLNK),
224 C(S_IFBLK), C(S_IFCHR), C(S_IFIFO), C(S_IFSOCK), 0);
225 fputc('|', stderr);
226 m &= ~S_IFMT;
227 }
228 fprintf(stderr, "%#o", m);
229 }
230
231 // TODO: this all relies on having the libc structs match the kernel structs,
232 // which isn't always true for glibc...
print_struct(long addr)233 static void print_struct(long addr)
234 {
235 if (!addr) { // All NULLs look the same...
236 fprintf(stderr, "NULL");
237 while (*TT.fmt != '}') ++TT.fmt;
238 ++TT.fmt;
239 } else if (strstart(&TT.fmt, "ifreq}")) {
240 struct ifreq ir;
241
242 ptrace_struct(addr, &ir, sizeof(ir));
243 // TODO: is this always an ioctl? use TT.regs[REG_ORDER[1]] to work out what to show.
244 fprintf(stderr, "{...}");
245 } else if (strstart(&TT.fmt, "fsxattr}")) {
246 struct fsxattr fx;
247
248 ptrace_struct(addr, &fx, sizeof(fx));
249 fprintf(stderr, "{fsx_xflags=%#x, fsx_extsize=%d, fsx_nextents=%d, "
250 "fsx_projid=%d, fsx_cowextsize=%d}", fx.fsx_xflags, fx.fsx_extsize,
251 fx.fsx_nextents, fx.fsx_projid, fx.fsx_cowextsize);
252 } else if (strstart(&TT.fmt, "long}")) {
253 long l;
254
255 ptrace_struct(addr, &l, sizeof(l));
256 fprintf(stderr, "%ld", l);
257 } else if (strstart(&TT.fmt, "longx}")) {
258 long l;
259
260 ptrace_struct(addr, &l, sizeof(l));
261 fprintf(stderr, "%#lx", l);
262 } else if (strstart(&TT.fmt, "rlimit}")) {
263 struct rlimit rl;
264
265 ptrace_struct(addr, &rl, sizeof(rl));
266 fprintf(stderr, "{rlim_cur=%lld, rlim_max=%lld}",
267 (long long)rl.rlim_cur, (long long)rl.rlim_max);
268 } else if (strstart(&TT.fmt, "sigset}")) {
269 long long ss;
270 int i;
271
272 ptrace_struct(addr, &ss, sizeof(ss));
273 fprintf(stderr, "[");
274 for (i=0; i<64;++i) {
275 // TODO: use signal names, fix spacing
276 if (ss & (1ULL<<i)) fprintf(stderr, "%d ", i);
277 }
278 fprintf(stderr, "]");
279 } else if (strstart(&TT.fmt, "stat}")) {
280 struct stat sb;
281
282 ptrace_struct(addr, &sb, sizeof(sb));
283 if (FLAG(v)) {
284 // TODO: full atime/mtime/ctime dump.
285 fprintf(stderr, "{st_dev=makedev(%#x, %#x), st_ino=%ld, st_mode=",
286 dev_major(sb.st_dev), dev_minor(sb.st_dev), sb.st_ino);
287 print_mode(sb.st_mode);
288 fprintf(stderr, ", st_nlink=%ld, st_uid=%d, st_gid=%d, "
289 "st_blksize=%ld, st_blocks=%ld, st_size=%lld, st_atime=%ld, "
290 "st_mtime=%ld, st_ctime=%ld}", (long) sb.st_nlink, sb.st_uid,
291 sb.st_gid, (long) sb.st_blksize, sb.st_blocks, (long long)sb.st_size,
292 sb.st_atime, sb.st_mtime, sb.st_ctime);
293 } else {
294 fprintf(stderr, "{st_mode=");
295 print_mode(sb.st_mode);
296 fprintf(stderr, ", st_size=%lld, ...}", (long long)sb.st_size);
297 }
298 } else if (strstart(&TT.fmt, "termios}")) {
299 struct termios to;
300
301 ptrace_struct(addr, &to, sizeof(to));
302 fprintf(stderr, "{c_iflag=%#lx, c_oflag=%#lx, c_cflag=%#lx, c_lflag=%#lx}",
303 (long)to.c_iflag, (long)to.c_oflag, (long)to.c_cflag, (long)to.c_lflag);
304 } else if (strstart(&TT.fmt, "timespec}")) {
305 struct timespec ts;
306
307 ptrace_struct(addr, &ts, sizeof(ts));
308 fprintf(stderr, "{tv_sec=%lld, tv_nsec=%lld}",
309 (long long)ts.tv_sec, (long long)ts.tv_nsec);
310 } else if (strstart(&TT.fmt, "winsize}")) {
311 struct winsize ws;
312
313 ptrace_struct(addr, &ws, sizeof(ws));
314 fprintf(stderr, "{ws_row=%hu, ws_col=%hu, ws_xpixel=%hu, ws_ypixel=%hu}",
315 ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
316 } else abort();
317 }
318
print_ptr(long addr)319 static void print_ptr(long addr)
320 {
321 if (!addr) fprintf(stderr, "NULL");
322 else fprintf(stderr, "0x%lx", addr);
323 }
324
print_string(long addr,long limit)325 static void print_string(long addr, long limit)
326 {
327 long offset = 0, total = 0;
328 int i;
329
330 fputc('"', stderr);
331 for (;;) {
332 errno = 0;
333 long v = ptrace(PTRACE_PEEKDATA, TT.pid, addr + offset);
334 if (errno) return;
335 memcpy(toybuf, &v, sizeof(v));
336 for (i=0; i<sizeof(v); ++i) {
337 if (!toybuf[i]) {
338 // TODO: handle the case of dumping n bytes (e.g. read()/write()), not
339 // just NUL-terminated strings.
340 fputc('"', stderr);
341 return;
342 }
343 if (isprint(toybuf[i])) fputc(toybuf[i], stderr);
344 else {
345 // TODO: reuse an existing escape function.
346 fputc('\\', stderr);
347 if (toybuf[i] == '\n') fputc('n', stderr);
348 else if (toybuf[i] == '\r') fputc('r', stderr);
349 else if (toybuf[i] == '\t') fputc('t', stderr);
350 else fprintf(stderr, "x%2.2x", toybuf[i]);
351 }
352 if (++total >= limit) {
353 fprintf(stderr, "\"...");
354 return;
355 }
356 }
357 offset += sizeof(v);
358 }
359 }
360
print_flags(long v)361 static void print_flags(long v)
362 {
363 if (strstart(&TT.fmt, "access|")) {
364 print_bits(1, v, "F_OK", C(R_OK), C(W_OK), C(X_OK), 0);
365 } else if (strstart(&TT.fmt, "mmap|")) {
366 print_bits(1, v, 0, C(MAP_SHARED), C(MAP_PRIVATE),
367 #if defined(MAP_32BIT)
368 C(MAP_32BIT),
369 #endif
370 C(MAP_ANONYMOUS), C(MAP_FIXED), C(MAP_GROWSDOWN), C(MAP_HUGETLB),
371 C(MAP_DENYWRITE), 0);
372 } else if (strstart(&TT.fmt, "open|")) {
373 print_bits(1, v, "O_RDONLY", C(O_WRONLY), C(O_RDWR), C(O_CLOEXEC),
374 C(O_CREAT), C(O_DIRECTORY), C(O_EXCL), C(O_NOCTTY), C(O_NOFOLLOW),
375 C(O_TRUNC), C(O_ASYNC), C(O_APPEND), C(O_DSYNC), C(O_EXCL),
376 C(O_NOATIME), C(O_NONBLOCK), C(O_PATH), C(O_SYNC),
377 0x4000, "O_DIRECT", 0x8000, "O_LARGEFILE", 0x410000, "O_TMPFILE", 0);
378 } else if (strstart(&TT.fmt, "prot|")) {
379 print_bits(1,v,"PROT_NONE",C(PROT_READ),C(PROT_WRITE),C(PROT_EXEC),
380 #if defined(PROT_BTI)
381 C(PROT_BTI),
382 #endif
383 #if defined(PROT_MTE)
384 C(PROT_MTE),
385 #endif
386 0);
387 } else if (strstart(&TT.fmt, "grnd|")) {
388 print_bits(1,v,"0",C(GRND_RANDOM),C(GRND_NONBLOCK),0);
389 } else abort();
390 }
391
print_alternatives(long v)392 static void print_alternatives(long v)
393 {
394 if (strstart(&TT.fmt, "clockid^")) {
395 print_bits(0, v, "CLOCK_REALTIME", C(CLOCK_MONOTONIC),
396 C(CLOCK_PROCESS_CPUTIME_ID), C(CLOCK_THREAD_CPUTIME_ID),
397 C(CLOCK_MONOTONIC_RAW), C(CLOCK_REALTIME_COARSE),
398 C(CLOCK_MONOTONIC_COARSE), C(CLOCK_BOOTTIME),
399 C(CLOCK_REALTIME_ALARM), C(CLOCK_BOOTTIME_ALARM), 0);
400 } else if (strstart(&TT.fmt, "rlimit^")) {
401 print_bits(0, v, "RLIMIT_CPU", C(RLIMIT_FSIZE), C(RLIMIT_DATA),
402 C(RLIMIT_STACK), C(RLIMIT_CORE), C(RLIMIT_RSS), C(RLIMIT_NPROC),
403 C(RLIMIT_NOFILE), C(RLIMIT_MEMLOCK), C(RLIMIT_AS), C(RLIMIT_LOCKS),
404 C(RLIMIT_SIGPENDING), C(RLIMIT_MSGQUEUE), C(RLIMIT_NICE),
405 C(RLIMIT_RTPRIO), C(RLIMIT_RTTIME), 0);
406 } else if (strstart(&TT.fmt, "seek^")) {
407 print_bits(0, v, "SEEK_SET", C(SEEK_CUR), C(SEEK_END), C(SEEK_DATA),
408 C(SEEK_HOLE), 0);
409 } else if (strstart(&TT.fmt, "sig^")) {
410 print_bits(0, v, "SIG_BLOCK", C(SIG_UNBLOCK), C(SIG_SETMASK), 0);
411 } else abort();
412 }
413
print_args()414 static void print_args()
415 {
416 int i;
417
418 // Loop through arguments and print according to format string
419 for (i = 0; *TT.fmt; i++, TT.arg++) {
420 long v = TT.regs[REG_ORDER[TT.arg]];
421 char *s, ch;
422
423 if (i) fprintf(stderr, ", ");
424 switch (ch = *TT.fmt++) {
425 case 'd': fprintf(stderr, "%ld", v); break; // decimal
426 case 'f': if ((int) v == AT_FDCWD) fprintf(stderr, "AT_FDCWD"); // fd
427 else fprintf(stderr, "%ld", v);
428 break;
429 case 'F': print_string(v, LONG_MAX); break;
430 case 'i': fprintf(stderr, "%s", strioctl(v)); break; // ioctl name
431 case 'm': print_mode(v); break;
432 case 'o': fprintf(stderr, "%ld", v); break; // off_t
433 case 'p': print_ptr(v); break;
434 case 's': print_string(v, TT.s); break;
435 case 'S': // The libc-reserved signals aren't known to num_to_sig().
436 // TODO: use an strace-only routine for >= 32?
437 if (!(s = num_to_sig(v))) fprintf(stderr, "%ld", v);
438 else fprintf(stderr, "SIG%s", s);
439 break;
440 case 'z': fprintf(stderr, "%lu", (unsigned long) v); break; // size_t
441 case 'x': fprintf(stderr, "%#lx", v); break; // hex
442
443 case '{': print_struct(v); break;
444 case '|': print_flags(v); break;
445 case '^': print_alternatives(v); break;
446
447 case '/': return; // Separates "enter" and "exit" arguments.
448
449 default: fprintf(stderr, "?%c<%#lx>", ch, v); break;
450 }
451 }
452 }
453
print_enter(void)454 static void print_enter(void)
455 {
456 struct iovec v = {.iov_base=&TT.regs, .iov_len=sizeof(TT.regs)};
457 char *name;
458
459 xptrace(PTRACE_GETREGSET, TT.pid, (void *)1, &v); // NT_PRSTATUS
460 TT.syscall = TT.regs[REG_ORDER[6]];
461 if (TT.syscall == __NR_ioctl) {
462 name = "ioctl";
463 switch (TT.regs[REG_ORDER[1]]) {
464 case FS_IOC_FSGETXATTR: TT.fmt = "fi/{fsxattr}"; break;
465 case FS_IOC_FSSETXATTR: TT.fmt = "fi{fsxattr}"; break;
466 case FS_IOC_GETFLAGS: TT.fmt = "fi/{longx}"; break;
467 case FS_IOC_GETVERSION: TT.fmt = "fi/{long}"; break;
468 case FS_IOC_SETFLAGS: TT.fmt = "fi{long}"; break;
469 case FS_IOC_SETVERSION: TT.fmt = "fi{long}"; break;
470 //case SIOCGIFCONF: struct ifconf
471 case SIOCGIFADDR:
472 case SIOCGIFBRDADDR:
473 case SIOCGIFDSTADDR:
474 case SIOCGIFFLAGS:
475 case SIOCGIFHWADDR:
476 case SIOCGIFMAP:
477 case SIOCGIFMTU:
478 case SIOCGIFNETMASK:
479 case SIOCGIFTXQLEN: TT.fmt = "fi/{ifreq}"; break;
480 case SIOCSIFADDR:
481 case SIOCSIFBRDADDR:
482 case SIOCSIFDSTADDR:
483 case SIOCSIFFLAGS:
484 case SIOCSIFHWADDR:
485 case SIOCSIFMAP:
486 case SIOCSIFMTU:
487 case SIOCSIFNETMASK:
488 case SIOCSIFTXQLEN: TT.fmt = "fi{ifreq}"; break;
489 case TCGETS: TT.fmt = "fi/{termios}"; break;
490 case TCSETS: TT.fmt = "fi{termios}"; break;
491 case TIOCGWINSZ: TT.fmt = "fi/{winsize}"; break;
492 case TIOCSWINSZ: TT.fmt = "fi{winsize}"; break;
493 default:
494 TT.fmt = (TT.regs[REG_ORDER[0]]&1) ? "fip" : "fi/p";
495 break;
496 }
497 } else switch (TT.syscall) {
498 #define SC(n,f) case __NR_ ## n: name = #n; TT.fmt = f; break
499 #if defined(__NR_access)
500 SC(access, "F|access|");
501 #endif
502 #if defined(__NR_arch_prctl)
503 SC(arch_prctl, "dp");
504 #endif
505 SC(brk, "p");
506 SC(clock_nanosleep, "^clockid^d{timespec}/{timespec}");
507 SC(close, "d");
508 SC(connect, "fpd"); // TODO: sockaddr
509 SC(dup, "f");
510 #if defined(__NR_dup2)
511 SC(dup2, "ff");
512 #endif
513 SC(dup3, "ff|open|");
514 SC(execve, "Fpp");
515 SC(exit_group, "d");
516 SC(faccessat, "fF|access|");
517 SC(fcntl, "fdp"); // TODO: probably needs special case
518 SC(fstat, "f/{stat}");
519 SC(futex, "pdxppx");
520 SC(getcwd, "/Fz");
521 SC(getdents64, "dpz");
522 SC(getegid, "");
523 SC(geteuid, "");
524 SC(getrandom, "pz|grnd|");
525 SC(getgid, "");
526 SC(getuid, "");
527
528 SC(getxattr, "Fspz");
529 SC(lgetxattr, "Fspz");
530 SC(fgetxattr, "fspz");
531
532 SC(lseek, "fo^seek^");
533 #if defined(__NR_lstat)
534 SC(lstat, "s/{stat}");
535 #endif
536 SC(mmap, "pz|prot||mmap|fx");
537 SC(mprotect, "pz|prot|");
538 SC(mremap, "pzzdp"); // TODO: flags
539 SC(munmap, "pz");
540 SC(nanosleep, "{timespec}/{timespec}");
541 #if defined(__NR_newfstatat)
542 SC(newfstatat, "fF/{stat}d");
543 #endif
544 #if defined(__NR_open)
545 SC(open, "Fd|open|m");
546 #endif
547 SC(openat, "fF|open|m");
548 #if defined(__NR_poll)
549 SC(poll, "pdd");
550 #endif
551 SC(prlimit64, "d^rlimit^{rlimit}/{rlimit}");
552 SC(read, "d/sz");
553 #if defined(__NR_readlink)
554 SC(readlink, "F/sz");
555 #endif
556 SC(readlinkat, "fF/sz");
557 #if defined(__NR_rseq)
558 SC(rseq, "pzxx");
559 #endif
560 SC(rt_sigaction, "Sppz");
561 SC(rt_sigprocmask, "^sig^{sigset}/{sigset}z");
562 SC(set_robust_list, "pd");
563 SC(set_tid_address, "p");
564 SC(socket, "ddd"); // TODO: flags
565 #if defined(__NR_stat)
566 SC(stat, "F/{stat}");
567 #endif
568 SC(statfs, "Fp");
569 SC(sysinfo, "p");
570 SC(umask, "m");
571 SC(uname, "p");
572 SC(write, "dsz");
573 default:
574 sprintf(name = toybuf, "SYS_%ld", TT.syscall);
575 TT.fmt = "pppppp";
576 break;
577 }
578
579 fprintf(stderr, "%s(", name);
580 TT.arg = 0;
581 print_args();
582 }
583
print_exit(void)584 static void print_exit(void)
585 {
586 long regs[256/sizeof(long)];
587 struct iovec v = {.iov_base=®s, .iov_len=sizeof(regs)};
588 long result;
589
590 // We read the registers into a local because we only want the result,
591 // and don't want to clobber the argument that was in the same register
592 // earlier (the first argument of getcwd(2), for example, gets printed
593 // on exit rather than entry, and arm/riscv both reuse that register for
594 // the result).
595 xptrace(PTRACE_GETREGSET, TT.pid, (void *)1, &v); // NT_PRSTATUS
596 if (*TT.fmt) print_args();
597 fprintf(stderr, ") = ");
598 result = regs[REG_ORDER[7]];
599 if (result >= -4095UL)
600 fprintf(stderr, "-1 %s (%s)", strerrno(-result), strerror(-result));
601 else if (TT.syscall==__NR_mmap || TT.syscall==__NR_brk) print_ptr(result);
602 else fprintf(stderr, "%ld", result);
603 fputc('\n', stderr);
604 }
605
next(void)606 static int next(void)
607 {
608 int status;
609
610 for (;;) {
611 ptrace(PTRACE_SYSCALL, TT.pid, 0, 0);
612 waitpid(TT.pid, &status, 0);
613 // PTRACE_O_TRACESYSGOOD sets bit 7 to indicate a syscall.
614 if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80) return 1;
615 if (WIFEXITED(status)) return 0;
616 fprintf(stderr, "[stopped %d (%x)]\n", status, WSTOPSIG(status));
617 }
618 }
619
strace_detach(int s)620 static void strace_detach(int s)
621 {
622 xptrace(PTRACE_DETACH, TT.pid, 0, 0);
623 exit(1);
624 }
625
strace_main(void)626 void strace_main(void)
627 {
628 int status;
629
630 if (!FLAG(s)) TT.s = 32;
631
632 if (FLAG(p)) {
633 if (*toys.optargs) help_exit("No arguments with -p");
634 TT.pid = TT.p;
635 signal(SIGINT, strace_detach);
636 // TODO: PTRACE_SEIZE instead?
637 xptrace(PTRACE_ATTACH, TT.pid, 0, 0);
638 } else {
639 if (!*toys.optargs) help_exit("Needs 1 argument");
640 TT.pid = xfork();
641 if (!TT.pid) {
642 errno = 0;
643 ptrace(PTRACE_TRACEME);
644 if (errno) perror_exit("PTRACE_TRACEME failed");
645 raise(SIGSTOP);
646 toys.stacktop = 0;
647 xexec(toys.optargs);
648 }
649 }
650
651 do {
652 waitpid(TT.pid, &status, 0);
653 } while (!WIFSTOPPED(status));
654
655 // TODO: PTRACE_O_TRACEEXIT
656 // TODO: PTRACE_O_TRACEFORK/PTRACE_O_TRACEVFORK/PTRACE_O_TRACECLONE for -f.
657 errno = 0;
658 ptrace(PTRACE_SETOPTIONS, TT.pid, 0, PTRACE_O_TRACESYSGOOD);
659 if (errno) perror_exit("PTRACE_SETOPTIONS PTRACE_O_TRACESYSGOOD failed");
660
661 // TODO: real strace swallows the failed execve()s if it started the child
662
663 for (;;) {
664 if (!next()) break;
665 print_enter();
666 if (!next()) break;
667 print_exit();
668 }
669
670 // TODO: support -f and keep track of children.
671 waitpid(TT.pid, &status, 0);
672 if (WIFEXITED(status))
673 fprintf(stderr, "+++ exited with %d +++\n", WEXITSTATUS(status));
674 if (WIFSTOPPED(status))
675 fprintf(stderr, "+++ stopped with %d +++\n", WSTOPSIG(status));
676 }
677