cosmopolitan/tool/build/strace.c
Justine Tunney a6b02ce5a6 Add lua repl interface to redbean
You can now interact with the global web server state on the command
line, which the web server is running. This supports Emacs shortcuts
with history, readline parity, <tab> completions, plus hints. Enjoy!
2022-04-16 20:31:16 -07:00

1200 lines
54 KiB
C

/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2022 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/user_regs_struct.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/append.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/ptrace.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/w.h"
/**
* @fileoverview ptrace() tutorial
*/
#define PROLOGUE "%r%8d %'18T "
#undef __NR_execve
#define __WALL 0x40000000
#define PTR 0
#define ULONG 0
#define INT 1
#define LONG 2
#define STR 3
#define BUF 4
#define IOV 5
#define STRLIST 6
#define INTPTR 7
#define STAT 8
#define SIG 9
#define SIGSET 10
#define PIPE 11
#define OCTAL 0x80
static const long __NR_brk = 12;
static const long __NR_sigreturn = 15;
static const struct Syscall {
long *number;
const char *name;
char arity;
char eager;
unsigned char ret;
unsigned char arg[6];
} kSyscalls[] = {
// clang-format off
{&__NR_exit, "exit", 1, 1, INT, {INT}},
{&__NR_exit_group, "exit_group", 1, 1, INT, {INT}},
{&__NR_read, "read", 3, 1, LONG, {INT, BUF, ULONG}},
{&__NR_write, "write", 3, 3, LONG, {INT, BUF, ULONG}},
{&__NR_open, "open", 3, 3, INT, {STR, INT, OCTAL|INT}},
{&__NR_close, "close", 1, 1, INT, {INT}},
{&__NR_brk, "brk", 1, 1, ULONG, {ULONG}},
{&__NR_stat, "stat", 2, 1, INT, {STR, STAT}},
{&__NR_fstat, "fstat", 2, 1, INT, {INT, STAT}},
{&__NR_lstat, "lstat", 2, 1, INT, {INT, STAT}},
{&__NR_poll, "poll", 3, 3, INT, {PTR, INT, INT}},
{&__NR_ppoll, "ppoll", 4, 4, INT},
{&__NR_lseek, "lseek", 3, 3, LONG, {INT, LONG, INT}},
{&__NR_mmap, "mmap", 6, 6, ULONG, {PTR, ULONG, INT, INT, INT, ULONG}},
{&__NR_msync, "msync", 3, 3, INT, {PTR, ULONG, INT}},
{&__NR_mprotect, "mprotect", 3, 3, INT, {PTR, ULONG, INT}},
{&__NR_munmap, "munmap", 2, 2, INT, {PTR, ULONG}},
{&__NR_sigreturn, "rt_sigreturn", 6, 6, LONG},
{&__NR_sigaction, "rt_sigaction", 4, 4, INT, {SIG}},
{&__NR_sigprocmask, "rt_sigprocmask", 4, 4, INT, {INT, SIGSET, SIGSET, LONG}},
{&__NR_sigpending, "rt_sigpending", 2, 2, INT, {SIGSET, LONG}},
{&__NR_sigsuspend, "rt_sigsuspend", 2, 2, INT, {SIGSET, LONG}},
{&__NR_rt_sigqueueinfo, "rt_sigqueueinfo", 6, 6},
{&__NR_ioctl, "ioctl", 3, 3, INT, {INT, ULONG, ULONG}},
{&__NR_pread, "pread64", 4, 1, LONG, {INT, BUF, ULONG, ULONG}},
{&__NR_pwrite, "pwrite64", 4, 4, LONG, {INT, BUF, ULONG, ULONG}},
{&__NR_readv, "readv", 3, 1, LONG, {INT, IOV, INT}},
{&__NR_writev, "writev", 3, 3, LONG, {INT, IOV, INT}},
{&__NR_access, "access", 2, 2, INT, {STR, OCTAL|INT}},
{&__NR_pipe, "pipe", 1, 0, INT, {PIPE}},
{&__NR_pipe2, "pipe2", 2, 0, INT, {PIPE, INT}},
{&__NR_select, "select", 5, 5},
{&__NR_pselect, "pselect", 6, 6},
{&__NR_pselect6, "pselect6", 6, 6},
{&__NR_sched_yield, "sched_yield", 0, 0, INT},
{&__NR_mremap, "mremap", 5, 5},
{&__NR_mincore, "mincore", 6, 6},
{&__NR_madvise, "madvise", 6, 6},
{&__NR_shmget, "shmget", 6, 6},
{&__NR_shmat, "shmat", 6, 6},
{&__NR_shmctl, "shmctl", 6, 6},
{&__NR_dup, "dup", 1, 1, INT, {INT}},
{&__NR_dup2, "dup2", 2, 2, INT, {INT, INT}},
{&__NR_pause, "pause", 0, 0, INT},
{&__NR_nanosleep, "nanosleep", 2, 1},
{&__NR_getitimer, "getitimer", 2, 2},
{&__NR_setitimer, "setitimer", 3, 3},
{&__NR_alarm, "alarm", 1, 1},
{&__NR_getpid, "getpid", 0, 0, INT},
{&__NR_sendfile, "sendfile", 6, 6},
{&__NR_socket, "socket", 3, 3, INT, {INT, INT, INT}},
{&__NR_connect, "connect", 3, 3},
{&__NR_accept, "accept", 3, 3},
{&__NR_sendto, "sendto", 6, 6},
{&__NR_recvfrom, "recvfrom", 6, 6},
{&__NR_sendmsg, "sendmsg", 6, 6},
{&__NR_recvmsg, "recvmsg", 6, 6},
{&__NR_shutdown, "shutdown", 6, 6},
{&__NR_bind, "bind", 6, 6},
{&__NR_listen, "listen", 6, 6},
{&__NR_getsockname, "getsockname", 6, 6},
{&__NR_getpeername, "getpeername", 6, 6},
{&__NR_socketpair, "socketpair", 6, 6},
{&__NR_setsockopt, "setsockopt", 6, 6},
{&__NR_getsockopt, "getsockopt", 6, 6},
{&__NR_fork, "fork", 0, 0, INT},
{&__NR_vfork, "vfork", 0, 0, INT},
{&__NR_posix_spawn, "posix_spawn", 6, 6},
{&__NR_execve, "execve", 3, 3, INT, {STR, STRLIST, STRLIST}},
{&__NR_wait4, "wait4", 4, 4, INT, {INT, INTPTR, INT, PTR}},
{&__NR_kill, "kill", 2, 2, INT, {INT, SIG}},
{&__NR_killpg, "killpg", 2, 2, INT, {INT, SIG}},
{&__NR_clone, "clone", 5, 5, INT, {PTR, PTR, INTPTR, INTPTR, ULONG}},
{&__NR_tkill, "tkill", 2, 2, INT, {INT, SIG}},
{&__NR_futex, "futex", 6, 6},
{&__NR_set_robust_list, "set_robust_list", 6, 6},
{&__NR_get_robust_list, "get_robust_list", 6, 6},
{&__NR_uname, "uname", 6, 6},
{&__NR_semget, "semget", 6, 6},
{&__NR_semop, "semop", 6, 6},
{&__NR_semctl, "semctl", 6, 6},
{&__NR_shmdt, "shmdt", 6, 6},
{&__NR_msgget, "msgget", 6, 6},
{&__NR_msgsnd, "msgsnd", 6, 6},
{&__NR_msgrcv, "msgrcv", 6, 6},
{&__NR_msgctl, "msgctl", 6, 6},
{&__NR_fcntl, "fcntl", 3, 3, INT, {INT, INT, ULONG}},
{&__NR_flock, "flock", 6, 6},
{&__NR_fsync, "fsync", 6, 6},
{&__NR_fdatasync, "fdatasync", 6, 6},
{&__NR_truncate, "truncate", 2, 2, INT, {STR, ULONG}},
{&__NR_ftruncate, "ftruncate", 6, 6, INT, {INT, ULONG}},
{&__NR_getcwd, "getcwd", 2, 2, INT, {BUF, ULONG}},
{&__NR_chdir, "chdir", 1, 1, INT, {STR}},
{&__NR_fchdir, "fchdir", 1, 1, INT, {INT}},
{&__NR_rename, "rename", 2, 2, INT, {STR, STR}},
{&__NR_mkdir, "mkdir", 2, 2, INT, {STR, OCTAL|INT}},
{&__NR_rmdir, "rmdir", 1, 1, INT, {STR}},
{&__NR_creat, "creat", 2, 2, INT, {STR, OCTAL|INT}},
{&__NR_link, "link", 2, 2, INT, {STR, STR}},
{&__NR_unlink, "unlink", 1, 1, INT, {STR}},
{&__NR_symlink, "symlink", 6, 6},
{&__NR_readlink, "readlink", 3, 1, INT, {STR, BUF, ULONG}},
{&__NR_chmod, "chmod", 6, 6},
{&__NR_fchmod, "fchmod", 6, 6},
{&__NR_chown, "chown", 6, 6},
{&__NR_fchown, "fchown", 6, 6},
{&__NR_lchown, "lchown", 6, 6},
{&__NR_umask, "umask", 1, 1, OCTAL|INT, {OCTAL|INT}},
{&__NR_gettimeofday, "gettimeofday", 6, 6},
{&__NR_getrlimit, "getrlimit", 6, 6},
{&__NR_getrusage, "getrusage", 6, 6},
{&__NR_sysinfo, "sysinfo", 6, 6},
{&__NR_times, "times", 6, 6},
{&__NR_ptrace, "ptrace", 6, 6},
{&__NR_syslog, "syslog", 6, 6},
{&__NR_getuid, "getuid", 0, 0, INT},
{&__NR_getgid, "getgid", 0, 0, INT},
{&__NR_getppid, "getppid", 0, 0, INT},
{&__NR_getpgrp, "getpgrp", 0, 0, INT},
{&__NR_setsid, "setsid", 1, 1, INT, {INT}},
{&__NR_getsid, "getsid", 0, 0, INT},
{&__NR_getpgid, "getpgid", 0, 0, INT},
{&__NR_setpgid, "setpgid", 2, 2, INT, {INT, INT}},
{&__NR_geteuid, "geteuid", 0, 0, INT},
{&__NR_getegid, "getegid", 0, 0, INT},
{&__NR_getgroups, "getgroups", 6, 6},
{&__NR_setgroups, "setgroups", 6, 6},
{&__NR_setreuid, "setreuid", 6, 6},
{&__NR_setregid, "setregid", 6, 6},
{&__NR_setuid, "setuid", 1, 1, INT, {INT}},
{&__NR_setgid, "setgid", 1, 1, INT, {INT}},
{&__NR_setresuid, "setresuid", 6, 6},
{&__NR_setresgid, "setresgid", 6, 6},
{&__NR_getresuid, "getresuid", 6, 6},
{&__NR_getresgid, "getresgid", 6, 6},
{&__NR_sigaltstack, "sigaltstack", 6, 6},
{&__NR_mknod, "mknod", 6, 6},
{&__NR_mknodat, "mknodat", 6, 6},
{&__NR_mkfifo, "mkfifo", 6, 6},
{&__NR_mkfifoat, "mkfifoat", 6, 6},
{&__NR_statfs, "statfs", 6, 6},
{&__NR_fstatfs, "fstatfs", 6, 6},
{&__NR_getpriority, "getpriority", 6, 6},
{&__NR_setpriority, "setpriority", 6, 6},
{&__NR_mlock, "mlock", 6, 6},
{&__NR_munlock, "munlock", 6, 6},
{&__NR_mlockall, "mlockall", 6, 6},
{&__NR_munlockall, "munlockall", 6, 6},
{&__NR_setrlimit, "setrlimit", 6, 6},
{&__NR_chroot, "chroot", 6, 6},
{&__NR_sync, "sync", 6, 6},
{&__NR_acct, "acct", 6, 6},
{&__NR_settimeofday, "settimeofday", 6, 6},
{&__NR_mount, "mount", 6, 6},
{&__NR_reboot, "reboot", 6, 6},
{&__NR_quotactl, "quotactl", 6, 6},
{&__NR_setfsuid, "setfsuid", 6, 6},
{&__NR_setfsgid, "setfsgid", 6, 6},
{&__NR_capget, "capget", 6, 6},
{&__NR_capset, "capset", 6, 6},
{&__NR_sigtimedwait, "sigtimedwait", 6, 6},
{&__NR_personality, "personality", 6, 6},
{&__NR_ustat, "ustat", 6, 6},
{&__NR_sysfs, "sysfs", 6, 6},
{&__NR_sched_setparam, "sched_setparam", 6, 6},
{&__NR_sched_getparam, "sched_getparam", 6, 6},
{&__NR_sched_setscheduler, "sched_setscheduler", 6, 6},
{&__NR_sched_getscheduler, "sched_getscheduler", 6, 6},
{&__NR_sched_get_priority_max, "sched_get_priority_max", 6, 6},
{&__NR_sched_get_priority_min, "sched_get_priority_min", 6, 6},
{&__NR_sched_rr_get_interval, "sched_rr_get_interval", 6, 6},
{&__NR_vhangup, "vhangup", 6, 6},
{&__NR_modify_ldt, "modify_ldt", 6, 6},
{&__NR_pivot_root, "pivot_root", 6, 6},
{&__NR__sysctl, "_sysctl", 6, 6},
{&__NR_prctl, "prctl", 6, 6},
{&__NR_arch_prctl, "arch_prctl", 2, 2, INT, {INT, ULONG}},
{&__NR_adjtimex, "adjtimex", 6, 6},
{&__NR_umount2, "umount2", 6, 6},
{&__NR_swapon, "swapon", 6, 6},
{&__NR_swapoff, "swapoff", 6, 6},
{&__NR_sethostname, "sethostname", 6, 6},
{&__NR_setdomainname, "setdomainname", 6, 6},
{&__NR_iopl, "iopl", 6, 6},
{&__NR_ioperm, "ioperm", 6, 6},
{&__NR_init_module, "init_module", 6, 6},
{&__NR_delete_module, "delete_module", 6, 6},
{&__NR_gettid, "gettid", 6, 6},
{&__NR_readahead, "readahead", 6, 6},
{&__NR_setxattr, "setxattr", 6, 6},
{&__NR_fsetxattr, "fsetxattr", 6, 6},
{&__NR_getxattr, "getxattr", 6, 6},
{&__NR_fgetxattr, "fgetxattr", 6, 6},
{&__NR_listxattr, "listxattr", 6, 6},
{&__NR_flistxattr, "flistxattr", 6, 6},
{&__NR_removexattr, "removexattr", 6, 6},
{&__NR_fremovexattr, "fremovexattr", 6, 6},
{&__NR_lsetxattr, "lsetxattr", 6, 6},
{&__NR_lgetxattr, "lgetxattr", 6, 6},
{&__NR_llistxattr, "llistxattr", 6, 6},
{&__NR_lremovexattr, "lremovexattr", 6, 6},
{&__NR_sched_setaffinity, "sched_setaffinity", 6, 6},
{&__NR_sched_getaffinity, "sched_getaffinity", 6, 6},
{&__NR_cpuset_getaffinity, "cpuset_getaffinity", 6, 6},
{&__NR_cpuset_setaffinity, "cpuset_setaffinity", 6, 6},
{&__NR_io_setup, "io_setup", 6, 6},
{&__NR_io_destroy, "io_destroy", 6, 6},
{&__NR_io_getevents, "io_getevents", 6, 6},
{&__NR_io_submit, "io_submit", 6, 6},
{&__NR_io_cancel, "io_cancel", 6, 6},
{&__NR_lookup_dcookie, "lookup_dcookie", 6, 6},
{&__NR_epoll_create, "epoll_create", 6, 6},
{&__NR_epoll_wait, "epoll_wait", 6, 6},
{&__NR_epoll_ctl, "epoll_ctl", 6, 6},
{&__NR_getdents, "getdents64", 6, 6},
{&__NR_set_tid_address, "set_tid_address", 1, 1},
{&__NR_restart_syscall, "restart_syscall", 6, 6},
{&__NR_semtimedop, "semtimedop", 6, 6},
{&__NR_fadvise, "fadvise", 6, 6},
{&__NR_timer_create, "timer_create", 6, 6},
{&__NR_timer_settime, "timer_settime", 6, 6},
{&__NR_timer_gettime, "timer_gettime", 6, 6},
{&__NR_timer_getoverrun, "timer_getoverrun", 6, 6},
{&__NR_timer_delete, "timer_delete", 6, 6},
{&__NR_clock_settime, "clock_settime", 6, 6},
{&__NR_clock_gettime, "clock_gettime", 6, 6},
{&__NR_clock_getres, "clock_getres", 6, 6},
{&__NR_clock_nanosleep, "clock_nanosleep", 6, 6},
{&__NR_tgkill, "tgkill", 6, 6},
{&__NR_mbind, "mbind", 6, 6},
{&__NR_set_mempolicy, "set_mempolicy", 6, 6},
{&__NR_get_mempolicy, "get_mempolicy", 6, 6},
{&__NR_mq_open, "mq_open", 6, 6},
{&__NR_mq_unlink, "mq_unlink", 6, 6},
{&__NR_mq_timedsend, "mq_timedsend", 6, 6},
{&__NR_mq_timedreceive, "mq_timedreceive", 6, 6},
{&__NR_mq_notify, "mq_notify", 6, 6},
{&__NR_mq_getsetattr, "mq_getsetattr", 6, 6},
{&__NR_kexec_load, "kexec_load", 6, 6},
{&__NR_waitid, "waitid", 6, 6},
{&__NR_add_key, "add_key", 6, 6},
{&__NR_request_key, "request_key", 6, 6},
{&__NR_keyctl, "keyctl", 6, 6},
{&__NR_ioprio_set, "ioprio_set", 6, 6},
{&__NR_ioprio_get, "ioprio_get", 6, 6},
{&__NR_inotify_init, "inotify_init", 6, 6},
{&__NR_inotify_add_watch, "inotify_add_watch", 6, 6},
{&__NR_inotify_rm_watch, "inotify_rm_watch", 6, 6},
{&__NR_openat, "openat", 4, 4, INT, {INT, STR, INT, OCTAL|INT}},
{&__NR_mkdirat, "mkdirat", 3, 3, INT, {INT, STR, OCTAL|INT}},
{&__NR_fchownat, "fchownat", 6, 6},
{&__NR_utime, "utime", 6, 6},
{&__NR_utimes, "utimes", 6, 6},
{&__NR_futimesat, "futimesat", 6, 6},
{&__NR_futimes, "futimes", 6, 6},
{&__NR_futimens, "futimens", 6, 6},
{&__NR_fstatat, "newfstatat", 4, 2, INT, {INT, STR, STAT, INT}},
{&__NR_unlinkat, "unlinkat", 3, 3, INT, {INT, STR, INT}},
{&__NR_renameat, "renameat", 4, 4, INT, {INT, STR, INT, STR}},
{&__NR_linkat, "linkat", 6, 6},
{&__NR_symlinkat, "symlinkat", 6, 6},
{&__NR_readlinkat, "readlinkat", 6, 6},
{&__NR_fchmodat, "fchmodat", 6, 6},
{&__NR_faccessat, "faccessat", 4, 4, INT, {INT, STR, INT, INT}},
{&__NR_unshare, "unshare", 6, 6},
{&__NR_splice, "splice", 6, 6},
{&__NR_tee, "tee", 6, 6},
{&__NR_sync_file_range, "sync_file_range", 4, 4},
{&__NR_vmsplice, "vmsplice", 6, 6},
{&__NR_migrate_pages, "migrate_pages", 6, 6},
{&__NR_move_pages, "move_pages", 6, 6},
{&__NR_preadv, "preadv", 4, 1, LONG, {INT, IOV, ULONG, ULONG}},
{&__NR_pwritev, "pwritev", 6, 6, LONG, {INT, IOV, ULONG, ULONG}},
{&__NR_utimensat, "utimensat", 6, 6},
{&__NR_fallocate, "fallocate", 6, 6},
{&__NR_posix_fallocate, "posix_fallocate", 6, 6},
{&__NR_accept4, "accept4", 4, 4},
{&__NR_dup3, "dup3", 3, 3, INT},
{&__NR_epoll_pwait, "epoll_pwait", 6, 6},
{&__NR_epoll_create1, "epoll_create1", 6, 6},
{&__NR_perf_event_open, "perf_event_open", 6, 6},
{&__NR_inotify_init1, "inotify_init1", 6, 6},
{&__NR_rt_tgsigqueueinfo, "rt_tgsigqueueinfo", 6, 6},
{&__NR_signalfd, "signalfd", 6, 6},
{&__NR_signalfd4, "signalfd4", 6, 6},
{&__NR_eventfd, "eventfd", 6, 6},
{&__NR_eventfd2, "eventfd2", 6, 6},
{&__NR_timerfd_create, "timerfd_create", 6, 6},
{&__NR_timerfd_settime, "timerfd_settime", 6, 6},
{&__NR_timerfd_gettime, "timerfd_gettime", 6, 6},
{&__NR_recvmmsg, "recvmmsg", 6, 6},
{&__NR_fanotify_init, "fanotify_init", 6, 6},
{&__NR_fanotify_mark, "fanotify_mark", 6, 6},
{&__NR_prlimit, "prlimit", 6, 6},
{&__NR_name_to_handle_at, "name_to_handle_at", 6, 6},
{&__NR_open_by_handle_at, "open_by_handle_at", 6, 6},
{&__NR_clock_adjtime, "clock_adjtime", 6, 6},
{&__NR_syncfs, "syncfs", 6, 6},
{&__NR_sendmmsg, "sendmmsg", 6, 6},
{&__NR_setns, "setns", 6, 6},
{&__NR_getcpu, "getcpu", 6, 6},
{&__NR_process_vm_readv, "process_vm_readv", 6, 6},
{&__NR_process_vm_writev, "process_vm_writev", 6, 6},
{&__NR_kcmp, "kcmp", 6, 6},
{&__NR_finit_module, "finit_module", 6, 6},
{&__NR_sched_setattr, "sched_setattr", 6, 6},
{&__NR_sched_getattr, "sched_getattr", 6, 6},
{&__NR_renameat2, "renameat2", 6, 6},
{&__NR_seccomp, "seccomp", 6, 6},
{&__NR_getrandom, "getrandom", 6, 6},
{&__NR_memfd_create, "memfd_create", 6, 6},
{&__NR_kexec_file_load, "kexec_file_load", 6, 6},
{&__NR_bpf, "bpf", 6, 6},
{&__NR_execveat, "execveat", 6, 6},
{&__NR_userfaultfd, "userfaultfd", 6, 6},
{&__NR_membarrier, "membarrier", 6, 6},
{&__NR_mlock2, "mlock2", 6, 6},
{&__NR_copy_file_range, "copy_file_range", 6, 6},
{&__NR_preadv2, "preadv2", 6, 6},
{&__NR_pwritev2, "pwritev2", 6, 6},
{&__NR_pkey_mprotect, "pkey_mprotect", 6, 6},
{&__NR_pkey_alloc, "pkey_alloc", 6, 6},
{&__NR_pkey_free, "pkey_free", 6, 6},
{&__NR_statx, "statx", 6, 6},
{&__NR_io_pgetevents, "io_pgetevents", 6, 6},
{&__NR_rseq, "rseq", 6, 6},
{&__NR_pidfd_send_signal, "pidfd_send_signal", 6, 6},
{&__NR_io_uring_setup, "io_uring_setup", 6, 6},
{&__NR_io_uring_enter, "io_uring_enter", 6, 6},
{&__NR_io_uring_register, "io_uring_register", 6, 6},
// clang-format on
};
static const struct Errno {
long *number;
const char *name;
} kErrnos[] = {
{&ENOSYS, "ENOSYS"}, //
{&EPERM, "EPERM"}, //
{&ENOENT, "ENOENT"}, //
{&ESRCH, "ESRCH"}, //
{&EINTR, "EINTR"}, //
{&EIO, "EIO"}, //
{&ENXIO, "ENXIO"}, //
{&E2BIG, "E2BIG"}, //
{&ENOEXEC, "ENOEXEC"}, //
{&EBADF, "EBADF"}, //
{&ECHILD, "ECHILD"}, //
{&EAGAIN, "EAGAIN"}, //
{&ENOMEM, "ENOMEM"}, //
{&EACCES, "EACCES"}, //
{&EFAULT, "EFAULT"}, //
{&ENOTBLK, "ENOTBLK"}, //
{&EBUSY, "EBUSY"}, //
{&EEXIST, "EEXIST"}, //
{&EXDEV, "EXDEV"}, //
{&ENODEV, "ENODEV"}, //
{&ENOTDIR, "ENOTDIR"}, //
{&EISDIR, "EISDIR"}, //
{&EINVAL, "EINVAL"}, //
{&ENFILE, "ENFILE"}, //
{&EMFILE, "EMFILE"}, //
{&ENOTTY, "ENOTTY"}, //
{&ETXTBSY, "ETXTBSY"}, //
{&EFBIG, "EFBIG"}, //
{&ENOSPC, "ENOSPC"}, //
{&EDQUOT, "EDQUOT"}, //
{&ESPIPE, "ESPIPE"}, //
{&EROFS, "EROFS"}, //
{&EMLINK, "EMLINK"}, //
{&EPIPE, "EPIPE"}, //
{&EDOM, "EDOM"}, //
{&ERANGE, "ERANGE"}, //
{&EDEADLK, "EDEADLK"}, //
{&ENAMETOOLONG, "ENAMETOOLONG"}, //
{&ENOLCK, "ENOLCK"}, //
{&ENOTEMPTY, "ENOTEMPTY"}, //
{&ELOOP, "ELOOP"}, //
{&ENOMSG, "ENOMSG"}, //
{&EIDRM, "EIDRM"}, //
{&ETIME, "ETIME"}, //
{&EPROTO, "EPROTO"}, //
{&EOVERFLOW, "EOVERFLOW"}, //
{&EILSEQ, "EILSEQ"}, //
{&EUSERS, "EUSERS"}, //
{&ENOTSOCK, "ENOTSOCK"}, //
{&EDESTADDRREQ, "EDESTADDRREQ"}, //
{&EMSGSIZE, "EMSGSIZE"}, //
{&EPROTOTYPE, "EPROTOTYPE"}, //
{&ENOPROTOOPT, "ENOPROTOOPT"}, //
{&EPROTONOSUPPORT, "EPROTONOSUPPORT"}, //
{&ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT"}, //
{&ENOTSUP, "ENOTSUP"}, //
{&EOPNOTSUPP, "EOPNOTSUPP"}, //
{&EPFNOSUPPORT, "EPFNOSUPPORT"}, //
{&EAFNOSUPPORT, "EAFNOSUPPORT"}, //
{&EADDRINUSE, "EADDRINUSE"}, //
{&EADDRNOTAVAIL, "EADDRNOTAVAIL"}, //
{&ENETDOWN, "ENETDOWN"}, //
{&ENETUNREACH, "ENETUNREACH"}, //
{&ENETRESET, "ENETRESET"}, //
{&ECONNABORTED, "ECONNABORTED"}, //
{&ECONNRESET, "ECONNRESET"}, //
{&ENOBUFS, "ENOBUFS"}, //
{&EISCONN, "EISCONN"}, //
{&ENOTCONN, "ENOTCONN"}, //
{&ESHUTDOWN, "ESHUTDOWN"}, //
{&ETOOMANYREFS, "ETOOMANYREFS"}, //
{&ETIMEDOUT, "ETIMEDOUT"}, //
{&ECONNREFUSED, "ECONNREFUSED"}, //
{&EHOSTDOWN, "EHOSTDOWN"}, //
{&EHOSTUNREACH, "EHOSTUNREACH"}, //
{&EALREADY, "EALREADY"}, //
{&EINPROGRESS, "EINPROGRESS"}, //
{&ESTALE, "ESTALE"}, //
{&EREMOTE, "EREMOTE"}, //
{&EBADRPC, "EBADRPC"}, //
{&ERPCMISMATCH, "ERPCMISMATCH"}, //
{&EPROGUNAVAIL, "EPROGUNAVAIL"}, //
{&EPROGMISMATCH, "EPROGMISMATCH"}, //
{&EPROCUNAVAIL, "EPROCUNAVAIL"}, //
{&EFTYPE, "EFTYPE"}, //
{&EAUTH, "EAUTH"}, //
{&ENEEDAUTH, "ENEEDAUTH"}, //
{&EPROCLIM, "EPROCLIM"}, //
{&ENOATTR, "ENOATTR"}, //
{&EPWROFF, "EPWROFF"}, //
{&EDEVERR, "EDEVERR"}, //
{&EBADEXEC, "EBADEXEC"}, //
{&EBADARCH, "EBADARCH"}, //
{&ESHLIBVERS, "ESHLIBVERS"}, //
{&EBADMACHO, "EBADMACHO"}, //
{&ENOPOLICY, "ENOPOLICY"}, //
{&EBADMSG, "EBADMSG"}, //
{&ECANCELED, "ECANCELED"}, //
{&EOWNERDEAD, "EOWNERDEAD"}, //
{&ENOTRECOVERABLE, "ENOTRECOVERABLE"}, //
{&ENONET, "ENONET"}, //
{&ERESTART, "ERESTART"}, //
{&ENOSR, "ENOSR"}, //
{&ENOSTR, "ENOSTR"}, //
{&ENODATA, "ENODATA"}, //
{&EMULTIHOP, "EMULTIHOP"}, //
{&ENOLINK, "ENOLINK"}, //
{&ENOMEDIUM, "ENOMEDIUM"}, //
{&EMEDIUMTYPE, "EMEDIUMTYPE"}, //
{&EWOULDBLOCK, "EWOULDBLOCK"}, //
};
struct Pid {
int pid;
uint32_t hash;
bool insyscall;
struct Syscall *call;
struct Syscall fakecall;
struct user_regs_struct args;
char fakename[12];
};
struct PidList {
uint32_t i, n;
struct Pid *p;
};
char *ob; // output buffer
struct Pid *sp; // active subprocess
static uint32_t Hash(uint64_t pid) {
uint32_t hash;
hash = (pid * 6364136223846793005 + 1442695040888963407) >> 32;
if (!hash) hash = 1;
return hash;
}
static struct Pid *GetPid(struct PidList *list, int pid) {
uint32_t i, hash, step;
DCHECK_NE(0, pid);
DCHECK_LE(list->i, list->n >> 1);
if (list->n) {
i = 0;
step = 0;
hash = Hash(pid);
do {
i = (hash + step * (step + 1) / 2) & (list->n - 1);
if (list->p[i].pid == pid) {
return list->p + i;
}
step++;
} while (list->p[i].hash);
}
return 0;
}
static void ReservePid(struct PidList *list, int count) {
size_t i, j, step;
struct PidList old;
DCHECK_LE(list->i, list->n >> 1);
while (list->i + count >= (list->n >> 1)) {
memcpy(&old, list, sizeof(*list));
list->n = list->n ? list->n << 1 : 16;
list->p = calloc(list->n, sizeof(*list->p));
for (i = 0; i < old.n; ++i) {
if (!old.p[i].hash) continue;
step = 0;
do {
j = (old.p[i].hash + step * (step + 1) / 2) & (list->n - 1);
step++;
} while (list->p[j].hash);
list->p[j] = old.p[i];
}
free(old.p);
}
}
static struct Pid *AddPid(struct PidList *list, int pid) {
struct Pid *item;
uint32_t i, hash, step;
DCHECK_NE(0, pid);
DCHECK_LE(list->i, list->n >> 1);
if (!(item = GetPid(list, pid))) {
ReservePid(list, 1);
hash = Hash(pid);
++list->i;
i = 0;
step = 0;
do {
i = (hash + step * (step + 1) / 2) & (list->n - 1);
step++;
} while (list->p[i].hash);
list->p[i].hash = hash;
list->p[i].pid = pid;
item = list->p + i;
}
return item;
}
static void RemovePid(struct PidList *list, int pid) {
uint32_t i, hash, step;
DCHECK_NE(0, pid);
DCHECK_LE(list->i, list->n >> 1);
if (list->n) {
i = 0;
step = 0;
hash = Hash(pid);
do {
i = (hash + step * (step + 1) / 2) & (list->n - 1);
if (list->p[i].pid == pid) {
bzero(list->p + i, sizeof(*list->p));
list->i--;
assert(list->i < 99999);
return;
}
step++;
} while (list->p[i].hash);
}
}
static ssize_t WriteAll(int fd, const char *p, size_t n) {
ssize_t rc;
size_t i, got;
for (i = 0; i < n;) {
rc = write(fd, p + i, n - i);
if (rc != -1) {
got = rc;
i += got;
} else if (errno != EINTR) {
return -1;
}
}
return i;
}
static void Flush(void) {
WriteAll(2, ob, appendz(ob).i);
appendr(&ob, 0);
}
static const char *GetErrnoName(int x) {
const char *s;
static char buf[16];
if ((s = strerror_short(x))) return s;
int64toarray_radix10(x, buf);
return buf;
}
static struct Syscall *GetSyscall(int x) {
int i;
if (x >= 0) {
for (i = 0; i < ARRAYLEN(kSyscalls); ++i) {
if (x == *kSyscalls[i].number) {
return kSyscalls + i;
}
}
}
return NULL;
}
static char *PeekString(unsigned long x) {
union {
char buf[8];
long word;
} u;
char *p;
unsigned offset;
unsigned long addr, i, n;
if (!x) return NULL;
addr = ROUNDDOWN(x, 8);
offset = x - addr;
errno = 0;
u.word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr);
if (errno) return 0;
n = strnlen(u.buf + offset, 8 - offset);
p = calloc(1, n);
memcpy(p, u.buf + offset, n);
if (n == 8 - offset) {
do {
addr += 8;
errno = 0;
u.word = ptrace(PTRACE_PEEKDATA, sp->pid, addr);
if (errno) break;
i = strnlen(u.buf, 8);
p = realloc(p, n + i);
memcpy(p + n, u.buf, i);
n += i;
} while (i == 8);
}
p = realloc(p, n + 1);
p[n] = 0;
return p;
}
static char *PrintString(char *s) {
kappendf(&ob, "%#s", s);
return s;
}
static void *PeekData(unsigned long x, size_t size) {
union {
char buf[8];
long word;
} u;
char *p;
unsigned offset;
unsigned long addr, i, n;
if (!x) return NULL;
addr = ROUNDDOWN(x, 8);
offset = x - addr;
errno = 0;
u.word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr);
if (errno) return 0;
p = calloc(1, size);
memcpy(p, u.buf + offset, MIN(size, 8 - offset));
for (i = 8 - offset; i < size; i += MIN(8, size - i)) {
addr += 8;
errno = 0;
u.word = ptrace(PTRACE_PEEKDATA, sp->pid, addr);
if (errno) break;
memcpy(p + i, u.buf, MIN(8, size - i));
}
return p;
}
static char *PrintData(char *data, size_t size) {
kappendf(&ob, "%#.*hhs%s", MIN(40, size), data, size > 40 ? "..." : "", data);
return data;
}
static struct iovec *PeekIov(unsigned long addr, int len) {
int i;
char *p;
long word;
struct iovec *iov;
if (!addr) return NULL;
p = calloc(1, len * 16);
for (i = 0; i < len * 2; ++i, addr += sizeof(word)) {
errno = 0;
word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr);
if (errno) break;
memcpy(p + i * sizeof(word), &word, sizeof(word));
}
iov = (struct iovec *)p;
for (i = 0; i < len; ++i) {
errno = 0;
iov[i].iov_base = PeekData((long)iov[i].iov_base, iov[i].iov_len);
if (errno) break;
}
return iov;
}
static struct iovec *PrintIov(struct iovec *iov, int iovlen) {
int i;
if (iov) {
appendw(&ob, '{');
for (i = 0; i < iovlen; ++i) {
if (i) appendw(&ob, READ16LE(", "));
appendw(&ob, '{');
PrintData(iov[i].iov_base, iov[i].iov_len);
kappendf(&ob, ", %#lx}", iov[i].iov_len);
}
appendw(&ob, '}');
} else {
appendw(&ob, READ32LE("NULL"));
}
return iov;
}
static void FreeIov(struct iovec *iov, int iovlen) {
int i;
if (iov) {
for (i = 0; i < iovlen; ++i) {
free(iov[i].iov_base);
}
free(iov);
}
}
static char **PeekStringList(unsigned long addr) {
char *p;
long word;
size_t i, n;
char **list;
if (!addr) return NULL;
for (p = NULL, n = 0;; ++n) {
p = realloc(p, (n + 1) * sizeof(word));
word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr + n * sizeof(word));
memcpy(p + n * sizeof(word), &word, sizeof(word));
if (!word) break;
}
list = (char **)p;
for (i = 0; i < n; ++i) {
list[i] = PeekString((long)list[i]);
}
return list;
}
static char **PrintStringList(char **list) {
int i;
if (list) {
appendw(&ob, '{');
for (i = 0;; ++i) {
if (i) appendw(&ob, READ16LE(", "));
PrintString(list[i]);
if (!list[i]) break;
}
appendw(&ob, '}');
} else {
appendw(&ob, READ32LE("NULL"));
}
return list;
}
static void FreeStringList(char **list) {
int i;
if (list) {
for (i = 0; list[i]; ++i) {
free(list[i]);
}
free(list);
}
}
static void PrintPipe(unsigned long addr) {
unsigned long word;
word = ptrace(PTRACE_PEEKDATA, sp->pid, addr);
kappendf(&ob, "[{%d, %d}]", (int)word, (int)(word >> 32));
}
static struct stat *PeekStat(unsigned long addr) {
int i;
char *p;
long word;
if (!addr) return NULL;
p = calloc(1, sizeof(struct stat));
for (i = 0; i < sizeof(struct stat); i += sizeof(word)) {
errno = 0;
word = ptrace(PTRACE_PEEKTEXT, sp->pid, addr + i);
if (errno) break;
memcpy(p + i, &word, sizeof(word));
}
return (struct stat *)p;
}
static struct stat *PrintStat(struct stat *st) {
bool printed;
printed = false;
appendw(&ob, '{');
if (st->st_size) {
kappendf(&ob, ".st_size = %#lx", st->st_size);
printed = true;
}
if (st->st_mode) {
if (printed) appendw(&ob, READ16LE(", "));
kappendf(&ob, ".st_mode = %#o", st->st_mode);
printed = true;
}
appendw(&ob, '}');
return st;
}
static void PrintSigset(unsigned long p) {
kappendf(&ob, "{%#lx}", ptrace(PTRACE_PEEKTEXT, sp->pid, p));
}
static void PrintSyscallArg(int type, unsigned long x, unsigned long y) {
char *s;
switch (type & 31) {
case ULONG:
kappendf(&ob, "%#lx", x);
break;
case INT:
if (type & OCTAL) {
kappendf(&ob, "%#o", x);
} else {
kappendf(&ob, "%d", x);
}
break;
case LONG:
kappendf(&ob, "%ld", x);
break;
case STR:
free(PrintString(PeekString(x)));
break;
case BUF:
free(PrintData(PeekData(x, MIN(32, y)), MIN(32, y)));
break;
case IOV:
FreeIov(PrintIov(PeekIov(x, y), y), y);
break;
case STAT:
free(PrintStat(PeekStat(x)));
break;
case PIPE:
PrintPipe(x);
break;
case SIG:
appends(&ob, strsignal(x));
break;
case SIGSET:
PrintSigset(x);
break;
case STRLIST:
FreeStringList(PrintStringList(PeekStringList(x)));
break;
case INTPTR:
if (x) {
s = PeekData(x, 4);
kappendf(&ob, "{%d}", READ32LE(s));
free(s);
}
break;
default:
kappendf(&ob, "wut[%'ld]", x);
break;
}
}
static void PrintSyscall(bool issecond) {
int a, b, eager, arity;
bool gotsome, isresuming, isunfinished;
gotsome = false;
if (issecond) {
if (!sp->call) {
return;
}
} else {
if (!(sp->call = GetSyscall(sp->args.orig_rax))) {
ksnprintf(sp->fakename, sizeof(sp->fakename), "%d", sp->args.orig_rax);
bzero(&sp->fakecall, sizeof(sp->fakecall));
sp->call = &sp->fakecall;
sp->call->name = sp->fakename;
sp->call->arity = 6;
sp->call->eager = 6;
}
if (sp->args.orig_rax == __NR_execve) {
if (sp->args.rax == -ENOSYS) {
sp->args.rax = 0;
}
} else if (sp->args.rax == -ENOSYS) {
return;
}
}
isresuming = false;
isunfinished = false;
if (!isunfinished) {
a = 0;
b = sp->call->arity;
} else if (!isresuming) {
a = 0;
b = sp->call->eager;
} else {
a = sp->call->eager;
b = sp->call->arity;
}
kappendf(&ob, PROLOGUE " %s%s(", sp->pid, isresuming ? "<resuming> " : "",
sp->call->name);
if (a <= 0 && 0 < b) {
if (gotsome) appendw(&ob, READ16LE(", "));
PrintSyscallArg(sp->call->arg[0], sp->args.rdi, sp->args.rsi);
gotsome = true;
}
if (a <= 1 && 1 < b) {
if (gotsome) appendw(&ob, READ16LE(", "));
PrintSyscallArg(sp->call->arg[1], sp->args.rsi, sp->args.rdx);
gotsome = true;
}
if (a <= 2 && 2 < b) {
if (gotsome) appendw(&ob, READ16LE(", "));
PrintSyscallArg(sp->call->arg[2], sp->args.rdx, sp->args.r10);
gotsome = true;
}
if (a <= 3 && 3 < b) {
if (gotsome) appendw(&ob, READ16LE(", "));
PrintSyscallArg(sp->call->arg[3], sp->args.r10, sp->args.r8);
gotsome = true;
}
if (a <= 4 && 4 < b) {
if (gotsome) appendw(&ob, READ16LE(", "));
PrintSyscallArg(sp->call->arg[4], sp->args.r8, sp->args.r9);
gotsome = true;
}
if (a <= 5 && 5 < b) {
if (gotsome) appendw(&ob, READ16LE(", "));
PrintSyscallArg(sp->call->arg[5], sp->args.r9, 0);
gotsome = true;
}
if (isunfinished) {
appends(&ob, ") → <unfinished>");
} else {
appends(&ob, ") → ");
if (sp->args.rax > (unsigned long)-4096) {
kappendf(&ob, "-1 %s", GetErrnoName(-sp->args.rax));
} else {
PrintSyscallArg(sp->call->ret, sp->args.rax, 0);
}
}
kappendf(&ob, "%n");
Flush();
sp->call = 0;
}
wontreturn void PropagateExit(int wstatus) {
exit(WEXITSTATUS(wstatus));
}
wontreturn void PropagateTermination(int wstatus) {
sigset_t mask;
// if signal that terminated program was sent to our whole
// process group, then that signal should be delivered and kill
// this process the moment it's unblocked here. we only unblock
// here because if the child process ignores the signal and does
// exit(0) then we want to propagate that intent too.
sigemptyset(&mask);
sigaddset(&mask, WTERMSIG(wstatus));
sigprocmask(SIG_UNBLOCK, &mask, 0);
// otherwise we can propagate it by the exit code convention
// commonly used with shell scripts
exit(128 + WTERMSIG(wstatus));
}
wontreturn void StraceMain(int argc, char *argv[]) {
unsigned long msg;
struct siginfo si;
struct Pid *s, *child;
struct PidList pidlist;
sigset_t mask, origmask;
int i, sig, evpid, root, wstatus, signal;
struct sigaction sigign, saveint, savequit;
if (!IsLinux()) {
kprintf("error: ptrace() is only supported on linux right now%n");
exit(1);
}
if (IsModeDbg()) ShowCrashReports();
if (argc < 2) {
kprintf("Usage: %s PROGRAM [ARGS...]%n", argv[0]);
exit(1);
}
pidlist.i = 0;
pidlist.n = 0;
pidlist.p = 0;
sigign.sa_flags = 0;
sigign.sa_handler = SIG_IGN;
sigemptyset(&sigign.sa_mask);
sigaction(SIGINT, &sigign, &saveint);
sigaction(SIGQUIT, &sigign, &savequit);
sigemptyset(&mask);
/* sigaddset(&mask, SIGCHLD); */
sigprocmask(SIG_BLOCK, &mask, &origmask);
CHECK_NE(-1, (root = fork()));
if (!root) {
sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0);
sigprocmask(SIG_SETMASK, &origmask, 0);
ptrace(PTRACE_TRACEME);
execvp(argv[1], argv + 1);
_Exit(127);
}
sp = AddPid(&pidlist, root);
// wait for ptrace(PTRACE_TRACEME) to be called
CHECK_EQ(sp->pid, waitpid(sp->pid, &wstatus, 0));
// configure linux process tracing
CHECK_NE(-1, ptrace(PTRACE_SETOPTIONS, sp->pid, 0,
(PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK |
PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC |
PTRACE_O_TRACEEXIT | PTRACE_O_TRACESYSGOOD)));
// continue child process setting breakpoint at next system call
CHECK_NE(-1, ptrace(PTRACE_SYSCALL, sp->pid, 0, 0));
for (;;) {
CHECK_NE(-1, (evpid = waitpid(-1, &wstatus, __WALL)));
// prevent rehash in second addpid call
ReservePid(&pidlist, 2);
// we can get wait notifications before fork/vfork/etc. events
sp = AddPid(&pidlist, evpid);
// handle actual exit
if (WIFEXITED(wstatus)) {
kprintf(PROLOGUE " exited with %d%n", sp->pid, WEXITSTATUS(wstatus));
RemovePid(&pidlist, sp->pid);
sp = 0;
// we exit when the last process being monitored exits
if (!pidlist.i) {
PropagateExit(wstatus);
} else {
continue;
}
}
// handle actual kill
if (WIFSIGNALED(wstatus)) {
kprintf(PROLOGUE " exited with signal %G%n", sp->pid, WTERMSIG(wstatus));
RemovePid(&pidlist, sp->pid);
sp = 0;
// we die when the last process being monitored dies
if (!pidlist.i) {
PropagateTermination(wstatus);
} else {
continue;
}
}
// handle core dump
if (WCOREDUMP(wstatus)) {
kprintf(PROLOGUE " exited with core%n", sp->pid);
RemovePid(&pidlist, sp->pid);
sp = 0;
// we die when the last process being monitored dies
if (!pidlist.i) {
PropagateTermination(wstatus);
} else {
continue;
}
}
// handle trace events
sig = 0;
signal = (wstatus >> 8) & 0xffff;
assert(WIFSTOPPED(wstatus));
if (signal == SIGTRAP | PTRACE_EVENT_STOP) {
CHECK_NE(-1, ptrace(PTRACE_GETSIGINFO, sp->pid, 0, &si));
if (si.si_code == SIGTRAP || si.si_code == SIGTRAP | 0x80) {
CHECK_NE(-1, ptrace(PTRACE_GETREGS, sp->pid, 0, &sp->args));
PrintSyscall(sp->insyscall);
sp->insyscall = !sp->insyscall;
ptrace(PTRACE_SYSCALL, sp->pid, 0, 0);
} else {
sig = signal & 127;
kappendf(&ob, PROLOGUE " got signal %G%n", sp->pid, sig);
Flush();
ptrace(PTRACE_SYSCALL, sp->pid, 0, sig);
}
} else if (signal == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) {
CHECK_NE(-1, ptrace(PTRACE_GETEVENTMSG, sp->pid, 0, &msg));
sig = WSTOPSIG(wstatus);
ptrace(PTRACE_SYSCALL, sp->pid, 0, 0);
} else if (signal == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) {
CHECK_NE(-1, ptrace(PTRACE_GETEVENTMSG, sp->pid, 0, &msg));
ptrace(PTRACE_SYSCALL, sp->pid, 0, 0);
} else if (signal == (SIGTRAP | (PTRACE_EVENT_FORK << 8)) ||
signal == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)) ||
signal == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
CHECK_NE(-1, ptrace(PTRACE_GETEVENTMSG, evpid, 0, &msg));
child = AddPid(&pidlist, msg);
child->pid = msg;
if (signal == (SIGTRAP | (PTRACE_EVENT_FORK << 8))) {
kappendf(&ob, PROLOGUE " fork() → %d%n", sp->pid, child->pid);
} else if (signal == (SIGTRAP | (PTRACE_EVENT_VFORK << 8))) {
kappendf(&ob, PROLOGUE " vfork() → %d%n", sp->pid, child->pid);
} else {
kappendf(&ob, PROLOGUE " clone() → %d%n", sp->pid, child->pid);
}
Flush();
ptrace(PTRACE_SYSCALL, child->pid, 0, 0);
ptrace(PTRACE_SYSCALL, sp->pid, 0, 0);
} else {
kappendf(&ob, PROLOGUE " gottish signal %G%n", sp->pid, sig);
Flush();
ptrace(PTRACE_SYSCALL, sp->pid, 0, signal & 127);
}
}
}
int main(int argc, char *argv[]) {
__nomultics = true;
StraceMain(argc, argv);
}