diff options
Diffstat (limited to 'usr.bin/truss')
-rw-r--r-- | usr.bin/truss/amd64-fbsd.c | 292 | ||||
-rw-r--r-- | usr.bin/truss/amd64-fbsd32.c | 297 | ||||
-rw-r--r-- | usr.bin/truss/amd64-linux32.c | 278 | ||||
-rw-r--r-- | usr.bin/truss/arm-fbsd.c | 318 | ||||
-rw-r--r-- | usr.bin/truss/extern.h | 43 | ||||
-rw-r--r-- | usr.bin/truss/i386-fbsd.c | 282 | ||||
-rw-r--r-- | usr.bin/truss/i386-linux.c | 276 | ||||
-rw-r--r-- | usr.bin/truss/ia64-fbsd.c | 272 | ||||
-rw-r--r-- | usr.bin/truss/main.c | 249 | ||||
-rw-r--r-- | usr.bin/truss/mips-fbsd.c | 350 | ||||
-rw-r--r-- | usr.bin/truss/powerpc-fbsd.c | 334 | ||||
-rw-r--r-- | usr.bin/truss/powerpc64-fbsd.c | 298 | ||||
-rw-r--r-- | usr.bin/truss/setup.c | 527 | ||||
-rw-r--r-- | usr.bin/truss/sparc64-fbsd.c | 320 | ||||
-rw-r--r-- | usr.bin/truss/syscall.h | 22 | ||||
-rw-r--r-- | usr.bin/truss/syscalls.c | 1482 | ||||
-rw-r--r-- | usr.bin/truss/truss.1 | 4 | ||||
-rw-r--r-- | usr.bin/truss/truss.h | 61 |
18 files changed, 1926 insertions, 3779 deletions
diff --git a/usr.bin/truss/amd64-fbsd.c b/usr.bin/truss/amd64-fbsd.c index d0be3e6..ade9322 100644 --- a/usr.bin/truss/amd64-fbsd.c +++ b/usr.bin/truss/amd64-fbsd.c @@ -29,311 +29,103 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/amd64-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ +/* FreeBSD/amd64-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <sys/syscall.h> #include <machine/reg.h> #include <machine/psl.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in amd64/amd64/trap.c - * is ever changed these functions need to keep up. - */ - -void -amd64_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +amd64_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, reg, syscall_num; + u_int i, reg; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over %rax if it contains + * either of these values. */ reg = 0; - syscall_num = regs.r_rax; - switch (syscall_num) { + switch (regs.r_rax) { case SYS_syscall: case SYS___syscall: - syscall_num = regs.r_rdi; reg++; break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - for (i = 0; i < nargs && reg < 6; i++, reg++) { + for (i = 0; i < narg && reg < 6; i++, reg++) { switch (reg) { - case 0: fsc->args[i] = regs.r_rdi; break; - case 1: fsc->args[i] = regs.r_rsi; break; - case 2: fsc->args[i] = regs.r_rdx; break; - case 3: fsc->args[i] = regs.r_rcx; break; - case 4: fsc->args[i] = regs.r_r8; break; - case 5: fsc->args[i] = regs.r_r9; break; + case 0: cs->args[i] = regs.r_rdi; break; + case 1: cs->args[i] = regs.r_rsi; break; + case 2: cs->args[i] = regs.r_rdx; break; + case 3: cs->args[i] = regs.r_rcx; break; + case 4: cs->args[i] = regs.r_r8; break; + case 5: cs->args[i] = regs.r_r9; break; } } - if (nargs > i) { + if (narg > i) { iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.r_rsp + sizeof(register_t)); - iorequest.piod_addr = &fsc->args[i]; - iorequest.piod_len = (nargs - i) * sizeof(register_t); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(register_t); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; + return (-1); } - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%lx%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -amd64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +amd64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_rax; - errorp = !!(regs.r_rflags & PSL_C); - - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ + retval[0] = regs.r_rax; + retval[1] = regs.r_rdx; + *errorp = !!(regs.r_rflags & PSL_C); + return (0); +} - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); +static struct procabi amd64_fbsd = { + "FreeBSD ELF64", + syscallnames, + nitems(syscallnames), + amd64_fetch_args, + amd64_fetch_retval +}; - return (retval); -} +PROCABI(amd64_fbsd); diff --git a/usr.bin/truss/amd64-fbsd32.c b/usr.bin/truss/amd64-fbsd32.c index 74d45a2..84a4b6b 100644 --- a/usr.bin/truss/amd64-fbsd32.c +++ b/usr.bin/truss/amd64-fbsd32.c @@ -29,312 +29,109 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/i386-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ +/* FreeBSD/i386-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <sys/syscall.h> #include <machine/reg.h> #include <machine/psl.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "freebsd32_syscalls.h" -static int nsyscalls = sizeof(freebsd32_syscallnames) / - sizeof(freebsd32_syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd32_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - unsigned int *args32; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd32_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct freebsd32_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd32_syscall *fsc) -{ - int i; - - free(fsc->args); - free(fsc->args32); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -amd64_fbsd32_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +amd64_fbsd32_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd32_syscall *fsc; - struct syscall *sc; - lwpid_t tid; + struct current_syscall *cs; + unsigned int args32[narg]; unsigned long parm_offset; - int i, syscall_num; + lwpid_t tid; + u_int i; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } parm_offset = regs.r_rsp + sizeof(int); /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - syscall_num = regs.r_rax; - switch (syscall_num) { + switch (regs.r_rax) { case SYS_syscall: - syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); parm_offset += sizeof(int); break; case SYS___syscall: - syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); parm_offset += sizeof(quad_t); break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : freebsd32_syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args32 = malloc((1 + nargs) * sizeof(unsigned int)); iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)parm_offset; - iorequest.piod_addr = fsc->args32; - iorequest.piod_len = (1 + nargs) * sizeof(unsigned int); + iorequest.piod_addr = args32; + iorequest.piod_len = sizeof(args32); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); - if (iorequest.piod_len == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - for (i = 0; i < nargs + 1; i++) - fsc->args[i] = fsc->args32[i]; - - sc = NULL; - if (fsc->name) - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif + if (iorequest.piod_len == 0) { + return (-1); } -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "freebsd32_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "freebsd32_execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + for (i = 0; i < narg; i++) + cs->args[i] = args32[i]; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -amd64_fbsd32_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +amd64_fbsd32_fetch_retval(struct trussinfo *trussinfo, long *retval, + int *errorp) { struct reg regs; - struct freebsd32_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_rax; - errorp = !!(regs.r_rflags & PSL_C); - - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } + retval[0] = regs.r_rax & 0xffffffff; + retval[1] = regs.r_rdx & 0xffffffff; + *errorp = !!(regs.r_rflags & PSL_C); + return (0); +} - if (fsc->name != NULL && (strcmp(fsc->name, "freebsd32_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; +static struct procabi amd64_fbsd32 = { + "FreeBSD ELF32", + freebsd32_syscallnames, + nitems(freebsd32_syscallnames), + amd64_fbsd32_fetch_args, + amd64_fbsd32_fetch_retval +}; - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ +PROCABI(amd64_fbsd32); - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); +static struct procabi amd64_fbsd32_aout = { + "FreeBSD a.out", + freebsd32_syscallnames, + nitems(freebsd32_syscallnames), + amd64_fbsd32_fetch_args, + amd64_fbsd32_fetch_retval +}; - return (retval); -} +PROCABI(amd64_fbsd32_aout); diff --git a/usr.bin/truss/amd64-linux32.c b/usr.bin/truss/amd64-linux32.c index 2c025a3..4f64af3 100644 --- a/usr.bin/truss/amd64-linux32.c +++ b/usr.bin/truss/amd64-linux32.c @@ -29,124 +29,36 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * Linux/i386-specific system call handling. Given how much of this code - * is taken from the freebsd equivalent, I can probably put even more of - * it in support routines that can be used by any personality support. - */ +/* Linux/i386-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <machine/reg.h> #include <machine/psl.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "linux32_syscalls.h" -static int nsyscalls = - sizeof(linux32_syscallnames) / sizeof(linux32_syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct linux_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long args[5]; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct linux_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct linux_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct linux_syscall *fsc) -{ - int i; - - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -amd64_linux32_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +amd64_linux32_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct reg regs; - struct linux_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; - } - - syscall_num = regs.r_rax; - - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : linux32_syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); + return (-1); } - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "linux_fork") == 0 || - strcmp(fsc->name, "linux_vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - /* * Linux passes syscall arguments in registers, not * on the stack. Fortunately, we've got access to the @@ -154,82 +66,22 @@ amd64_linux32_syscall_entry(struct trussinfo *trussinfo, int nargs) * number of arguments. And what does linux do for syscalls * that have more than five arguments? */ - - fsc->args[0] = regs.r_rbx; - fsc->args[1] = regs.r_rcx; - fsc->args[2] = regs.r_rdx; - fsc->args[3] = regs.r_rsi; - fsc->args[4] = regs.r_rdi; - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif + switch (narg) { + default: + cs->args[5] = regs.r_rbp; /* Unconfirmed */ + case 5: + cs->args[4] = regs.r_rdi; + case 4: + cs->args[3] = regs.r_rsi; + case 3: + cs->args[2] = regs.r_rdx; + case 2: + cs->args[1] = regs.r_rcx; + case 1: + cs->args[0] = regs.r_rbx; } -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "linux_execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } /* @@ -247,83 +99,43 @@ static const int bsd_to_linux_errno[] = { -6, }; -long -amd64_linux32_syscall_exit(struct trussinfo *trussinfo, - int syscall_num __unused) +static int +amd64_linux32_fetch_retval(struct trussinfo *trussinfo, long *retval, + int *errorp) { struct reg regs; - struct linux_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); + size_t i; tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_rax; - errorp = !!(regs.r_rflags & PSL_C); + retval[0] = regs.r_rax & 0xffffffff; + retval[1] = regs.r_rdx & 0xffffffff; + *errorp = !!(regs.r_rflags & PSL_C); - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; + if (*errorp) { + for (i = 0; i < nitems(bsd_to_linux_errno); i++) { + if (retval[0] == bsd_to_linux_errno[i]) { + retval[0] = i; + return (0); } } - } - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - if (errorp) { - for (i = 0; - (size_t)i < sizeof(bsd_to_linux_errno) / sizeof(int); i++) { - if (retval == bsd_to_linux_errno[i]) - break; - } + /* XXX: How to handle unknown errors? */ } + return (0); +} - if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - errorp ? i : retval, fsc->sc); - free_fsc(fsc); +static struct procabi amd64_linux32 = { + "Linux ELF32", + linux32_syscallnames, + nitems(linux32_syscallnames), + amd64_linux32_fetch_args, + amd64_linux32_fetch_retval +}; - return (retval); -} +PROCABI(amd64_linux32); diff --git a/usr.bin/truss/arm-fbsd.c b/usr.bin/truss/arm-fbsd.c index 2aea4e4..4b8222a 100644 --- a/usr.bin/truss/arm-fbsd.c +++ b/usr.bin/truss/arm-fbsd.c @@ -29,17 +29,11 @@ * SUCH DAMAGE. */ -/* - * FreeBSD/arm-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ - #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include <sys/types.h> + +/* FreeBSD/arm-specific system call handling. */ + #include <sys/ptrace.h> #include <sys/syscall.h> @@ -47,324 +41,98 @@ __FBSDID("$FreeBSD$"); #include <machine/armreg.h> #include <machine/ucontext.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <err.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" - -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -arm_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +arm_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; - register_t *ap; + u_int i, reg, syscall_num; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } - ap = ®s.r[0]; /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ #ifdef __ARM_EABI__ syscall_num = regs.r[7]; #else - if ((syscall_num = ptrace(PT_READ_I, tid, + if ((syscall_num = ptrace(PT_READ_I, tid, (caddr_t)(regs.r[_REG_PC] - INSN_SIZE), 0)) == -1) { fprintf(trussinfo->outfile, "-- CANNOT READ PC --\n"); - return; + return (-1); } syscall_num = syscall_num & 0x000fffff; #endif + + reg = 0; switch (syscall_num) { case SYS_syscall: - syscall_num = *ap++; - nargs--; + reg = 1; break; case SYS___syscall: - syscall_num = ap[_QUAD_LOWWORD]; - ap += 2; - nargs -= 2; + reg = 2; break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - switch (nargs) { - default: - /* - * The OS doesn't seem to allow more than 10 words of - * parameters (yay!). So we shouldn't be here. - */ - warn("More than 10 words (%d) of arguments!\n", nargs); - break; - case 10: - case 9: - case 8: - case 7: - case 6: - case 5: - /* - * If there are 7-10 words of arguments, they are placed - * on the stack, as is normal for other processors. - * The fall-through for all of these is deliberate!!! - */ - // XXX BAD constant used here + for (i = 0; i < narg && reg < 4; i++, reg++) + cs->args[i] = regs.r[reg]; + if (narg > i) { iorequest.piod_op = PIOD_READ_D; - iorequest.piod_offs = (void *)(regs.r[_REG_SP] + + iorequest.piod_offs = (void *)(regs.r_sp + 4 * sizeof(uint32_t)); - iorequest.piod_addr = &fsc->args[4]; - iorequest.piod_len = (nargs - 4) * sizeof(fsc->args[0]); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - case 4: fsc->args[3] = ap[3]; - case 3: fsc->args[2] = ap[2]; - case 2: fsc->args[1] = ap[1]; - case 1: fsc->args[0] = ap[0]; - case 0: break; + return (-1); } - sc = NULL; - if (fsc->name) - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -arm_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +arm_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r[0]; - errorp = !!(regs.r_cpsr & PSR_C); - - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ + /* XXX: Does not have the __ARMEB__ handling for __syscall(). */ + retval[0] = regs.r[0]; + retval[1] = regs.r[1]; + *errorp = !!(regs.r_cpsr & PSR_C); + return (0); +} - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); +static struct procabi arm_fbsd = { + "FreeBSD ELF32", + syscallnames, + nitems(syscallnames), + arm_fetch_args, + arm_fetch_retval +}; - return (retval); -} +PROCABI(arm_fbsd); diff --git a/usr.bin/truss/extern.h b/usr.bin/truss/extern.h index c60c6c7..708d055 100644 --- a/usr.bin/truss/extern.h +++ b/usr.bin/truss/extern.h @@ -31,46 +31,9 @@ * $FreeBSD$ */ -extern int setup_and_wait(char **); -extern int start_tracing(pid_t); +extern void setup_and_wait(struct trussinfo *, char **); +extern void start_tracing(struct trussinfo *, pid_t); extern void restore_proc(int); -extern void waitevent(struct trussinfo *); +extern void eventloop(struct trussinfo *); extern const char *ioctlname(unsigned long val); extern char *strsig(int sig); -#ifdef __arm__ -extern void arm_syscall_entry(struct trussinfo *, int); -extern long arm_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __amd64__ -extern void amd64_syscall_entry(struct trussinfo *, int); -extern long amd64_syscall_exit(struct trussinfo *, int); -extern void amd64_linux32_syscall_entry(struct trussinfo *, int); -extern long amd64_linux32_syscall_exit(struct trussinfo *, int); -extern void amd64_fbsd32_syscall_entry(struct trussinfo *, int); -extern long amd64_fbsd32_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __i386__ -extern void i386_syscall_entry(struct trussinfo *, int); -extern long i386_syscall_exit(struct trussinfo *, int); -extern void i386_linux_syscall_entry(struct trussinfo *, int); -extern long i386_linux_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __ia64__ -extern void ia64_syscall_entry(struct trussinfo *, int); -extern long ia64_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __powerpc__ -extern void powerpc_syscall_entry(struct trussinfo *, int); -extern long powerpc_syscall_exit(struct trussinfo *, int); -extern void powerpc64_syscall_entry(struct trussinfo *, int); -extern long powerpc64_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __sparc64__ -extern void sparc64_syscall_entry(struct trussinfo *, int); -extern long sparc64_syscall_exit(struct trussinfo *, int); -#endif -#ifdef __mips__ -extern void mips_syscall_entry(struct trussinfo *, int); -extern long mips_syscall_exit(struct trussinfo *, int); -#endif - diff --git a/usr.bin/truss/i386-fbsd.c b/usr.bin/truss/i386-fbsd.c index 89879d2..029815e 100644 --- a/usr.bin/truss/i386-fbsd.c +++ b/usr.bin/truss/i386-fbsd.c @@ -29,305 +29,103 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/i386-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ +/* FreeBSD/i386-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <sys/syscall.h> #include <machine/reg.h> #include <machine/psl.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -i386_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +i386_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; unsigned int parm_offset; - int i, syscall_num; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } parm_offset = regs.r_esp + sizeof(int); /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - syscall_num = regs.r_eax; - switch (syscall_num) { + switch (regs.r_eax) { case SYS_syscall: - syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); parm_offset += sizeof(int); break; case SYS___syscall: - syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0); parm_offset += sizeof(quad_t); break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)parm_offset; - iorequest.piod_addr = fsc->args; - iorequest.piod_len = (1 + nargs) * sizeof(unsigned long); + iorequest.piod_addr = cs->args; + iorequest.piod_len = narg * sizeof(unsigned long); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - - sc = NULL; - if (fsc->name) - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif + return (-1); - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +i386_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_eax; - errorp = !!(regs.r_eflags & PSL_C); - - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ + retval[0] = regs.r_eax; + retval[1] = regs.r_edx; + *errorp = !!(regs.r_eflags & PSL_C); + return (0); +} - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } +static struct procabi i386_fbsd = { + "FreeBSD ELF32", + syscallnames, + nitems(syscallnames), + i386_fetch_args, + i386_fetch_retval +}; - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; +PROCABI(i386_fbsd); - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ +static struct procabi i386_fbsd_aout = { + "FreeBSD a.out", + syscallnames, + nitems(syscallnames), + i386_fetch_args, + i386_fetch_retval +}; - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); +PROCABI(i386_fbsd_aout); - return (retval); -} diff --git a/usr.bin/truss/i386-linux.c b/usr.bin/truss/i386-linux.c index 84f1451..fa57af2 100644 --- a/usr.bin/truss/i386-linux.c +++ b/usr.bin/truss/i386-linux.c @@ -29,124 +29,36 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * Linux/i386-specific system call handling. Given how much of this code - * is taken from the freebsd equivalent, I can probably put even more of - * it in support routines that can be used by any personality support. - */ +/* Linux/i386-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <machine/reg.h> #include <machine/psl.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "linux_syscalls.h" -static int nsyscalls = - sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct linux_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long args[5]; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct linux_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct linux_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct linux_syscall *fsc) -{ - int i; - - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c - * is ever changed these functions need to keep up. - */ - -void -i386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +i386_linux_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct reg regs; - struct linux_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; - } - - syscall_num = regs.r_eax; - - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : linux_syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); + return (-1); } - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "linux_fork") == 0 || - strcmp(fsc->name, "linux_vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - /* * Linux passes syscall arguments in registers, not * on the stack. Fortunately, we've got access to the @@ -154,82 +66,22 @@ i386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs) * number of arguments. And what does linux do for syscalls * that have more than five arguments? */ - - fsc->args[0] = regs.r_ebx; - fsc->args[1] = regs.r_ecx; - fsc->args[2] = regs.r_edx; - fsc->args[3] = regs.r_esi; - fsc->args[4] = regs.r_edi; - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif + switch (narg) { + default: + cs->args[5] = regs.r_ebp; /* Unconfirmed */ + case 5: + cs->args[4] = regs.r_edi; + case 4: + cs->args[3] = regs.r_esi; + case 3: + cs->args[2] = regs.r_edx; + case 2: + cs->args[1] = regs.r_ecx; + case 1: + cs->args[0] = regs.r_ebx; } -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "linux_execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } /* @@ -247,82 +99,42 @@ static const int bsd_to_linux_errno[] = { -6, }; -long -i386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +i386_linux_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct linux_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); + size_t i; tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_eax; - errorp = !!(regs.r_eflags & PSL_C); + retval[0] = regs.r_eax; + retval[1] = regs.r_edx; + *errorp = !!(regs.r_eflags & PSL_C); - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; + if (*errorp) { + for (i = 0; i < nitems(bsd_to_linux_errno); i++) { + if (retval[0] == bsd_to_linux_errno[i]) { + retval[0] = i; + return (0); } } - } - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - if (errorp) { - for (i = 0; - (size_t)i < sizeof(bsd_to_linux_errno) / sizeof(int); i++) { - if (retval == bsd_to_linux_errno[i]) - break; - } + /* XXX: How to handle unknown errors? */ } + return (0); +} - if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - errorp ? i : retval, fsc->sc); - free_fsc(fsc); +static struct procabi i386_linux = { + "Linux ELF32", + linux_syscallnames, + nitems(linux_syscallnames), + i386_linux_fetch_args, + i386_linux_fetch_retval +}; - return (retval); -} +PROCABI(i386_linux); diff --git a/usr.bin/truss/ia64-fbsd.c b/usr.bin/truss/ia64-fbsd.c index 2218fe5..16ce749 100644 --- a/usr.bin/truss/ia64-fbsd.c +++ b/usr.bin/truss/ia64-fbsd.c @@ -29,101 +29,33 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/ia64-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - */ +/* FreeBSD/ia64-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <sys/syscall.h> #include <machine/reg.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in ia64/ia64/trap.c - * is ever changed these functions need to keep up. - */ - -void -ia64_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +ia64_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; unsigned long *parm_offset; lwpid_t tid; - int i, syscall_num; + int i; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return; @@ -131,184 +63,50 @@ ia64_syscall_entry(struct trussinfo *trussinfo, int nargs) parm_offset = ®s.r_scratch.gr16; /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over %rax if it contains + * either of these values. */ - syscall_num = regs.r_scratch.gr15; /* XXX double-check. */ - if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) - syscall_num = (int)*parm_offset++; - - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); - } - - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - memcpy(fsc->args, parm_offset, nargs * sizeof(long)); - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif + switch (regs.r_scratch.gr15) { + case SYS_syscall: + case SYS___syscall: + parm_offset++; + break; } -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif + memcpy(cs->args, parm_offset, narg * sizeof(long)); - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -ia64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +ia64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_scratch.gr8; - errorp = (regs.r_scratch.gr10 != 0) ? 1 : 0; - - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ + retval[0] = regs.r_scratch.gr8; + retval[1] = regs.r_scratch.gr9; + *errorp = (regs.r_scratch.gr10 != 0) ? 1 : 0; + return (0); +} - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); +static struct procabi ia64_fbsd = { + "FreeBSD ELF64", + syscallnames, + nitems(syscallnames), + ia64_fetch_args, + ia64_fetch_retval +}; - return (retval); -} +PROCABI(ia64_fbsd); diff --git a/usr.bin/truss/main.c b/usr.bin/truss/main.c index ecaa0db..5eec953 100644 --- a/usr.bin/truss/main.c +++ b/usr.bin/truss/main.c @@ -38,20 +38,12 @@ __FBSDID("$FreeBSD$"); * do a lot of the work :). */ -#include <sys/param.h> -#include <sys/types.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <sys/sysctl.h> -#include <sys/wait.h> +#include <sys/ptrace.h> #include <err.h> -#include <errno.h> -#include <fcntl.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <time.h> #include <unistd.h> @@ -59,8 +51,6 @@ __FBSDID("$FreeBSD$"); #include "extern.h" #include "syscall.h" -#define MAXARGS 6 - static void usage(void) { @@ -70,133 +60,48 @@ usage(void) exit(1); } -/* - * WARNING! "FreeBSD a.out" must be first, or set_etype will not - * work correctly. - */ -static struct ex_types { - const char *type; - void (*enter_syscall)(struct trussinfo *, int); - long (*exit_syscall)(struct trussinfo *, int); -} ex_types[] = { -#ifdef __arm__ - { "FreeBSD ELF32", arm_syscall_entry, arm_syscall_exit }, -#endif -#ifdef __amd64__ - { "FreeBSD ELF64", amd64_syscall_entry, amd64_syscall_exit }, - { "FreeBSD ELF32", amd64_fbsd32_syscall_entry, amd64_fbsd32_syscall_exit }, - { "Linux ELF32", amd64_linux32_syscall_entry, amd64_linux32_syscall_exit }, -#endif -#ifdef __i386__ - { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, - { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, - { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, - { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, -#endif -#ifdef __ia64__ - { "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit }, -#endif -#ifdef __powerpc__ - { "FreeBSD ELF", powerpc_syscall_entry, powerpc_syscall_exit }, - { "FreeBSD ELF32", powerpc_syscall_entry, powerpc_syscall_exit }, -#ifdef __powerpc64__ - { "FreeBSD ELF64", powerpc64_syscall_entry, powerpc64_syscall_exit }, -#endif -#endif -#ifdef __sparc64__ - { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, -#endif -#ifdef __mips__ - { "FreeBSD ELF", mips_syscall_entry, mips_syscall_exit }, - { "FreeBSD ELF32", mips_syscall_entry, mips_syscall_exit }, - { "FreeBSD ELF64", mips_syscall_entry, mips_syscall_exit }, // XXX -#endif - { 0, 0, 0 }, -}; - -/* - * Set the execution type. This is called after every exec, and when - * a process is first monitored. - */ - -static struct ex_types * -set_etype(struct trussinfo *trussinfo) -{ - struct ex_types *funcs; - size_t len; - int error; - int mib[4]; - char progt[32]; - - len = sizeof(progt); - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_SV_NAME; - mib[3] = trussinfo->pid; - error = sysctl(mib, 4, progt, &len, NULL, 0); - if (error != 0) - err(2, "can not get etype"); - - for (funcs = ex_types; funcs->type; funcs++) - if (strcmp(funcs->type, progt) == 0) - break; - - if (funcs->type == NULL) { - funcs = &ex_types[0]; - warn("execution type %s is not supported -- using %s", - progt, funcs->type); - } - return (funcs); -} - char * strsig(int sig) { - char *ret; + static char tmp[64]; - ret = NULL; if (sig > 0 && sig < NSIG) { - asprintf(&ret, "SIG%s", sys_signame[sig]); - if (ret == NULL) - return (NULL); + snprintf(tmp, sizeof(tmp), "SIG%s", sys_signame[sig]); + return (tmp); } - return (ret); + return (NULL); } int main(int ac, char **av) { - struct timespec timediff; struct sigaction sa; - struct ex_types *funcs; struct trussinfo *trussinfo; char *fname; - char *signame; char **command; - pid_t childpid; - int c, initial_open, status; + pid_t pid; + int c; fname = NULL; - initial_open = 1; /* Initialize the trussinfo struct */ trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo)); if (trussinfo == NULL) errx(1, "calloc() failed"); + pid = 0; trussinfo->outfile = stderr; trussinfo->strsize = 32; - trussinfo->pr_why = S_NONE; trussinfo->curthread = NULL; - SLIST_INIT(&trussinfo->threadlist); + LIST_INIT(&trussinfo->proclist); + init_syscalls(); while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) { switch (c) { case 'p': /* specified pid */ - trussinfo->pid = atoi(optarg); + pid = atoi(optarg); /* make sure i don't trace me */ - if (trussinfo->pid == getpid()) { - fprintf(stderr, "attempt to grab self.\n"); - exit(2); + if (pid == getpid()) { + errx(2, "attempt to grab self."); } break; case 'f': /* Follow fork()'s */ @@ -206,7 +111,7 @@ main(int ac, char **av) trussinfo->flags |= EXECVEARGS; break; case 'c': /* Count number of system calls and time. */ - trussinfo->flags |= COUNTONLY; + trussinfo->flags |= (COUNTONLY | NOSIGS); break; case 'e': /* Print execve() environment strings. */ trussinfo->flags |= EXECVEENVS; @@ -232,8 +137,8 @@ main(int ac, char **av) } ac -= optind; av += optind; - if ((trussinfo->pid == 0 && ac == 0) || - (trussinfo->pid != 0 && ac != 0)) + if ((pid == 0 && ac == 0) || + (pid != 0 && ac != 0)) usage(); if (fname != NULL) { /* Use output file */ @@ -251,10 +156,10 @@ main(int ac, char **av) * exit. If, however, we are examining an already-running process, * then we restore the event mask on these same signals. */ - - if (trussinfo->pid == 0) { /* Start a command ourselves */ + if (pid == 0) { + /* Start a command ourselves */ command = av; - trussinfo->pid = setup_and_wait(command); + setup_and_wait(trussinfo, command); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); @@ -265,119 +170,37 @@ main(int ac, char **av) sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); - start_tracing(trussinfo->pid); + start_tracing(trussinfo, pid); } - /* * At this point, if we started the process, it is stopped waiting to * be woken up, either in exit() or in execve(). */ + if (LIST_FIRST(&trussinfo->proclist)->abi == NULL) { + /* + * If we are not able to handle this ABI, detach from the + * process and exit. If we just created a new process to + * run a command, kill the new process rather than letting + * it run untraced. + */ + if (pid == 0) + kill(LIST_FIRST(&trussinfo->proclist)->pid, SIGKILL); + ptrace(PT_DETACH, LIST_FIRST(&trussinfo->proclist)->pid, NULL, + 0); + return (1); + } + ptrace(PT_SYSCALL, LIST_FIRST(&trussinfo->proclist)->pid, (caddr_t)1, + 0); -START_TRACE: - funcs = set_etype(trussinfo); - - initial_open = 0; /* * At this point, it's a simple loop, waiting for the process to * stop, finding out why, printing out why, and then continuing it. * All of the grunt work is done in the support routines. */ - clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); - do { - waitevent(trussinfo); - - switch (trussinfo->pr_why) { - case S_SCE: - funcs->enter_syscall(trussinfo, MAXARGS); - clock_gettime(CLOCK_REALTIME, - &trussinfo->curthread->before); - break; - case S_SCX: - clock_gettime(CLOCK_REALTIME, - &trussinfo->curthread->after); - - if (trussinfo->curthread->in_fork && - (trussinfo->flags & FOLLOWFORKS)) { - trussinfo->curthread->in_fork = 0; - childpid = funcs->exit_syscall(trussinfo, - trussinfo->pr_data); - - /* - * Fork a new copy of ourself to trace - * the child of the original traced - * process. - */ - if (fork() == 0) { - trussinfo->pid = childpid; - start_tracing(trussinfo->pid); - goto START_TRACE; - } - break; - } - funcs->exit_syscall(trussinfo, MAXARGS); - break; - case S_SIG: - if (trussinfo->flags & NOSIGS) - break; - if (trussinfo->flags & FOLLOWFORKS) - fprintf(trussinfo->outfile, "%5d: ", - trussinfo->pid); - if (trussinfo->flags & ABSOLUTETIMESTAMPS) { - timespecsubt(&trussinfo->curthread->after, - &trussinfo->start_time, &timediff); - fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, - timediff.tv_nsec); - } - if (trussinfo->flags & RELATIVETIMESTAMPS) { - timespecsubt(&trussinfo->curthread->after, - &trussinfo->curthread->before, &timediff); - fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, - timediff.tv_nsec); - } - signame = strsig(trussinfo->pr_data); - fprintf(trussinfo->outfile, - "SIGNAL %u (%s)\n", trussinfo->pr_data, - signame == NULL ? "?" : signame); - free(signame); - break; - case S_EXIT: - if (trussinfo->flags & COUNTONLY) - break; - if (trussinfo->flags & FOLLOWFORKS) - fprintf(trussinfo->outfile, "%5d: ", - trussinfo->pid); - if (trussinfo->flags & ABSOLUTETIMESTAMPS) { - timespecsubt(&trussinfo->curthread->after, - &trussinfo->start_time, &timediff); - fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, - timediff.tv_nsec); - } - if (trussinfo->flags & RELATIVETIMESTAMPS) { - timespecsubt(&trussinfo->curthread->after, - &trussinfo->curthread->before, &timediff); - fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, timediff.tv_nsec); - } - fprintf(trussinfo->outfile, - "process exit, rval = %u\n", trussinfo->pr_data); - break; - default: - break; - } - } while (trussinfo->pr_why != S_EXIT && - trussinfo->pr_why != S_DETACHED); - - if (trussinfo->flags & FOLLOWFORKS) { - do { - childpid = wait(&status); - } while (childpid != -1); - } + eventloop(trussinfo); if (trussinfo->flags & COUNTONLY) print_summary(trussinfo); diff --git a/usr.bin/truss/mips-fbsd.c b/usr.bin/truss/mips-fbsd.c index 71e9efa..d488b7e 100644 --- a/usr.bin/truss/mips-fbsd.c +++ b/usr.bin/truss/mips-fbsd.c @@ -29,339 +29,113 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/sparc64-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - * - * This file is almost nothing more than a slightly-edited i386-fbsd.c. - */ +/* FreeBSD/mips-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <sys/syscall.h> #include <machine/frame.h> #include <machine/reg.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <stddef.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in sparc64/sparc64/trap.c - * is ever changed these functions need to keep up. - */ - -void -mips_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +mips_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; - int indir; /* indirect system call */ + u_int i, reg; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; - } - - indir = 0; - syscall_num = regs.r_regs[V0]; - if (syscall_num == SYS_syscall) { - indir = 1; - syscall_num = regs.r_regs[A0]; + return (-1); } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); + /* + * FreeBSD has two special kinds of system call redirections -- + * SYS_syscall, and SYS___syscall. The former is the old syscall() + * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. + */ + reg = A0; + switch (regs.r_regs[V0]) { + case SYS_syscall: + reg = A1; + break; + case SYS___syscall: +#if defined(__mips_n32) || defined(__mips_n64) + reg = A1; +#else + reg = A2; +#endif + break; } - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); -#if 0 // XXX - iorequest.piod_op = PIOD_READ_D; - iorequest.piod_offs = (void *)parm_offset; - iorequest.piod_addr = fsc->args; - iorequest.piod_len = (1 + nargs) * sizeof(unsigned long); - ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); - if (iorequest.piod_len == 0) - return; +#if defined(__mips_n32) || defined(__mips_n64) +#define MAXREG A7 #else - iorequest.piod_op = PIOD_READ_D; +#define MAXREG A3 #endif - switch (nargs) { - default: - /* - * The OS doesn't seem to allow more than 10 words of - * parameters (yay!). So we shouldn't be here. - */ - warn("More than 10 words (%d) of arguments!\n", nargs); - break; - case 10: - case 9: - case 8: - case 7: - case 6: - case 5: - /* - * If there are 7-10 words of arguments, they are placed - * on the stack, as is normal for other processors. - * The fall-through for all of these is deliberate!!! - */ - // XXX BAD constant used here + for (i = 0; i < narg && reg <= MAXREG; i++, reg++) + cs->args[i] = regs.r_regs[reg]; + if (narg > i) { iorequest.piod_op = PIOD_READ_D; - iorequest.piod_offs = (void *)(regs.r_regs[SP] + - 4 * sizeof(uint32_t)); - iorequest.piod_addr = &fsc->args[4]; - iorequest.piod_len = (nargs - 4) * sizeof(fsc->args[0]); + iorequest.piod_offs = (void *)((uintptr_t)regs.r_regs[SP] + + 4 * sizeof(cs->args[0])); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - case 4: fsc->args[3] = regs.r_regs[A3]; - case 3: fsc->args[2] = regs.r_regs[A2]; - case 2: fsc->args[1] = regs.r_regs[A1]; - case 1: fsc->args[0] = regs.r_regs[A0]; - case 0: break; - } - if (indir) { - memmove(&fsc->args[0], &fsc->args[1], - (nargs - 1) * sizeof(fsc->args[0])); + return (-1); } - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -mips_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +mips_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { - fprintf(trussinfo->outfile, "\n"); + fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_regs[V0]; - errorp = !!regs.r_regs[A3]; - - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; + /* XXX: Does not have special handling for __syscall(). */ + retval[0] = regs.r_regs[V0]; + retval[1] = regs.r_regs[V1]; + *errorp = !!regs.r_regs[A3]; + return (0); +} - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); +static struct procabi mips_fbsd = { +#ifdef __mips_n64 + "FreeBSD ELF64", +#else + "FreeBSD ELF32", +#endif + syscallnames, + nitems(syscallnames), + mips_fetch_args, + mips_fetch_retval +}; - return (retval); -} +PROCABI(mips_fbsd); diff --git a/usr.bin/truss/powerpc-fbsd.c b/usr.bin/truss/powerpc-fbsd.c index 990da29..4a11459 100644 --- a/usr.bin/truss/powerpc-fbsd.c +++ b/usr.bin/truss/powerpc-fbsd.c @@ -25,41 +25,20 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/powerpc-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - * - * This file is almost nothing more than a slightly-edited i386-fbsd.c. - */ +/* FreeBSD/powerpc-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <sys/syscall.h> #include <machine/reg.h> #include <machine/frame.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #ifdef __powerpc64__ /* 32-bit compatibility */ #include "freebsd32_syscalls.h" @@ -68,285 +47,104 @@ static const char rcsid[] = #include "syscalls.h" #endif -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in powerpc/powerpc/trap.c - * is ever changed these functions need to keep up. - */ - -void -powerpc_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +powerpc_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; - void *args; + struct current_syscall *cs; lwpid_t tid; - int i, regargs, syscall_num; - - /* Account for a 64-bit argument with corresponding alignment. */ - nargs += 2; + u_int i, reg; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - regargs = NARGREG; - syscall_num = regs.fixreg[0]; - args = ®s.fixreg[3]; - if (syscall_num == SYS_syscall) { - args = ®s.fixreg[4]; - regargs -= 1; - syscall_num = regs.fixreg[3]; - } else if (syscall_num == SYS___syscall) { - args = ®s.fixreg[5]; - regargs -= 2; - syscall_num = regs.fixreg[4]; + reg = 0; + switch (regs.fixreg[0]) { + case SYS_syscall: + reg += 1; + break; + case SYS___syscall: + reg += 2; + break; } - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); + for (i = 0; i < narg && reg < NARGREG; i++, reg++) { +#ifdef __powerpc64__ + cs->args[i] = regs.fixreg[FIRSTARG + reg] & 0xffffffff; +#else + cs->args[i] = regs.fixreg[FIRSTARG + reg]; +#endif } + if (narg > i) { +#ifdef __powerpc64__ + uint32_t args32[narg - i]; + u_int j; - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - - if (nargs > regargs) { - memmove(&fsc->args[0], args, regargs * sizeof(fsc->args[0])); - +#endif iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.fixreg[1] + 8); - iorequest.piod_addr = &fsc->args[regargs]; - iorequest.piod_len = (nargs - regargs) * sizeof(fsc->args[0]); +#ifdef __powerpc64__ + iorequest.piod_addr = args32; + iorequest.piod_len = sizeof(args32); +#else + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); +#endif ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - } else - memmove(&fsc->args[0], args, nargs * sizeof(fsc->args[0])); - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); + return (-1); +#ifdef __powerpc64__ + for (j = 0; j < narg - i; j++) + cs->args[i + j] = args32[j]; #endif - fsc->nargs = nargs; } - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -powerpc_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +powerpc_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { - fprintf(trussinfo->outfile, "\n"); + fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.fixreg[3]; - errorp = !!(regs.cr & 0x10000000); - - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * On 32-bit big-endian, the low word of a 64-bit return is - * in the greater address. Switch to this. XXX note that - * print_syscall_ret can't handle 64-bit return values (llseek) - */ - if (sc->ret_type == 2) - retval = regs.fixreg[4]; - - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ + /* XXX: Does not have fixup for __syscall(). */ +#ifdef __powerpc64__ + retval[0] = regs.fixreg[3] & 0xffffffff; + retval[1] = regs.fixreg[4] & 0xffffffff; +#else + retval[0] = regs.fixreg[3]; + retval[1] = regs.fixreg[4]; +#endif + *errorp = !!(regs.cr & 0x10000000); + return (0); +} - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); +static struct procabi powerpc_fbsd = { + "FreeBSD ELF32", + syscallnames, + nitems(syscallnames), + powerpc_fetch_args, + powerpc_fetch_retval +}; - return (retval); -} +PROCABI(powerpc_fbsd); diff --git a/usr.bin/truss/powerpc64-fbsd.c b/usr.bin/truss/powerpc64-fbsd.c index 9d5cbd9..5ca5c22 100644 --- a/usr.bin/truss/powerpc64-fbsd.c +++ b/usr.bin/truss/powerpc64-fbsd.c @@ -25,308 +25,94 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/powerpc-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - * - * This file is almost nothing more than a slightly-edited i386-fbsd.c. - */ +/* FreeBSD/powerpc64-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <sys/syscall.h> #include <machine/reg.h> #include <machine/frame.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in powerpc/powerpc/trap.c - * is ever changed these functions need to keep up. - */ - -void -powerpc64_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +powerpc64_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; - void *args; + struct current_syscall *cs; lwpid_t tid; - int i, regargs, syscall_num; + u_int i, reg; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - regargs = NARGREG; - syscall_num = regs.fixreg[0]; - args = ®s.fixreg[3]; - if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) { - args = ®s.fixreg[4]; - regargs -= 1; - syscall_num = regs.fixreg[3]; - } - - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); + reg = 0; + switch (regs.fixreg[0]) { + case SYS_syscall: + case SYS___syscall: + reg += 1; + break; } - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - - if (nargs > regargs) { - memmove(&fsc->args[0], args, regargs * sizeof(fsc->args[0])); - + for (i = 0; i < narg && reg < NARGREG; i++, reg++) + cs->args[i] = regs.fixreg[FIRSTARG + reg]; + if (narg > i) { iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.fixreg[1] + 48); - iorequest.piod_addr = &fsc->args[regargs]; - iorequest.piod_len = (nargs - regargs) * sizeof(fsc->args[0]); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - } else - memmove(&fsc->args[0], args, nargs * sizeof(fsc->args[0])); - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif + return (-1); } -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -powerpc64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +powerpc64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { - fprintf(trussinfo->outfile, "\n"); + fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.fixreg[3]; - errorp = !!(regs.cr & 0x10000000); - - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ + retval[0] = regs.fixreg[3]; + retval[1] = regs.fixreg[4]; + *errorp = !!(regs.cr & 0x10000000); + return (0); +} - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); +static struct procabi powerpc64_fbsd = { + "FreeBSD ELF64", + syscallnames, + nitems(syscallnames), + powerpc64_fetch_args, + powerpc64_fetch_retval +}; - return (retval); -} +PROCABI(powerpc64_fbsd); diff --git a/usr.bin/truss/setup.c b/usr.bin/truss/setup.c index a52bd16..74678e6 100644 --- a/usr.bin/truss/setup.c +++ b/usr.bin/truss/setup.c @@ -37,37 +37,39 @@ __FBSDID("$FreeBSD$"); * I'm afraid. */ -#include <sys/param.h> -#include <sys/types.h> #include <sys/ptrace.h> +#include <sys/sysctl.h> #include <sys/wait.h> +#include <assert.h> #include <err.h> #include <errno.h> -#include <fcntl.h> #include <signal.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> -#include <machine/reg.h> - #include "truss.h" +#include "syscall.h" #include "extern.h" +SET_DECLARE(procabi, struct procabi); + static sig_atomic_t detaching; +static void new_proc(struct trussinfo *, pid_t); + /* * setup_and_wait() is called to start a process. All it really does - * is fork(), set itself up to stop on exec or exit, and then exec - * the given command. At that point, the child process stops, and - * the parent can wake up and deal with it. + * is fork(), enable tracing in the child, and then exec the given + * command. At that point, the child process stops, and the parent + * can wake up and deal with it. */ - -int -setup_and_wait(char *command[]) +void +setup_and_wait(struct trussinfo *info, char *command[]) { pid_t pid; @@ -84,17 +86,14 @@ setup_and_wait(char *command[]) if (waitpid(pid, NULL, 0) < 0) err(1, "unexpect stop in waitpid"); - return (pid); + new_proc(info, pid); } /* - * start_tracing picks up where setup_and_wait() dropped off -- namely, - * it sets the event mask for the given process id. Called for both - * monitoring an existing process and when we create our own. + * start_tracing is called to attach to an existing process. */ - -int -start_tracing(pid_t pid) +void +start_tracing(struct trussinfo *info, pid_t pid) { int ret, retry; @@ -109,7 +108,7 @@ start_tracing(pid_t pid) if (waitpid(pid, NULL, 0) < 0) err(1, "Unexpect stop in waitpid"); - return (0); + new_proc(info, pid); } /* @@ -118,7 +117,6 @@ start_tracing(pid_t pid) * applies if truss was told to monitor an already-existing * process. */ - void restore_proc(int signo __unused) { @@ -126,114 +124,469 @@ restore_proc(int signo __unused) detaching = 1; } -static int +static void detach_proc(pid_t pid) { - int waitval; /* stop the child so that we can detach */ kill(pid, SIGSTOP); - if (waitpid(pid, &waitval, 0) < 0) + if (waitpid(pid, NULL, 0) < 0) err(1, "Unexpected stop in waitpid"); if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0) err(1, "Can not detach the process"); kill(pid, SIGCONT); +} - return (waitval); +/* + * Determine the ABI. This is called after every exec, and when + * a process is first monitored. + */ +static struct procabi * +find_abi(pid_t pid) +{ + struct procabi **pabi; + size_t len; + int error; + int mib[4]; + char progt[32]; + + len = sizeof(progt); + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_SV_NAME; + mib[3] = pid; + error = sysctl(mib, 4, progt, &len, NULL, 0); + if (error != 0) + err(2, "can not get sysvec name"); + + SET_FOREACH(pabi, procabi) { + if (strcmp((*pabi)->type, progt) == 0) + return (*pabi); + } + warnx("ABI %s for pid %ld is not supported", progt, (long)pid); + return (NULL); +} + +static void +new_proc(struct trussinfo *info, pid_t pid) +{ + struct procinfo *np; + + /* + * If this happens it means there is a bug in truss. Unfortunately + * this will kill any processes are attached to. + */ + LIST_FOREACH(np, &info->proclist, entries) { + if (np->pid == pid) + errx(1, "Duplicate process for pid %ld", (long)pid); + } + + if (info->flags & FOLLOWFORKS) + if (ptrace(PT_FOLLOW_FORK, pid, NULL, 1) == -1) + err(1, "Unable to follow forks for pid %ld", (long)pid); + np = calloc(1, sizeof(struct procinfo)); + np->pid = pid; + np->abi = find_abi(pid); + SLIST_INIT(&np->threadlist); + LIST_INSERT_HEAD(&info->proclist, np, entries); +} + +static void +free_proc(struct procinfo *p) +{ + struct threadinfo *t, *t2; + + SLIST_FOREACH_SAFE(t, &p->threadlist, entries, t2) { + free(t); + } + LIST_REMOVE(p, entries); + free(p); +} + +static void +detach_all_procs(struct trussinfo *info) +{ + struct procinfo *p, *p2; + + LIST_FOREACH_SAFE(p, &info->proclist, entries, p2) { + detach_proc(p->pid); + free_proc(p); + } +} + +static struct procinfo * +find_proc(struct trussinfo *info, pid_t pid) +{ + struct procinfo *np; + + LIST_FOREACH(np, &info->proclist, entries) { + if (np->pid == pid) + return (np); + } + + return (NULL); } /* - * Change curthread member based on lwpid. - * If it is a new thread, create a threadinfo structure + * Change curthread member based on (pid, lwpid). + * If it is a new thread, create a threadinfo structure. */ static void -find_thread(struct trussinfo *info, lwpid_t lwpid) +find_thread(struct trussinfo *info, pid_t pid, lwpid_t lwpid) { - struct threadinfo *np; + struct procinfo *np; + struct threadinfo *nt; + + np = find_proc(info, pid); + assert(np != NULL); - info->curthread = NULL; - SLIST_FOREACH(np, &info->threadlist, entries) { - if (np->tid == lwpid) { - info->curthread = np; + SLIST_FOREACH(nt, &np->threadlist, entries) { + if (nt->tid == lwpid) { + info->curthread = nt; return; } } - np = (struct threadinfo *)calloc(1, sizeof(struct threadinfo)); - if (np == NULL) + nt = calloc(1, sizeof(struct threadinfo)); + if (nt == NULL) err(1, "calloc() failed"); - np->tid = lwpid; - SLIST_INSERT_HEAD(&info->threadlist, np, entries); - info->curthread = np; + nt->proc = np; + nt->tid = lwpid; + SLIST_INSERT_HEAD(&np->threadlist, nt, entries); + info->curthread = nt; } /* - * Start the traced process and wait until it stoped. - * Fill trussinfo structure. - * When this even returns, the traced process is in stop state. + * When a process exits, it no longer has any threads left. However, + * the main loop expects a valid curthread. In cases when a thread + * triggers the termination (e.g. calling exit or triggering a fault) + * we would ideally use that thread. However, if a process is killed + * by a signal sent from another process then there is no "correct" + * thread. We just punt and use the first thread. */ -void -waitevent(struct trussinfo *info) +static void +find_exit_thread(struct trussinfo *info, pid_t pid) { - struct ptrace_lwpinfo lwpinfo; - static int pending_signal = 0; - int waitval; + struct procinfo *np; + struct threadinfo *nt; + + np = find_proc(info, pid); + assert(np != NULL); + + if (SLIST_EMPTY(&np->threadlist)) { + /* + * If an existing process exits right after we attach + * to it but before it posts any events, there won't + * be any threads. Create a dummy thread and set its + * "before" time to the global start time. + */ + nt = calloc(1, sizeof(struct threadinfo)); + if (nt == NULL) + err(1, "calloc() failed"); + nt->proc = np; + nt->tid = 0; + SLIST_INSERT_HEAD(&np->threadlist, nt, entries); + nt->before = info->start_time; + } + info->curthread = SLIST_FIRST(&np->threadlist); +} - ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal); - pending_signal = 0; +static void +alloc_syscall(struct threadinfo *t, struct ptrace_lwpinfo *pl) +{ + u_int i; + + assert(t->in_syscall == 0); + assert(t->cs.number == 0); + assert(t->cs.name == NULL); + assert(t->cs.nargs == 0); + for (i = 0; i < nitems(t->cs.s_args); i++) + assert(t->cs.s_args[i] == NULL); + memset(t->cs.args, 0, sizeof(t->cs.args)); + t->cs.number = pl->pl_syscall_code; + t->in_syscall = 1; +} -detach: - if (detaching) { - waitval = detach_proc(info->pid); - info->pr_why = S_DETACHED; - info->pr_data = WEXITSTATUS(waitval); +static void +free_syscall(struct threadinfo *t) +{ + u_int i; + + for (i = 0; i < t->cs.nargs; i++) + free(t->cs.s_args[i]); + memset(&t->cs, 0, sizeof(t->cs)); + t->in_syscall = 0; +} + +static void +enter_syscall(struct trussinfo *info, struct ptrace_lwpinfo *pl) +{ + struct threadinfo *t; + struct syscall *sc; + u_int i, narg; + + t = info->curthread; + alloc_syscall(t, pl); + narg = MIN(pl->pl_syscall_narg, nitems(t->cs.args)); + if (narg != 0 && t->proc->abi->fetch_args(info, narg) != 0) { + free_syscall(t); return; } - if (waitpid(info->pid, &waitval, 0) == -1) { - if (errno == EINTR) - goto detach; - err(1, "Unexpected stop in waitpid"); + if (t->cs.number >= 0 && t->cs.number < t->proc->abi->nsyscalls) + t->cs.name = t->proc->abi->syscallnames[t->cs.number]; + if (t->cs.name == NULL) + fprintf(info->outfile, "-- UNKNOWN %s SYSCALL %d --\n", + t->proc->abi->type, t->cs.number); + + sc = get_syscall(t->cs.name, narg); + t->cs.nargs = sc->nargs; + assert(sc->nargs <= nitems(t->cs.s_args)); + + t->cs.sc = sc; + + /* + * At this point, we set up the system call arguments. + * We ignore any OUT ones, however -- those are arguments that + * are set by the system call, and so are probably meaningless + * now. This doesn't currently support arguments that are + * passed in *and* out, however. + */ + if (t->cs.name != NULL) { +#if DEBUG + fprintf(stderr, "syscall %s(", t->cs.name); +#endif + for (i = 0; i < t->cs.nargs; i++) { +#if DEBUG + fprintf(stderr, "0x%lx%s", sc ? + t->cs.args[sc->args[i].offset] : t->cs.args[i], + i < (t->cs.nargs - 1) ? "," : ""); +#endif + if (!(sc->args[i].type & OUT)) { + t->cs.s_args[i] = print_arg(&sc->args[i], + t->cs.args, 0, info); + } + } +#if DEBUG + fprintf(stderr, ")\n"); +#endif } - if (WIFCONTINUED(waitval)) { - info->pr_why = S_NONE; + clock_gettime(CLOCK_REALTIME, &t->before); +} + +static void +exit_syscall(struct trussinfo *info, struct ptrace_lwpinfo *pl) +{ + struct threadinfo *t; + struct procinfo *p; + struct syscall *sc; + long retval[2]; + u_int i; + int errorp; + + t = info->curthread; + if (!t->in_syscall) return; - } - if (WIFEXITED(waitval)) { - info->pr_why = S_EXIT; - info->pr_data = WEXITSTATUS(waitval); + + clock_gettime(CLOCK_REALTIME, &t->after); + p = t->proc; + if (p->abi->fetch_retval(info, retval, &errorp) < 0) { + free_syscall(t); return; } - if (WIFSTOPPED(waitval)) { - ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo, - sizeof(lwpinfo)); - find_thread(info, lwpinfo.pl_lwpid); - switch (WSTOPSIG(waitval)) { - case SIGTRAP: - if (lwpinfo.pl_flags & PL_FLAG_SCE) { - info->pr_why = S_SCE; - info->curthread->in_syscall = 1; - break; - } else if (lwpinfo.pl_flags & PL_FLAG_SCX) { - info->pr_why = S_SCX; - info->curthread->in_syscall = 0; - break; + + sc = t->cs.sc; + /* + * Here, we only look for arguments that have OUT masked in -- + * otherwise, they were handled in enter_syscall(). + */ + for (i = 0; i < sc->nargs; i++) { + char *temp; + + if (sc->args[i].type & OUT) { + /* + * If an error occurred, then don't bother + * getting the data; it may not be valid. + */ + if (errorp) { + asprintf(&temp, "0x%lx", + t->cs.args[sc->args[i].offset]); + } else { + temp = print_arg(&sc->args[i], + t->cs.args, retval, info); } - /* We didn't send the SIGTRAP, just forward it. */ - /* FALLTHROUGH */ - default: - info->pr_why = S_SIG; - info->pr_data = WSTOPSIG(waitval); - pending_signal = info->pr_data; - break; + t->cs.s_args[i] = temp; } } - if (WIFSIGNALED(waitval)) { - info->pr_why = S_EXIT; - info->pr_data = 0; - return; + + print_syscall_ret(info, t->cs.name, t->cs.nargs, t->cs.s_args, + errorp, retval, sc); + free_syscall(t); + + /* + * If the process executed a new image, check the ABI. If the + * new ABI isn't supported, stop tracing this process. + */ + if (pl->pl_flags & PL_FLAG_EXEC) { + p->abi = find_abi(p->pid); + if (p->abi == NULL) { + if (ptrace(PT_DETACH, p->pid, (caddr_t)1, 0) < 0) + err(1, "Can not detach the process"); + free_proc(p); + } + } +} + +static void +report_exit(struct trussinfo *info, siginfo_t *si) +{ + struct timespec timediff; + + if (info->flags & FOLLOWFORKS) + fprintf(info->outfile, "%5d: ", si->si_pid); + clock_gettime(CLOCK_REALTIME, &info->curthread->after); + if (info->flags & ABSOLUTETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->start_time, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + if (info->flags & RELATIVETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->curthread->before, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + if (si->si_code == CLD_EXITED) + fprintf(info->outfile, "process exit, rval = %u\n", + si->si_status); + else + fprintf(info->outfile, "process killed, signal = %u%s\n", + si->si_status, si->si_code == CLD_DUMPED ? + " (core dumped)" : ""); +} + +static void +report_new_child(struct trussinfo *info, pid_t pid) +{ + struct timespec timediff; + + clock_gettime(CLOCK_REALTIME, &info->curthread->after); + assert(info->flags & FOLLOWFORKS); + fprintf(info->outfile, "%5d: ", pid); + if (info->flags & ABSOLUTETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->start_time, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + if (info->flags & RELATIVETIMESTAMPS) { + timediff.tv_sec = 0; + timediff.tv_nsec = 0; + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + fprintf(info->outfile, "<new process>\n"); +} + +static void +report_signal(struct trussinfo *info, siginfo_t *si) +{ + struct timespec timediff; + char *signame; + + if (info->flags & FOLLOWFORKS) + fprintf(info->outfile, "%5d: ", si->si_pid); + if (info->flags & ABSOLUTETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->start_time, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + if (info->flags & RELATIVETIMESTAMPS) { + timespecsubt(&info->curthread->after, &info->curthread->before, + &timediff); + fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, + timediff.tv_nsec); + } + signame = strsig(si->si_status); + fprintf(info->outfile, "SIGNAL %u (%s)\n", si->si_status, + signame == NULL ? "?" : signame); +} + +/* + * Wait for events until all the processes have exited or truss has been + * asked to stop. + */ +void +eventloop(struct trussinfo *info) +{ + struct ptrace_lwpinfo pl; + siginfo_t si; + int pending_signal; + + while (!LIST_EMPTY(&info->proclist)) { + if (detaching) { + detach_all_procs(info); + return; + } + + if (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) { + if (errno == EINTR) + continue; + err(1, "Unexpected error from waitid"); + } + + assert(si.si_signo == SIGCHLD); + + switch (si.si_code) { + case CLD_EXITED: + case CLD_KILLED: + case CLD_DUMPED: + find_exit_thread(info, si.si_pid); + if ((info->flags & COUNTONLY) == 0) + report_exit(info, &si); + free_proc(info->curthread->proc); + info->curthread = NULL; + break; + case CLD_TRAPPED: + if (ptrace(PT_LWPINFO, si.si_pid, (caddr_t)&pl, + sizeof(pl)) == -1) + err(1, "ptrace(PT_LWPINFO)"); + + if (pl.pl_flags & PL_FLAG_CHILD) { + new_proc(info, si.si_pid); + assert(LIST_FIRST(&info->proclist)->abi != + NULL); + } + find_thread(info, si.si_pid, pl.pl_lwpid); + + if (si.si_status == SIGTRAP && + (pl.pl_flags & (PL_FLAG_SCE|PL_FLAG_SCX)) != 0) { + if (pl.pl_flags & PL_FLAG_SCE) + enter_syscall(info, &pl); + else if (pl.pl_flags & PL_FLAG_SCX) + exit_syscall(info, &pl); + pending_signal = 0; + } else if (pl.pl_flags & PL_FLAG_CHILD) { + if ((info->flags & COUNTONLY) == 0) + report_new_child(info, si.si_pid); + pending_signal = 0; + } else { + if ((info->flags & NOSIGS) == 0) + report_signal(info, &si); + pending_signal = si.si_status; + } + ptrace(PT_SYSCALL, si.si_pid, (caddr_t)1, + pending_signal); + break; + case CLD_STOPPED: + errx(1, "waitid reported CLD_STOPPED"); + case CLD_CONTINUED: + break; + } } } diff --git a/usr.bin/truss/sparc64-fbsd.c b/usr.bin/truss/sparc64-fbsd.c index 3c6de5f..c8c1e8c 100644 --- a/usr.bin/truss/sparc64-fbsd.c +++ b/usr.bin/truss/sparc64-fbsd.c @@ -29,22 +29,11 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); -/* - * FreeBSD/sparc64-specific system call handling. This is probably the most - * complex part of the entire truss program, although I've got lots of - * it handled relatively cleanly now. The system call names are generated - * automatically, thanks to /usr/src/sys/kern/syscalls.master. The - * names used for the various structures are confusing, I sadly admit. - * - * This file is almost nothing more than a slightly-edited i386-fbsd.c. - */ +/* FreeBSD/sparc64-specific system call handling. */ -#include <sys/types.h> #include <sys/ptrace.h> #include <sys/syscall.h> @@ -52,310 +41,85 @@ static const char rcsid[] = #include <machine/reg.h> #include <machine/tstate.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <stddef.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> #include "truss.h" -#include "syscall.h" -#include "extern.h" #include "syscalls.h" -static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]); - -/* - * This is what this particular file uses to keep track of a system call. - * It is probably not quite sufficient -- I can probably use the same - * structure for the various syscall personalities, and I also probably - * need to nest system calls (for signal handlers). - * - * 'struct syscall' describes the system call; it may be NULL, however, - * if we don't know about this particular system call yet. - */ -struct freebsd_syscall { - struct syscall *sc; - const char *name; - int number; - unsigned long *args; - int nargs; /* number of arguments -- *not* number of words! */ - char **s_args; /* the printable arguments */ -}; - -static struct freebsd_syscall * -alloc_fsc(void) -{ - - return (malloc(sizeof(struct freebsd_syscall))); -} - -/* Clear up and free parts of the fsc structure. */ -static void -free_fsc(struct freebsd_syscall *fsc) -{ - int i; - - free(fsc->args); - if (fsc->s_args) { - for (i = 0; i < fsc->nargs; i++) - free(fsc->s_args[i]); - free(fsc->s_args); - } - free(fsc); -} - -/* - * Called when a process has entered a system call. nargs is the - * number of words, not number of arguments (a necessary distinction - * in some cases). Note that if the STOPEVENT() code in sparc64/sparc64/trap.c - * is ever changed these functions need to keep up. - */ - -void -sparc64_syscall_entry(struct trussinfo *trussinfo, int nargs) +static int +sparc64_fetch_args(struct trussinfo *trussinfo, u_int narg) { struct ptrace_io_desc iorequest; struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; + struct current_syscall *cs; lwpid_t tid; - int i, syscall_num; - int indir; /* indirect system call */ + u_int i, reg; tid = trussinfo->curthread->tid; - + cs = &trussinfo->curthread->cs; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); - return; + return (-1); } /* - * FreeBSD has two special kinds of system call redirctions -- + * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. + * + * The system call argument count and code from ptrace() already + * account for these, but we need to skip over the first argument. */ - indir = 0; - syscall_num = regs.r_global[1]; - if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) { - indir = 1; - syscall_num = regs.r_out[0]; - } - - fsc = alloc_fsc(); - if (fsc == NULL) - return; - fsc->number = syscall_num; - fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? - NULL : syscallnames[syscall_num]; - if (!fsc->name) { - fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", - syscall_num); + reg = 0; + switch (regs.r_global[1]) { + case SYS_syscall: + case SYS___syscall: + reg = 1; + break; } - if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && - (strcmp(fsc->name, "fork") == 0 || - strcmp(fsc->name, "pdfork") == 0 || - strcmp(fsc->name, "rfork") == 0 || - strcmp(fsc->name, "vfork") == 0)) - trussinfo->curthread->in_fork = 1; - - if (nargs == 0) - return; - - fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); - switch (nargs) { - default: - /* - * The OS doesn't seem to allow more than 10 words of - * parameters (yay!). So we shouldn't be here. - */ - warn("More than 10 words (%d) of arguments!\n", nargs); - break; - case 10: - case 9: - case 8: - case 7: - /* - * If there are 7-10 words of arguments, they are placed - * on the stack, as is normal for other processors. - * The fall-through for all of these is deliberate!!! - */ + for (i = 0; i < narg && reg < 6; i++, reg++) + cs->args[i] = regs.r_out[reg]; + if (narg > i) { iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.r_out[6] + SPOFF + offsetof(struct frame, fr_pad[6])); - iorequest.piod_addr = &fsc->args[6]; - iorequest.piod_len = (nargs - 6) * sizeof(fsc->args[0]); + iorequest.piod_addr = &cs->args[i]; + iorequest.piod_len = (narg - i) * sizeof(cs->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) - return; - case 6: fsc->args[5] = regs.r_out[5]; - case 5: fsc->args[4] = regs.r_out[4]; - case 4: fsc->args[3] = regs.r_out[3]; - case 3: fsc->args[2] = regs.r_out[2]; - case 2: fsc->args[1] = regs.r_out[1]; - case 1: fsc->args[0] = regs.r_out[0]; - case 0: - break; + return (-1); } - if (indir) - memmove(&fsc->args[0], &fsc->args[1], (nargs - 1) * - sizeof(fsc->args[0])); - - sc = get_syscall(fsc->name); - if (sc) - fsc->nargs = sc->nargs; - else { -#if DEBUG - fprintf(trussinfo->outfile, "unknown syscall %s -- setting " - "args to %d\n", fsc->name, nargs); -#endif - fsc->nargs = nargs; - } - - fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); - fsc->sc = sc; - - /* - * At this point, we set up the system call arguments. - * We ignore any OUT ones, however -- those are arguments that - * are set by the system call, and so are probably meaningless - * now. This doesn't currently support arguments that are - * passed in *and* out, however. - */ - - if (fsc->name) { -#if DEBUG - fprintf(stderr, "syscall %s(", fsc->name); -#endif - for (i = 0; i < fsc->nargs; i++) { -#if DEBUG - fprintf(stderr, "0x%x%s", sc ? - fsc->args[sc->args[i].offset] : fsc->args[i], - i < (fsc->nargs - 1) ? "," : ""); -#endif - if (sc && !(sc->args[i].type & OUT)) { - fsc->s_args[i] = print_arg(&sc->args[i], - fsc->args, 0, trussinfo); - } - } -#if DEBUG - fprintf(stderr, ")\n"); -#endif - } - -#if DEBUG - fprintf(trussinfo->outfile, "\n"); -#endif - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) { - /* - * XXX - * This could be done in a more general - * manner but it still wouldn't be very pretty. - */ - if (strcmp(fsc->name, "execve") == 0) { - if ((trussinfo->flags & EXECVEARGS) == 0) { - if (fsc->s_args[1]) { - free(fsc->s_args[1]); - fsc->s_args[1] = NULL; - } - } - if ((trussinfo->flags & EXECVEENVS) == 0) { - if (fsc->s_args[2]) { - free(fsc->s_args[2]); - fsc->s_args[2] = NULL; - } - } - } - } - trussinfo->curthread->fsc = fsc; + return (0); } -/* - * And when the system call is done, we handle it here. - * Currently, no attempt is made to ensure that the system calls - * match -- this needs to be fixed (and is, in fact, why S_SCX includes - * the system call number instead of, say, an error status). - */ - -long -sparc64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) +static int +sparc64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp) { struct reg regs; - struct freebsd_syscall *fsc; - struct syscall *sc; lwpid_t tid; - long retval; - int errorp, i; - - if (trussinfo->curthread->fsc == NULL) - return (-1); tid = trussinfo->curthread->tid; - if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { - fprintf(trussinfo->outfile, "\n"); + fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return (-1); } - retval = regs.r_out[0]; - errorp = !!(regs.r_tstate & TSTATE_XCC_C); - - /* - * This code, while simpler than the initial versions I used, could - * stand some significant cleaning. - */ - - fsc = trussinfo->curthread->fsc; - sc = fsc->sc; - if (!sc) { - for (i = 0; i < fsc->nargs; i++) - asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]); - } else { - /* - * Here, we only look for arguments that have OUT masked in -- - * otherwise, they were handled in the syscall_entry function. - */ - for (i = 0; i < sc->nargs; i++) { - char *temp; - if (sc->args[i].type & OUT) { - /* - * If an error occurred, then don't bother - * getting the data; it may not be valid. - */ - if (errorp) { - asprintf(&temp, "0x%lx", - fsc->args[sc->args[i].offset]); - } else { - temp = print_arg(&sc->args[i], - fsc->args, retval, trussinfo); - } - fsc->s_args[i] = temp; - } - } - } - - if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || - strcmp(fsc->name, "exit") == 0)) - trussinfo->curthread->in_syscall = 1; - - /* - * It would probably be a good idea to merge the error handling, - * but that complicates things considerably. - */ + retval[0] = regs.r_out[0]; + retval[1] = regs.r_out[1]; + *errorp = !!(regs.r_tstate & TSTATE_XCC_C); + return (0); +} - print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp, - retval, fsc->sc); - free_fsc(fsc); +static struct procabi sparc64_fbsd = { + "FreeBSD ELF64", + syscallnames, + nitems(syscallnames), + sparc64_fetch_args, + sparc64_fetch_retval +}; - return (retval); -} +PROCABI(sparc64_fbsd); diff --git a/usr.bin/truss/syscall.h b/usr.bin/truss/syscall.h index b74eded..a240749 100644 --- a/usr.bin/truss/syscall.h +++ b/usr.bin/truss/syscall.h @@ -10,6 +10,7 @@ * BinString -- pointer to an array of chars, printed via strvisx(). * Ptr -- pointer to some unspecified structure. Just print as hex for now. * Stat -- a pointer to a stat buffer. Prints a couple fields. + * StatFs -- a pointer to a statfs buffer. Prints a few fields. * Ioctl -- an ioctl command. Woefully limited. * Quad -- a double-word value. e.g., lseek(int, offset_t, int) * Signal -- a signal number. Prints the signal name (SIGxxx) @@ -39,10 +40,11 @@ enum Argtype { None = 1, Hex, Octal, Int, LongHex, Name, Ptr, Stat, Ioctl, Quad, Signal, Sockaddr, StringArray, Timespec, Timeval, Itimerval, Pollfd, Fd_set, Sigaction, Fcntl, Mprot, Mmapflags, Whence, Readlinkres, - Umtx, Sigset, Sigprocmask, Kevent, Sockdomain, Socktype, Open, + Umtx, Sigset, Sigprocmask, StatFs, Kevent, Sockdomain, Socktype, Open, Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2, Pathconf, Rforkflags, ExitStatus, Waitoptions, Idtype, Procctl, - LinuxSockArgs, Umtxop }; + LinuxSockArgs, Umtxop, Atfd, Atflags, Accessmode, Long, + Sysarch, ExecArgs, ExecEnv, PipeFds, QuadHex }; #define ARG_MASK 0xff #define OUT 0x100 @@ -54,9 +56,10 @@ struct syscall_args { }; struct syscall { + STAILQ_ENTRY(syscall) entries; const char *name; - int ret_type; /* 0, 1, or 2 return values */ - int nargs; /* actual number of meaningful arguments */ + u_int ret_type; /* 0, 1, or 2 return values */ + u_int nargs; /* actual number of meaningful arguments */ /* Hopefully, no syscalls with > 10 args */ struct syscall_args args[10]; struct timespec time; /* Time spent for this call */ @@ -64,8 +67,8 @@ struct syscall { int nerror; /* Number of calls that returned with error */ }; -struct syscall *get_syscall(const char*); -char *print_arg(struct syscall_args *, unsigned long*, long, struct trussinfo *); +struct syscall *get_syscall(const char *, int nargs); +char *print_arg(struct syscall_args *, unsigned long*, long *, struct trussinfo *); /* * Linux Socket defines @@ -86,11 +89,11 @@ char *print_arg(struct syscall_args *, unsigned long*, long, struct trussinfo *) #define LINUX_SETSOCKOPT 14 #define LINUX_GETSOCKOPT 15 #define LINUX_SENDMSG 16 -#define LINUX_RECVMSG 17 +#define LINUX_RECVMSG 17 #define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \ 0 : sizeof(register_t) - sizeof(t)) - + #if BYTE_ORDER == LITTLE_ENDIAN #define PADL_(t) 0 #define PADR_(t) PAD_(t) @@ -107,7 +110,8 @@ struct linux_socketcall_args { char args_l_[PADL_(l_ulong)]; l_ulong args; char args_r_[PADR_(l_ulong)]; }; +void init_syscalls(void); void print_syscall(struct trussinfo *, const char *, int, char **); void print_syscall_ret(struct trussinfo *, const char *, int, char **, int, - long, struct syscall *); + long *, struct syscall *); void print_summary(struct trussinfo *trussinfo); diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c index f473be4..7815fd1 100644 --- a/usr.bin/truss/syscalls.c +++ b/usr.bin/truss/syscalls.c @@ -29,10 +29,8 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); /* * This file has routines used to print out system calls and their @@ -40,28 +38,30 @@ static const char rcsid[] = */ #include <sys/types.h> +#include <sys/event.h> +#include <sys/ioccom.h> #include <sys/mman.h> +#include <sys/mount.h> #include <sys/procctl.h> #include <sys/ptrace.h> +#include <sys/resource.h> #include <sys/socket.h> -#include <sys/time.h> +#include <sys/stat.h> +#include <machine/atomic.h> +#include <errno.h> +#include <sys/umtx.h> #include <sys/un.h> #include <sys/wait.h> +#include <machine/sysarch.h> #include <netinet/in.h> #include <arpa/inet.h> -#include <sys/ioccom.h> -#include <machine/atomic.h> -#include <errno.h> -#include <sys/umtx.h> -#include <sys/event.h> -#include <sys/stat.h> -#include <sys/resource.h> #include <ctype.h> #include <err.h> #include <fcntl.h> #include <poll.h> #include <signal.h> +#include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -75,7 +75,7 @@ static const char rcsid[] = #include "syscall.h" /* 64-bit alignment on 32-bit platforms. */ -#ifdef __powerpc__ +#if !defined(__LP64__) && defined(__powerpc__) #define QUAD_ALIGN 1 #else #define QUAD_ALIGN 0 @@ -91,203 +91,296 @@ static const char rcsid[] = /* * This should probably be in its own file, sorted alphabetically. */ -static struct syscall syscalls[] = { - { .name = "fcntl", .ret_type = 1, .nargs = 3, - .args = { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 } } }, - { .name = "fork", .ret_type = 1, .nargs = 0 }, - { .name = "vfork", .ret_type = 1, .nargs = 0 }, - { .name = "rfork", .ret_type = 1, .nargs = 1, - .args = { { Rforkflags, 0 } } }, - { .name = "getegid", .ret_type = 1, .nargs = 0 }, - { .name = "geteuid", .ret_type = 1, .nargs = 0 }, - { .name = "linux_readlink", .ret_type = 1, .nargs = 3, - .args = { { Name, 0 } , { Name | OUT, 1 }, { Int, 2 }}}, - { .name = "linux_socketcall", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 } , { LinuxSockArgs, 1 }}}, - { .name = "getgid", .ret_type = 1, .nargs = 0 }, - { .name = "getpid", .ret_type = 1, .nargs = 0 }, - { .name = "getpgid", .ret_type = 1, .nargs = 1, - .args = { { Int, 0 } } }, - { .name = "getpgrp", .ret_type = 1, .nargs = 0 }, - { .name = "getppid", .ret_type = 1, .nargs = 0 }, - { .name = "getsid", .ret_type = 1, .nargs = 1, - .args = { { Int, 0 } } }, - { .name = "getuid", .ret_type = 1, .nargs = 0 }, - { .name = "readlink", .ret_type = 1, .nargs = 3, - .args = { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 } } }, - { .name = "lseek", .ret_type = 2, .nargs = 3, - .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } }, - { .name = "linux_lseek", .ret_type = 2, .nargs = 3, - .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, - { .name = "mmap", .ret_type = 2, .nargs = 6, - .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } }, - { .name = "linux_mkdir", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0} , {Int, 1}}}, - { .name = "mprotect", .ret_type = 1, .nargs = 3, - .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, - { .name = "open", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 } , { Open, 1 }, { Octal, 2 } } }, - { .name = "mkdir", .ret_type = 1, .nargs = 2, - .args = { { Name, 0 } , { Octal, 1 } } }, - { .name = "linux_open", .ret_type = 1, .nargs = 3, - .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, - { .name = "close", .ret_type = 1, .nargs = 1, - .args = { { Int, 0 } } }, - { .name = "link", .ret_type = 0, .nargs = 2, - .args = { { Name, 0 }, { Name, 1 } } }, - { .name = "unlink", .ret_type = 0, .nargs = 1, - .args = { { Name, 0 } } }, - { .name = "chdir", .ret_type = 0, .nargs = 1, - .args = { { Name, 0 } } }, - { .name = "chroot", .ret_type = 0, .nargs = 1, - .args = { { Name, 0 } } }, - { .name = "mknod", .ret_type = 0, .nargs = 3, - .args = { { Name, 0 }, { Octal, 1 }, { Int, 3 } } }, - { .name = "chmod", .ret_type = 0, .nargs = 2, - .args = { { Name, 0 }, { Octal, 1 } } }, - { .name = "chown", .ret_type = 0, .nargs = 3, - .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, - { .name = "linux_stat64", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 }}}, - { .name = "mount", .ret_type = 0, .nargs = 4, - .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, - { .name = "umount", .ret_type = 0, .nargs = 2, - .args = { { Name, 0 }, { Int, 2 } } }, - { .name = "fstat", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Stat | OUT , 1 } } }, - { .name = "stat", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, - { .name = "lstat", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, - { .name = "linux_newstat", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, - { .name = "linux_access", .ret_type = 1, .nargs = 2, - .args = { { Name, 0 }, { Int, 1 }}}, - { .name = "linux_newfstat", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Ptr | OUT, 1 } } }, - { .name = "write", .ret_type = 1, .nargs = 3, - .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, - { .name = "ioctl", .ret_type = 1, .nargs = 3, - .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, - { .name = "break", .ret_type = 1, .nargs = 1, - .args = { { Ptr, 0 } } }, - { .name = "exit", .ret_type = 0, .nargs = 1, - .args = { { Hex, 0 } } }, - { .name = "access", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Int, 1 } } }, - { .name = "sigaction", .ret_type = 1, .nargs = 3, - .args = { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } }, +static struct syscall decoded_syscalls[] = { + /* Native ABI */ + { .name = "__getcwd", .ret_type = 1, .nargs = 2, + .args = { { Name | OUT, 0 }, { Int, 1 } } }, + { .name = "_umtx_lock", .ret_type = 1, .nargs = 1, + .args = { { Umtx, 0 } } }, + { .name = "_umtx_op", .ret_type = 1, .nargs = 5, + .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 }, + { Ptr, 4 } } }, + { .name = "_umtx_unlock", .ret_type = 1, .nargs = 1, + .args = { { Umtx, 0 } } }, { .name = "accept", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, + { .name = "access", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Accessmode, 1 } } }, { .name = "bind", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, + { .name = "bindat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 }, + { Int, 3 } } }, + { .name = "break", .ret_type = 1, .nargs = 1, + .args = { { Ptr, 0 } } }, + { .name = "chdir", .ret_type = 1, .nargs = 1, + .args = { { Name, 0 } } }, + { .name = "chflags", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Hex, 1 } } }, + { .name = "chmod", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Octal, 1 } } }, + { .name = "chown", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, + { .name = "chroot", .ret_type = 1, .nargs = 1, + .args = { { Name, 0 } } }, + { .name = "clock_gettime", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Timespec | OUT, 1 } } }, + { .name = "close", .ret_type = 1, .nargs = 1, + .args = { { Int, 0 } } }, { .name = "connect", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, + { .name = "connectat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 }, + { Int, 3 } } }, + { .name = "eaccess", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Accessmode, 1 } } }, + { .name = "execve", .ret_type = 1, .nargs = 3, + .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 }, + { ExecEnv | IN, 2 } } }, + { .name = "exit", .ret_type = 0, .nargs = 1, + .args = { { Hex, 0 } } }, + { .name = "faccessat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 }, + { Atflags, 3 } } }, + { .name = "fchmod", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Octal, 1 } } }, + { .name = "fchmodat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } }, + { .name = "fchown", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } }, + { .name = "fchownat", .ret_type = 1, .nargs = 5, + .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 }, + { Atflags, 4 } } }, + { .name = "fcntl", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } }, + { .name = "fstat", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Stat | OUT, 1 } } }, + { .name = "fstatat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 }, + { Atflags, 3 } } }, + { .name = "fstatfs", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { StatFs | OUT, 1 } } }, + { .name = "ftruncate", .ret_type = 1, .nargs = 2, + .args = { { Int | IN, 0 }, { QuadHex | IN, 1 + QUAD_ALIGN } } }, + { .name = "futimes", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Timeval2 | IN, 1 } } }, + { .name = "futimesat", .ret_type = 1, .nargs = 3, + .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } }, + { .name = "getitimer", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Itimerval | OUT, 2 } } }, { .name = "getpeername", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, + { .name = "getpgid", .ret_type = 1, .nargs = 1, + .args = { { Int, 0 } } }, + { .name = "getrlimit", .ret_type = 1, .nargs = 2, + .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } }, + { .name = "getrusage", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Rusage | OUT, 1 } } }, + { .name = "getsid", .ret_type = 1, .nargs = 1, + .args = { { Int, 0 } } }, { .name = "getsockname", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, - { .name = "recvfrom", .ret_type = 1, .nargs = 6, - .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, - { .name = "sendto", .ret_type = 1, .nargs = 6, - .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, - { .name = "execve", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, - { .name = "linux_execve", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, - { .name = "kldload", .ret_type = 0, .nargs = 1, + { .name = "gettimeofday", .ret_type = 1, .nargs = 2, + .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } }, + { .name = "ioctl", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, + { .name = "kevent", .ret_type = 1, .nargs = 6, + .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, + { Int, 4 }, { Timespec, 5 } } }, + { .name = "kill", .ret_type = 1, .nargs = 2, + .args = { { Int | IN, 0 }, { Signal | IN, 1 } } }, + { .name = "kldfind", .ret_type = 1, .nargs = 1, .args = { { Name | IN, 0 } } }, - { .name = "kldunload", .ret_type = 0, .nargs = 1, + { .name = "kldfirstmod", .ret_type = 1, .nargs = 1, .args = { { Int, 0 } } }, - { .name = "kldfind", .ret_type = 0, .nargs = 1, + { .name = "kldload", .ret_type = 1, .nargs = 1, .args = { { Name | IN, 0 } } }, - { .name = "kldnext", .ret_type = 0, .nargs = 1, + { .name = "kldnext", .ret_type = 1, .nargs = 1, .args = { { Int, 0 } } }, - { .name = "kldstat", .ret_type = 0, .nargs = 2, + { .name = "kldstat", .ret_type = 1, .nargs = 2, .args = { { Int, 0 }, { Ptr, 1 } } }, - { .name = "kldfirstmod", .ret_type = 0, .nargs = 1, + { .name = "kldunload", .ret_type = 1, .nargs = 1, .args = { { Int, 0 } } }, - { .name = "nanosleep", .ret_type = 0, .nargs = 1, - .args = { { Timespec, 0 } } }, - { .name = "select", .ret_type = 1, .nargs = 5, - .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 } } }, - { .name = "poll", .ret_type = 1, .nargs = 3, - .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, - { .name = "gettimeofday", .ret_type = 1, .nargs = 2, - .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } }, - { .name = "clock_gettime", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Timespec | OUT, 1 } } }, - { .name = "getitimer", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Itimerval | OUT, 2 } } }, - { .name = "setitimer", .ret_type = 1, .nargs = 3, - .args = { { Int, 0 }, { Itimerval, 1 } , { Itimerval | OUT, 2 } } }, { .name = "kse_release", .ret_type = 0, .nargs = 1, .args = { { Timespec, 0 } } }, - { .name = "kevent", .ret_type = 0, .nargs = 6, - .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 } } }, - { .name = "_umtx_lock", .ret_type = 0, .nargs = 1, - .args = { { Umtx, 0 } } }, - { .name = "_umtx_unlock", .ret_type = 0, .nargs = 1, - .args = { { Umtx, 0 } } }, - { .name = "sigprocmask", .ret_type = 0, .nargs = 3, - .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, - { .name = "unmount", .ret_type = 1, .nargs = 2, - .args = { { Name, 0 }, { Int, 1 } } }, - { .name = "socket", .ret_type = 1, .nargs = 3, - .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, - { .name = "getrusage", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Rusage | OUT, 1 } } }, - { .name = "__getcwd", .ret_type = 1, .nargs = 2, - .args = { { Name | OUT, 0 }, { Int, 1 } } }, - { .name = "shutdown", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Shutdown, 1 } } }, - { .name = "getrlimit", .ret_type = 1, .nargs = 2, - .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } }, - { .name = "setrlimit", .ret_type = 1, .nargs = 2, - .args = { { Resource, 0 }, { Rlimit | IN, 1 } } }, - { .name = "utimes", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, - { .name = "lutimes", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, - { .name = "futimes", .ret_type = 1, .nargs = 2, - .args = { { Int, 0 }, { Timeval | IN, 1 } } }, - { .name = "chflags", .ret_type = 1, .nargs = 2, - .args = { { Name | IN, 0 }, { Hex, 1 } } }, { .name = "lchflags", .ret_type = 1, .nargs = 2, .args = { { Name | IN, 0 }, { Hex, 1 } } }, + { .name = "lchmod", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Octal, 1 } } }, + { .name = "lchown", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, + { .name = "link", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Name, 1 } } }, + { .name = "linkat", .ret_type = 1, .nargs = 5, + .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 }, + { Atflags, 4 } } }, + { .name = "lseek", .ret_type = 2, .nargs = 3, + .args = { { Int, 0 }, { QuadHex, 1 + QUAD_ALIGN }, + { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } }, + { .name = "lstat", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, + { .name = "lutimes", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, + { .name = "mkdir", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Octal, 1 } } }, + { .name = "mkdirat", .ret_type = 1, .nargs = 3, + .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } }, + { .name = "mkfifo", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Octal, 1 } } }, + { .name = "mkfifoat", .ret_type = 1, .nargs = 3, + .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } }, + { .name = "mknod", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } }, + { .name = "mknodat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } }, + { .name = "mmap", .ret_type = 1, .nargs = 6, + .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, + { Int, 4 }, { QuadHex, 5 + QUAD_ALIGN } } }, + { .name = "modfind", .ret_type = 1, .nargs = 1, + .args = { { Name | IN, 0 } } }, + { .name = "mount", .ret_type = 1, .nargs = 4, + .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, + { .name = "mprotect", .ret_type = 1, .nargs = 3, + .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, + { .name = "munmap", .ret_type = 1, .nargs = 2, + .args = { { Ptr, 0 }, { Int, 1 } } }, + { .name = "nanosleep", .ret_type = 1, .nargs = 1, + .args = { { Timespec, 0 } } }, + { .name = "open", .ret_type = 1, .nargs = 3, + .args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } }, + { .name = "openat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 }, + { Octal, 3 } } }, { .name = "pathconf", .ret_type = 1, .nargs = 2, .args = { { Name | IN, 0 }, { Pathconf, 1 } } }, { .name = "pipe", .ret_type = 1, .nargs = 1, - .args = { { Ptr, 0 } } }, - { .name = "truncate", .ret_type = 1, .nargs = 3, - .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, - { .name = "ftruncate", .ret_type = 1, .nargs = 3, - .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, - { .name = "kill", .ret_type = 1, .nargs = 2, - .args = { { Int | IN, 0 }, { Signal | IN, 1 } } }, - { .name = "munmap", .ret_type = 1, .nargs = 2, - .args = { { Ptr, 0 }, { Int, 1 } } }, + .args = { { PipeFds | OUT, 0 } } }, + { .name = "pipe2", .ret_type = 1, .nargs = 2, + .args = { { Ptr, 0 }, { Open, 1 } } }, + { .name = "poll", .ret_type = 1, .nargs = 3, + .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, + { .name = "posix_openpt", .ret_type = 1, .nargs = 1, + .args = { { Open, 0 } } }, + { .name = "procctl", .ret_type = 1, .nargs = 4, + .args = { { Idtype, 0 }, { Quad, 1 + QUAD_ALIGN }, + { Procctl, 1 + QUAD_ALIGN + QUAD_SLOTS }, + { Ptr, 2 + QUAD_ALIGN + QUAD_SLOTS } } }, { .name = "read", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } }, + { .name = "readlink", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } }, + { .name = "readlinkat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name, 1 }, { Readlinkres | OUT, 2 }, + { Int, 3 } } }, + { .name = "recvfrom", .ret_type = 1, .nargs = 6, + .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, + { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, { .name = "rename", .ret_type = 1, .nargs = 2, - .args = { { Name , 0 } , { Name, 1 } } }, + .args = { { Name, 0 }, { Name, 1 } } }, + { .name = "renameat", .ret_type = 1, .nargs = 4, + .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } }, + { .name = "rfork", .ret_type = 1, .nargs = 1, + .args = { { Rforkflags, 0 } } }, + { .name = "select", .ret_type = 1, .nargs = 5, + .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, + { Timeval, 4 } } }, + { .name = "sendto", .ret_type = 1, .nargs = 6, + .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, + { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, + { .name = "setitimer", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Itimerval, 1 }, { Itimerval | OUT, 2 } } }, + { .name = "setrlimit", .ret_type = 1, .nargs = 2, + .args = { { Resource, 0 }, { Rlimit | IN, 1 } } }, + { .name = "shutdown", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Shutdown, 1 } } }, + { .name = "sigaction", .ret_type = 1, .nargs = 3, + .args = { { Signal, 0 }, { Sigaction | IN, 1 }, + { Sigaction | OUT, 2 } } }, + { .name = "sigpending", .ret_type = 1, .nargs = 1, + .args = { { Sigset | OUT, 0 } } }, + { .name = "sigprocmask", .ret_type = 1, .nargs = 3, + .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, + { .name = "sigqueue", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } }, + { .name = "sigreturn", .ret_type = 1, .nargs = 1, + .args = { { Ptr, 0 } } }, + { .name = "sigsuspend", .ret_type = 1, .nargs = 1, + .args = { { Sigset | IN, 0 } } }, + { .name = "sigtimedwait", .ret_type = 1, .nargs = 3, + .args = { { Sigset | IN, 0 }, { Ptr, 1 }, { Timespec | IN, 2 } } }, + { .name = "sigwait", .ret_type = 1, .nargs = 2, + .args = { { Sigset | IN, 0 }, { Ptr, 1 } } }, + { .name = "sigwaitinfo", .ret_type = 1, .nargs = 2, + .args = { { Sigset | IN, 0 }, { Ptr, 1 } } }, + { .name = "socket", .ret_type = 1, .nargs = 3, + .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, + { .name = "stat", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, + { .name = "statfs", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { StatFs | OUT, 1 } } }, { .name = "symlink", .ret_type = 1, .nargs = 2, - .args = { { Name , 0 } , { Name, 1 } } }, - { .name = "posix_openpt", .ret_type = 1, .nargs = 1, - .args = { { Open, 0 } } }, + .args = { { Name, 0 }, { Name, 1 } } }, + { .name = "symlinkat", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } }, + { .name = "sysarch", .ret_type = 1, .nargs = 2, + .args = { { Sysarch, 0 }, { Ptr, 1 } } }, + { .name = "thr_kill", .ret_type = 1, .nargs = 2, + .args = { { Long, 0 }, { Signal, 1 } } }, + { .name = "thr_self", .ret_type = 1, .nargs = 1, + .args = { { Ptr, 0 } } }, + { .name = "truncate", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { QuadHex | IN, 1 + QUAD_ALIGN } } }, +#if 0 + /* Does not exist */ + { .name = "umount", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Int, 2 } } }, +#endif + { .name = "unlink", .ret_type = 1, .nargs = 1, + .args = { { Name, 0 } } }, + { .name = "unlinkat", .ret_type = 1, .nargs = 3, + .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } }, + { .name = "unmount", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Int, 1 } } }, + { .name = "utimes", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, { .name = "wait4", .ret_type = 1, .nargs = 4, .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 }, { Rusage | OUT, 3 } } }, { .name = "wait6", .ret_type = 1, .nargs = 6, - .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 }, - { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } }, - { .name = "procctl", .ret_type = 1, .nargs = 4, - .args = { { Idtype, 0 }, { Int, 1 }, { Procctl, 2 }, { Ptr, 3 } } }, - { .name = "_umtx_op", .ret_type = 1, .nargs = 5, - .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 }, - { Ptr, 4 } } }, + .args = { { Idtype, 0 }, { Quad, 1 + QUAD_ALIGN }, + { ExitStatus | OUT, 1 + QUAD_ALIGN + QUAD_SLOTS }, + { Waitoptions, 2 + QUAD_ALIGN + QUAD_SLOTS }, + { Rusage | OUT, 3 + QUAD_ALIGN + QUAD_SLOTS }, + { Ptr, 4 + QUAD_ALIGN + QUAD_SLOTS } } }, + { .name = "write", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, + + /* Linux ABI */ + { .name = "linux_access", .ret_type = 1, .nargs = 2, + .args = { { Name, 0 }, { Accessmode, 1 } } }, + { .name = "linux_execve", .ret_type = 1, .nargs = 3, + .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 }, + { ExecEnv | IN, 2 } } }, + { .name = "linux_lseek", .ret_type = 2, .nargs = 3, + .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, + { .name = "linux_mkdir", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Int, 1 } } }, + { .name = "linux_newfstat", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { Ptr | OUT, 1 } } }, + { .name = "linux_newstat", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, + { .name = "linux_open", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, + { .name = "linux_readlink", .ret_type = 1, .nargs = 3, + .args = { { Name, 0 }, { Name | OUT, 1 }, { Int, 2 } } }, + { .name = "linux_socketcall", .ret_type = 1, .nargs = 2, + .args = { { Int, 0 }, { LinuxSockArgs, 1 } } }, + { .name = "linux_stat64", .ret_type = 1, .nargs = 3, + .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 } } }, + { .name = 0 }, }; +static STAILQ_HEAD(, syscall) syscalls; /* Xlat idea taken from strace */ struct xlat { @@ -301,12 +394,37 @@ struct xlat { static struct xlat kevent_filters[] = { X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE) X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER) - X(EVFILT_FS) X(EVFILT_READ) XEND + X(EVFILT_FS) X(EVFILT_LIO) X(EVFILT_USER) XEND }; static struct xlat kevent_flags[] = { X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT) - X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND + X(EV_CLEAR) X(EV_RECEIPT) X(EV_DISPATCH) + X(EV_DROP) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND +}; + +static struct xlat kevent_user_ffctrl[] = { + X(NOTE_FFNOP) X(NOTE_FFAND) X(NOTE_FFOR) X(NOTE_FFCOPY) + XEND +}; + +static struct xlat kevent_rdwr_fflags[] = { + X(NOTE_LOWAT) XEND +}; + +static struct xlat kevent_vnode_fflags[] = { + X(NOTE_DELETE) X(NOTE_WRITE) X(NOTE_EXTEND) X(NOTE_ATTRIB) + X(NOTE_LINK) X(NOTE_RENAME) X(NOTE_REVOKE) XEND +}; + +static struct xlat kevent_proc_fflags[] = { + X(NOTE_EXIT) X(NOTE_FORK) X(NOTE_EXEC) X(NOTE_TRACK) X(NOTE_TRACKERR) + X(NOTE_CHILD) XEND +}; + +static struct xlat kevent_timer_fflags[] = { + X(NOTE_SECONDS) X(NOTE_MSECONDS) X(NOTE_USECONDS) X(NOTE_NSECONDS) + XEND }; static struct xlat poll_flags[] = { @@ -319,7 +437,7 @@ static struct xlat mmap_flags[] = { X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME) X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100) X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON) - X(MAP_NOCORE) X(MAP_PREFAULT_READ) + X(MAP_EXCL) X(MAP_NOCORE) X(MAP_PREFAULT_READ) #ifdef MAP_32BIT X(MAP_32BIT) #endif @@ -331,7 +449,7 @@ static struct xlat mprot_flags[] = { }; static struct xlat whence_arg[] = { - X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND + X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) X(SEEK_DATA) X(SEEK_HOLE) XEND }; static struct xlat sigaction_flags[] = { @@ -341,7 +459,10 @@ static struct xlat sigaction_flags[] = { static struct xlat fcntl_arg[] = { X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL) - X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND + X(F_GETOWN) X(F_SETOWN) X(F_OGETLK) X(F_OSETLK) X(F_OSETLKW) + X(F_DUP2FD) X(F_GETLK) X(F_SETLK) X(F_SETLKW) X(F_SETLK_REMOTE) + X(F_READAHEAD) X(F_RDAHEAD) X(F_DUPFD_CLOEXEC) X(F_DUP2FD_CLOEXEC) + XEND }; static struct xlat fcntlfd_arg[] = { @@ -350,7 +471,7 @@ static struct xlat fcntlfd_arg[] = { static struct xlat fcntlfl_arg[] = { X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW) - X(O_DIRECT) XEND + X(FRDAHEAD) X(O_DIRECT) XEND }; static struct xlat sockdomain_arg[] = { @@ -361,7 +482,8 @@ static struct xlat sockdomain_arg[] = { X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX) X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6) X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER) - X(PF_ARP) X(PF_BLUETOOTH) XEND + X(PF_ARP) X(PF_BLUETOOTH) X(PF_IEEE80211) X(PF_INET_SDP) + X(PF_INET6_SDP) XEND }; static struct xlat socktype_arg[] = { @@ -373,7 +495,8 @@ static struct xlat open_flags[] = { X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK) X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC) X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY) - X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) XEND + X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) + XEND }; static struct xlat shutdown_arg[] = { @@ -383,7 +506,8 @@ static struct xlat shutdown_arg[] = { static struct xlat resource_arg[] = { X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK) X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC) - X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND + X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) X(RLIMIT_NPTS) + X(RLIMIT_SWAP) XEND }; static struct xlat pathconf_arg[] = { @@ -396,12 +520,12 @@ static struct xlat pathconf_arg[] = { X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN) X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX) X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT) - XEND + X(_PC_ACL_NFS4) X(_PC_MIN_HOLE_SIZE) XEND }; static struct xlat rfork_flags[] = { - X(RFPROC) X(RFNOWAIT) X(RFFDG) X(RFCFDG) X(RFTHREAD) X(RFMEM) - X(RFSIGSHARE) X(RFTSIGZMB) X(RFLINUXTHPN) XEND + X(RFFDG) X(RFPROC) X(RFMEM) X(RFNOWAIT) X(RFCFDG) X(RFTHREAD) + X(RFSIGSHARE) X(RFLINUXTHPN) X(RFTSIGZMB) X(RFPPWAIT) XEND }; static struct xlat wait_options[] = { @@ -416,7 +540,9 @@ static struct xlat idtype_arg[] = { }; static struct xlat procctl_arg[] = { - X(PROC_SPROTECT) XEND + X(PROC_SPROTECT) X(PROC_REAP_ACQUIRE) X(PROC_REAP_RELEASE) + X(PROC_REAP_STATUS) X(PROC_REAP_GETPIDS) X(PROC_REAP_KILL) + X(PROC_TRACE_CTL) X(PROC_TRACE_STATUS) XEND }; static struct xlat umtx_ops[] = { @@ -431,6 +557,40 @@ static struct xlat umtx_ops[] = { XEND }; +static struct xlat at_flags[] = { + X(AT_EACCESS) X(AT_SYMLINK_NOFOLLOW) X(AT_SYMLINK_FOLLOW) + X(AT_REMOVEDIR) XEND +}; + +static struct xlat access_modes[] = { + X(R_OK) X(W_OK) X(X_OK) XEND +}; + +static struct xlat sysarch_ops[] = { +#if defined(__i386__) || defined(__amd64__) + X(I386_GET_LDT) X(I386_SET_LDT) X(I386_GET_IOPERM) X(I386_SET_IOPERM) + X(I386_VM86) X(I386_GET_FSBASE) X(I386_SET_FSBASE) X(I386_GET_GSBASE) + X(I386_SET_GSBASE) X(I386_GET_XFPUSTATE) X(AMD64_GET_FSBASE) + X(AMD64_SET_FSBASE) X(AMD64_GET_GSBASE) X(AMD64_SET_GSBASE) + X(AMD64_GET_XFPUSTATE) +#endif + XEND +}; + +static struct xlat linux_socketcall_ops[] = { + X(LINUX_SOCKET) X(LINUX_BIND) X(LINUX_CONNECT) X(LINUX_LISTEN) + X(LINUX_ACCEPT) X(LINUX_GETSOCKNAME) X(LINUX_GETPEERNAME) + X(LINUX_SOCKETPAIR) X(LINUX_SEND) X(LINUX_RECV) X(LINUX_SENDTO) + X(LINUX_RECVFROM) X(LINUX_SHUTDOWN) X(LINUX_SETSOCKOPT) + X(LINUX_GETSOCKOPT) X(LINUX_SENDMSG) X(LINUX_RECVMSG) + XEND +}; + +static struct xlat sigprocmask_ops[] = { + X(SIG_BLOCK) X(SIG_UNBLOCK) X(SIG_SETMASK) + XEND +}; + #undef X #undef XEND @@ -470,9 +630,11 @@ xlookup(struct xlat *xlat, int val) return (lookup(xlat, val, 16)); } -/* Searches an xlat array containing bitfield values. Remaining bits - set after removing the known ones are printed at the end: - IN|0x400 */ +/* + * Searches an xlat array containing bitfield values. Remaining bits + * set after removing the known ones are printed at the end: + * IN|0x400. + */ static char * xlookup_bits(struct xlat *xlat, int val) { @@ -483,15 +645,21 @@ xlookup_bits(struct xlat *xlat, int val) rem = val; for (; xlat->str != NULL; xlat++) { if ((xlat->val & rem) == xlat->val) { - /* don't print the "all-bits-zero" string unless all - bits are really zero */ + /* + * Don't print the "all-bits-zero" string unless all + * bits are really zero. + */ if (xlat->val == 0 && val != 0) continue; len += sprintf(str + len, "%s|", xlat->str); rem &= ~(xlat->val); } } - /* if we have leftover bits or didn't match anything */ + + /* + * If we have leftover bits or didn't match anything, print + * the remainder. + */ if (rem || len == 0) len += sprintf(str + len, "0x%x", rem); if (len && str[len - 1] == '|') @@ -500,33 +668,54 @@ xlookup_bits(struct xlat *xlat, int val) return (str); } +void +init_syscalls(void) +{ + struct syscall *sc; + + STAILQ_INIT(&syscalls); + for (sc = decoded_syscalls; sc->name != NULL; sc++) + STAILQ_INSERT_HEAD(&syscalls, sc, entries); +} /* * If/when the list gets big, it might be desirable to do it * as a hash table or binary search. */ - struct syscall * -get_syscall(const char *name) +get_syscall(const char *name, int nargs) { struct syscall *sc; + int i; - sc = syscalls; if (name == NULL) return (NULL); - while (sc->name) { + STAILQ_FOREACH(sc, &syscalls, entries) if (strcmp(name, sc->name) == 0) return (sc); - sc++; + + /* It is unknown. Add it into the list. */ +#if DEBUG + fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name, + nargs); +#endif + + sc = calloc(1, sizeof(struct syscall)); + sc->name = strdup(name); + sc->ret_type = 1; + sc->nargs = nargs; + for (i = 0; i < nargs; i++) { + sc->args[i].offset = i; + /* Treat all unknown arguments as LongHex. */ + sc->args[i].type = LongHex; } - return (NULL); + STAILQ_INSERT_HEAD(&syscalls, sc, entries); + + return (sc); } /* - * get_struct - * * Copy a fixed amount of bytes from the process. */ - static int get_struct(pid_t pid, void *offset, void *buf, int len) { @@ -542,44 +731,55 @@ get_struct(pid_t pid, void *offset, void *buf, int len) } #define MAXSIZE 4096 -#define BLOCKSIZE 1024 + /* - * get_string * Copy a string from the process. Note that it is * expected to be a C string, but if max is set, it will * only get that much. */ - static char * -get_string(pid_t pid, void *offset, int max) +get_string(pid_t pid, void *addr, int max) { struct ptrace_io_desc iorequest; - char *buf; - int diff, i, size, totalsize; + char *buf, *nbuf; + size_t offset, size, totalsize; - diff = 0; - totalsize = size = max ? (max + 1) : BLOCKSIZE; + offset = 0; + if (max) + size = max + 1; + else { + /* Read up to the end of the current page. */ + size = PAGE_SIZE - ((uintptr_t)addr % PAGE_SIZE); + if (size > MAXSIZE) + size = MAXSIZE; + } + totalsize = size; buf = malloc(totalsize); if (buf == NULL) return (NULL); for (;;) { - diff = totalsize - size; iorequest.piod_op = PIOD_READ_D; - iorequest.piod_offs = (char *)offset + diff; - iorequest.piod_addr = buf + diff; + iorequest.piod_offs = (char *)addr + offset; + iorequest.piod_addr = buf + offset; iorequest.piod_len = size; if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { free(buf); return (NULL); } - for (i = 0 ; i < size; i++) { - if (buf[diff + i] == '\0') + if (memchr(buf + offset, '\0', size) != NULL) + return (buf); + offset += size; + if (totalsize < MAXSIZE && max == 0) { + size = MAXSIZE - totalsize; + if (size > PAGE_SIZE) + size = PAGE_SIZE; + nbuf = realloc(buf, totalsize + size); + if (nbuf == NULL) { + buf[totalsize - 1] = '\0'; return (buf); - } - if (totalsize < MAXSIZE - BLOCKSIZE && max == 0) { - totalsize += BLOCKSIZE; - buf = realloc(buf, totalsize); - size = BLOCKSIZE; + } + buf = nbuf; + totalsize += size; } else { buf[totalsize - 1] = '\0'; return (buf); @@ -590,72 +790,136 @@ get_string(pid_t pid, void *offset, int max) static char * strsig2(int sig) { - char *tmp; + static char tmp[sizeof(int) * 3 + 1]; + char *ret; - tmp = strsig(sig); - if (tmp == NULL) - asprintf(&tmp, "%d", sig); - return (tmp); + ret = strsig(sig); + if (ret == NULL) { + snprintf(tmp, sizeof(tmp), "%d", sig); + ret = tmp; + } + return (ret); +} + +static void +print_kevent(FILE *fp, struct kevent *ke, int input) +{ + + switch (ke->filter) { + case EVFILT_READ: + case EVFILT_WRITE: + case EVFILT_VNODE: + case EVFILT_PROC: + case EVFILT_TIMER: + fprintf(fp, "%ju", (uintmax_t)ke->ident); + break; + case EVFILT_SIGNAL: + fputs(strsig2(ke->ident), fp); + break; + default: + fprintf(fp, "%p", (void *)ke->ident); + } + fprintf(fp, ",%s,%s,", xlookup(kevent_filters, ke->filter), + xlookup_bits(kevent_flags, ke->flags)); + switch (ke->filter) { + case EVFILT_READ: + case EVFILT_WRITE: + fputs(xlookup_bits(kevent_rdwr_fflags, ke->fflags), fp); + break; + case EVFILT_VNODE: + fputs(xlookup_bits(kevent_vnode_fflags, ke->fflags), fp); + break; + case EVFILT_PROC: + fputs(xlookup_bits(kevent_proc_fflags, ke->fflags), fp); + break; + case EVFILT_TIMER: + fputs(xlookup_bits(kevent_timer_fflags, ke->fflags), fp); + break; + case EVFILT_USER: { + int ctrl, data; + + ctrl = ke->fflags & NOTE_FFCTRLMASK; + data = ke->fflags & NOTE_FFLAGSMASK; + if (input) { + fputs(xlookup(kevent_user_ffctrl, ctrl), fp); + if (ke->fflags & NOTE_TRIGGER) + fputs("|NOTE_TRIGGER", fp); + if (data != 0) + fprintf(fp, "|%#x", data); + } else { + fprintf(fp, "%#x", data); + } + break; + } + default: + fprintf(fp, "%#x", ke->fflags); + } + fprintf(fp, ",%p,%p", (void *)ke->data, (void *)ke->udata); } /* - * print_arg * Converts a syscall argument into a string. Said string is - * allocated via malloc(), so needs to be free()'d. The file - * descriptor is for the process' memory (via /proc), and is used - * to get any data (where the argument is a pointer). sc is + * allocated via malloc(), so needs to be free()'d. sc is * a pointer to the syscall description (see above); args is * an array of all of the system call arguments. */ - char * -print_arg(struct syscall_args *sc, unsigned long *args, long retval, +print_arg(struct syscall_args *sc, unsigned long *args, long *retval, struct trussinfo *trussinfo) { + FILE *fp; char *tmp; + size_t tmplen; pid_t pid; - tmp = NULL; - pid = trussinfo->pid; + fp = open_memstream(&tmp, &tmplen); + pid = trussinfo->curthread->proc->pid; switch (sc->type & ARG_MASK) { case Hex: - asprintf(&tmp, "0x%x", (int)args[sc->offset]); + fprintf(fp, "0x%x", (int)args[sc->offset]); break; case Octal: - asprintf(&tmp, "0%o", (int)args[sc->offset]); + fprintf(fp, "0%o", (int)args[sc->offset]); break; case Int: - asprintf(&tmp, "%d", (int)args[sc->offset]); + fprintf(fp, "%d", (int)args[sc->offset]); break; case LongHex: - asprintf(&tmp, "0x%lx", args[sc->offset]); - break; + fprintf(fp, "0x%lx", args[sc->offset]); + break; + case Long: + fprintf(fp, "%ld", args[sc->offset]); + break; case Name: { /* NULL-terminated string. */ char *tmp2; + tmp2 = get_string(pid, (void*)args[sc->offset], 0); - asprintf(&tmp, "\"%s\"", tmp2); + fprintf(fp, "\"%s\"", tmp2); free(tmp2); break; } case BinString: { - /* Binary block of data that might have printable characters. - XXX If type|OUT, assume that the length is the syscall's - return value. Otherwise, assume that the length of the block - is in the next syscall argument. */ + /* + * Binary block of data that might have printable characters. + * XXX If type|OUT, assume that the length is the syscall's + * return value. Otherwise, assume that the length of the block + * is in the next syscall argument. + */ int max_string = trussinfo->strsize; - char tmp2[max_string+1], *tmp3; + char tmp2[max_string + 1], *tmp3; int len; int truncated = 0; if (sc->type & OUT) - len = retval; + len = retval[0]; else len = args[sc->offset + 1]; - /* Don't print more than max_string characters, to avoid word - wrap. If we have to truncate put some ... after the string. - */ + /* + * Don't print more than max_string characters, to avoid word + * wrap. If we have to truncate put some ... after the string. + */ if (len > max_string) { len = max_string; truncated = 1; @@ -670,84 +934,132 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, len--; truncated = 1; }; - asprintf(&tmp, "\"%s\"%s", tmp3, truncated ? + fprintf(fp, "\"%s\"%s", tmp3, truncated ? "..." : ""); free(tmp3); } else { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); } break; } + case ExecArgs: + case ExecEnv: case StringArray: { - int num, size, i; - char *tmp2; + uintptr_t addr; + union { + char *strarray[0]; + char buf[PAGE_SIZE]; + } u; char *string; - char *strarray[100]; /* XXX This is ugly. */ - - if (get_struct(pid, (void *)args[sc->offset], - (void *)&strarray, sizeof(strarray)) == -1) - err(1, "get_struct %p", (void *)args[sc->offset]); - num = 0; - size = 0; - - /* Find out how large of a buffer we'll need. */ - while (strarray[num] != NULL) { - string = get_string(pid, (void*)strarray[num], 0); - size += strlen(string); - free(string); - num++; + size_t len; + u_int first, i; + + /* + * Only parse argv[] and environment arrays from exec calls + * if requested. + */ + if (((sc->type & ARG_MASK) == ExecArgs && + (trussinfo->flags & EXECVEARGS) == 0) || + ((sc->type & ARG_MASK) == ExecEnv && + (trussinfo->flags & EXECVEENVS) == 0)) { + fprintf(fp, "0x%lx", args[sc->offset]); + break; + } + + /* + * Read a page of pointers at a time. Punt if the top-level + * pointer is not aligned. Note that the first read is of + * a partial page. + */ + addr = args[sc->offset]; + if (addr % sizeof(char *) != 0) { + fprintf(fp, "0x%lx", args[sc->offset]); + break; + } + + len = PAGE_SIZE - (addr & PAGE_MASK); + if (get_struct(pid, (void *)addr, u.buf, len) == -1) { + fprintf(fp, "0x%lx", args[sc->offset]); + break; } - size += 4 + (num * 4); - tmp = (char *)malloc(size); - tmp2 = tmp; - - tmp2 += sprintf(tmp2, " ["); - for (i = 0; i < num; i++) { - string = get_string(pid, (void*)strarray[i], 0); - tmp2 += sprintf(tmp2, " \"%s\"%c", string, - (i + 1 == num) ? ' ' : ','); + + fputc('[', fp); + first = 1; + i = 0; + while (u.strarray[i] != NULL) { + string = get_string(pid, u.strarray[i], 0); + fprintf(fp, "%s \"%s\"", first ? "" : ",", string); free(string); + first = 0; + + i++; + if (i == len / sizeof(char *)) { + addr += len; + len = PAGE_SIZE; + if (get_struct(pid, (void *)addr, u.buf, len) == + -1) { + fprintf(fp, ", <inval>"); + break; + } + i = 0; + } } - tmp2 += sprintf(tmp2, "]"); + fputs(" ]", fp); break; } #ifdef __LP64__ case Quad: - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "%ld", args[sc->offset]); + break; + case QuadHex: + fprintf(fp, "0x%lx", args[sc->offset]); break; #else - case Quad: { + case Quad: + case QuadHex: { unsigned long long ll; - ll = *(unsigned long long *)(args + sc->offset); - asprintf(&tmp, "0x%llx", ll); + +#if _BYTE_ORDER == _LITTLE_ENDIAN + ll = (unsigned long long)args[sc->offset + 1] << 32 | + args[sc->offset]; +#else + ll = (unsigned long long)args[sc->offset] << 32 | + args[sc->offset + 1]; +#endif + if ((sc->type & ARG_MASK) == Quad) + fprintf(fp, "%lld", ll); + else + fprintf(fp, "0x%llx", ll); break; } #endif case Ptr: - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; case Readlinkres: { char *tmp2; - if (retval == -1) { - tmp = strdup(""); + + if (retval[0] == -1) break; - } - tmp2 = get_string(pid, (void*)args[sc->offset], retval); - asprintf(&tmp, "\"%s\"", tmp2); + tmp2 = get_string(pid, (void*)args[sc->offset], retval[0]); + fprintf(fp, "\"%s\"", tmp2); free(tmp2); break; } case Ioctl: { - const char *temp = ioctlname(args[sc->offset]); + const char *temp; + unsigned long cmd; + + cmd = args[sc->offset]; + temp = ioctlname(cmd); if (temp) - tmp = strdup(temp); + fputs(temp, fp); else { - unsigned long arg = args[sc->offset]; - asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", - arg, arg & IOC_OUT ? "R" : "", - arg & IOC_IN ? "W" : "", IOCGROUP(arg), - isprint(IOCGROUP(arg)) ? (char)IOCGROUP(arg) : '?', - arg & 0xFF, IOCPARM_LEN(arg)); + fprintf(fp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", + cmd, cmd & IOC_OUT ? "R" : "", + cmd & IOC_IN ? "W" : "", IOCGROUP(cmd), + isprint(IOCGROUP(cmd)) ? (char)IOCGROUP(cmd) : '?', + cmd & 0xFF, IOCPARM_LEN(cmd)); } break; } @@ -755,123 +1067,70 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, struct umtx umtx; if (get_struct(pid, (void *)args[sc->offset], &umtx, sizeof(umtx)) != -1) - asprintf(&tmp, "{ 0x%lx }", (long)umtx.u_owner); + fprintf(fp, "{ 0x%lx }", (long)umtx.u_owner); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Timespec: { struct timespec ts; + if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts)) != -1) - asprintf(&tmp, "{%ld.%09ld }", (long)ts.tv_sec, + fprintf(fp, "{ %jd.%09ld }", (intmax_t)ts.tv_sec, ts.tv_nsec); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Timeval: { struct timeval tv; + if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1) - asprintf(&tmp, "{%ld.%06ld }", (long)tv.tv_sec, + fprintf(fp, "{ %jd.%06ld }", (intmax_t)tv.tv_sec, tv.tv_usec); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Timeval2: { struct timeval tv[2]; + if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1) - asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", - (long)tv[0].tv_sec, tv[0].tv_usec, - (long)tv[1].tv_sec, tv[1].tv_usec); + fprintf(fp, "{ %jd.%06ld, %jd.%06ld }", + (intmax_t)tv[0].tv_sec, tv[0].tv_usec, + (intmax_t)tv[1].tv_sec, tv[1].tv_usec); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Itimerval: { struct itimerval itv; + if (get_struct(pid, (void *)args[sc->offset], &itv, sizeof(itv)) != -1) - asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", - (long)itv.it_interval.tv_sec, + fprintf(fp, "{ %jd.%06ld, %jd.%06ld }", + (intmax_t)itv.it_interval.tv_sec, itv.it_interval.tv_usec, - (long)itv.it_value.tv_sec, + (intmax_t)itv.it_value.tv_sec, itv.it_value.tv_usec); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case LinuxSockArgs: { struct linux_socketcall_args largs; - if (get_struct(pid, (void *)args[sc->offset], (void *)&largs, - sizeof(largs)) == -1) { - err(1, "get_struct %p", (void *)args[sc->offset]); - } - const char *what; - char buf[30]; - switch (largs.what) { - case LINUX_SOCKET: - what = "LINUX_SOCKET"; - break; - case LINUX_BIND: - what = "LINUX_BIND"; - break; - case LINUX_CONNECT: - what = "LINUX_CONNECT"; - break; - case LINUX_LISTEN: - what = "LINUX_LISTEN"; - break; - case LINUX_ACCEPT: - what = "LINUX_ACCEPT"; - break; - case LINUX_GETSOCKNAME: - what = "LINUX_GETSOCKNAME"; - break; - case LINUX_GETPEERNAME: - what = "LINUX_GETPEERNAME"; - break; - case LINUX_SOCKETPAIR: - what = "LINUX_SOCKETPAIR"; - break; - case LINUX_SEND: - what = "LINUX_SEND"; - break; - case LINUX_RECV: - what = "LINUX_RECV"; - break; - case LINUX_SENDTO: - what = "LINUX_SENDTO"; - break; - case LINUX_RECVFROM: - what = "LINUX_RECVFROM"; - break; - case LINUX_SHUTDOWN: - what = "LINUX_SHUTDOWN"; - break; - case LINUX_SETSOCKOPT: - what = "LINUX_SETSOCKOPT"; - break; - case LINUX_GETSOCKOPT: - what = "LINUX_GETSOCKOPT"; - break; - case LINUX_SENDMSG: - what = "LINUX_SENDMSG"; - break; - case LINUX_RECVMSG: - what = "LINUX_RECVMSG"; - break; - default: - sprintf(buf, "%d", largs.what); - what = buf; - break; - } - asprintf(&tmp, "(0x%lx)%s, 0x%lx", args[sc->offset], what, (long unsigned int)largs.args); + if (get_struct(pid, (void *)args[sc->offset], (void *)&largs, + sizeof(largs)) != -1) + fprintf(fp, "{ %s, 0x%lx }", + lookup(linux_socketcall_ops, largs.what, 10), + (long unsigned int)largs.args); + else + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Pollfd: { @@ -881,35 +1140,23 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, * syscall. */ struct pollfd *pfd; - int numfds = args[sc->offset+1]; - int bytes = sizeof(struct pollfd) * numfds; - int i, tmpsize, u, used; - const int per_fd = 100; + int numfds = args[sc->offset + 1]; + size_t bytes = sizeof(struct pollfd) * numfds; + int i; if ((pfd = malloc(bytes)) == NULL) - err(1, "Cannot malloc %d bytes for pollfd array", + err(1, "Cannot malloc %zu bytes for pollfd array", bytes); if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) != -1) { - used = 0; - tmpsize = 1 + per_fd * numfds + 2; - if ((tmp = malloc(tmpsize)) == NULL) - err(1, "Cannot alloc %d bytes for poll output", - tmpsize); - - tmp[used++] = '{'; + fputs("{", fp); for (i = 0; i < numfds; i++) { - - u = snprintf(tmp + used, per_fd, "%s%d/%s", - i > 0 ? " " : "", pfd[i].fd, + fprintf(fp, " %d/%s", pfd[i].fd, xlookup_bits(poll_flags, pfd[i].events)); - if (u > 0) - used += u < per_fd ? u : per_fd; } - tmp[used++] = '}'; - tmp[used++] = '\0'; + fputs(" }", fp); } else { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); } free(pfd); break; @@ -922,114 +1169,86 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, */ fd_set *fds; int numfds = args[0]; - int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; - int i, tmpsize, u, used; - const int per_fd = 20; + size_t bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; + int i; if ((fds = malloc(bytes)) == NULL) - err(1, "Cannot malloc %d bytes for fd_set array", + err(1, "Cannot malloc %zu bytes for fd_set array", bytes); if (get_struct(pid, (void *)args[sc->offset], fds, bytes) != -1) { - used = 0; - tmpsize = 1 + numfds * per_fd + 2; - if ((tmp = malloc(tmpsize)) == NULL) - err(1, "Cannot alloc %d bytes for fd_set " - "output", tmpsize); - - tmp[used++] = '{'; + fputs("{", fp); for (i = 0; i < numfds; i++) { - if (FD_ISSET(i, fds)) { - u = snprintf(tmp + used, per_fd, "%d ", - i); - if (u > 0) - used += u < per_fd ? u : per_fd; - } + if (FD_ISSET(i, fds)) + fprintf(fp, " %d", i); } - if (tmp[used-1] == ' ') - used--; - tmp[used++] = '}'; - tmp[used++] = '\0'; + fputs(" }", fp); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); free(fds); break; } case Signal: - tmp = strsig2(args[sc->offset]); + fputs(strsig2(args[sc->offset]), fp); break; case Sigset: { long sig; sigset_t ss; - int i, used; - char *signame; + int i, first; sig = args[sc->offset]; if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, sizeof(ss)) == -1) { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } - tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */ - used = 0; + fputs("{ ", fp); + first = 1; for (i = 1; i < sys_nsig; i++) { if (sigismember(&ss, i)) { - signame = strsig(i); - used += sprintf(tmp + used, "%s|", signame); - free(signame); + fprintf(fp, "%s%s", !first ? "|" : "", + strsig(i)); + first = 0; } } - if (used) - tmp[used-1] = 0; - else - strcpy(tmp, "0x0"); + if (!first) + fputc(' ', fp); + fputc('}', fp); break; } case Sigprocmask: { - switch (args[sc->offset]) { -#define S(a) case a: tmp = strdup(#a); break; - S(SIG_BLOCK); - S(SIG_UNBLOCK); - S(SIG_SETMASK); -#undef S - } - if (tmp == NULL) - asprintf(&tmp, "0x%lx", args[sc->offset]); + fputs(xlookup(sigprocmask_ops, args[sc->offset]), fp); break; } case Fcntlflag: { - /* XXX output depends on the value of the previous argument */ - switch (args[sc->offset-1]) { + /* XXX: Output depends on the value of the previous argument. */ + switch (args[sc->offset - 1]) { case F_SETFD: - tmp = strdup(xlookup_bits(fcntlfd_arg, - args[sc->offset])); + fputs(xlookup_bits(fcntlfd_arg, args[sc->offset]), fp); break; case F_SETFL: - tmp = strdup(xlookup_bits(fcntlfl_arg, - args[sc->offset])); + fputs(xlookup_bits(fcntlfl_arg, args[sc->offset]), fp); break; case F_GETFD: case F_GETFL: case F_GETOWN: - tmp = strdup(""); break; default: - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } break; } case Open: - tmp = strdup(xlookup_bits(open_flags, args[sc->offset])); + fputs(xlookup_bits(open_flags, args[sc->offset]), fp); break; case Fcntl: - tmp = strdup(xlookup(fcntl_arg, args[sc->offset])); + fputs(xlookup(fcntl_arg, args[sc->offset]), fp); break; case Mprot: - tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset])); + fputs(xlookup_bits(mprot_flags, args[sc->offset]), fp); break; case Mmapflags: { - char *base, *alignstr; int align, flags; /* @@ -1043,285 +1262,332 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval, align = args[sc->offset] & MAP_ALIGNMENT_MASK; if (align != 0) { if (align == MAP_ALIGNED_SUPER) - alignstr = strdup("MAP_ALIGNED_SUPER"); + fputs("MAP_ALIGNED_SUPER", fp); else - asprintf(&alignstr, "MAP_ALIGNED(%d)", + fprintf(fp, "MAP_ALIGNED(%d)", align >> MAP_ALIGNMENT_SHIFT); - if (flags == 0) { - tmp = alignstr; + if (flags == 0) break; - } - } else - alignstr = NULL; - base = strdup(xlookup_bits(mmap_flags, flags)); - if (alignstr == NULL) { - tmp = base; - break; + fputc('|', fp); } - asprintf(&tmp, "%s|%s", alignstr, base); - free(alignstr); - free(base); + fputs(xlookup_bits(mmap_flags, flags), fp); break; } case Whence: - tmp = strdup(xlookup(whence_arg, args[sc->offset])); + fputs(xlookup(whence_arg, args[sc->offset]), fp); break; case Sockdomain: - tmp = strdup(xlookup(sockdomain_arg, args[sc->offset])); + fputs(xlookup(sockdomain_arg, args[sc->offset]), fp); break; - case Socktype: - tmp = strdup(xlookup(socktype_arg, args[sc->offset])); + case Socktype: { + int type, flags; + + flags = args[sc->offset] & (SOCK_CLOEXEC | SOCK_NONBLOCK); + type = args[sc->offset] & ~flags; + fputs(xlookup(socktype_arg, type), fp); + if (flags & SOCK_CLOEXEC) + fprintf(fp, "|SOCK_CLOEXEC"); + if (flags & SOCK_NONBLOCK) + fprintf(fp, "|SOCK_NONBLOCK"); break; + } case Shutdown: - tmp = strdup(xlookup(shutdown_arg, args[sc->offset])); + fputs(xlookup(shutdown_arg, args[sc->offset]), fp); break; case Resource: - tmp = strdup(xlookup(resource_arg, args[sc->offset])); + fputs(xlookup(resource_arg, args[sc->offset]), fp); break; case Pathconf: - tmp = strdup(xlookup(pathconf_arg, args[sc->offset])); + fputs(xlookup(pathconf_arg, args[sc->offset]), fp); break; case Rforkflags: - tmp = strdup(xlookup_bits(rfork_flags, args[sc->offset])); + fputs(xlookup_bits(rfork_flags, args[sc->offset]), fp); break; case Sockaddr: { - struct sockaddr_storage ss; char addr[64]; struct sockaddr_in *lsin; struct sockaddr_in6 *lsin6; struct sockaddr_un *sun; struct sockaddr *sa; - char *p; + socklen_t len; u_char *q; - int i; if (args[sc->offset] == 0) { - asprintf(&tmp, "NULL"); + fputs("NULL", fp); break; } - /* yuck: get ss_len */ - if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, - sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) - err(1, "get_struct %p", (void *)args[sc->offset]); /* - * If ss_len is 0, then try to guess from the sockaddr type. - * AF_UNIX may be initialized incorrectly, so always frob - * it by using the "right" size. + * Extract the address length from the next argument. If + * this is an output sockaddr (OUT is set), then the + * next argument is a pointer to a socklen_t. Otherwise + * the next argument contains a socklen_t by value. */ - if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) { - switch (ss.ss_family) { - case AF_INET: - ss.ss_len = sizeof(*lsin); - break; - case AF_UNIX: - ss.ss_len = sizeof(*sun); - break; - default: - /* hurrrr */ + if (sc->type & OUT) { + if (get_struct(pid, (void *)args[sc->offset + 1], + &len, sizeof(len)) == -1) { + fprintf(fp, "0x%lx", args[sc->offset]); break; } + } else + len = args[sc->offset + 1]; + + /* If the length is too small, just bail. */ + if (len < sizeof(*sa)) { + fprintf(fp, "0x%lx", args[sc->offset]); + break; } - if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, - ss.ss_len) == -1) { - err(2, "get_struct %p", (void *)args[sc->offset]); + + sa = calloc(1, len); + if (get_struct(pid, (void *)args[sc->offset], sa, len) == -1) { + free(sa); + fprintf(fp, "0x%lx", args[sc->offset]); + break; } - switch (ss.ss_family) { + switch (sa->sa_family) { case AF_INET: - lsin = (struct sockaddr_in *)&ss; - inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); - asprintf(&tmp, "{ AF_INET %s:%d }", addr, + if (len < sizeof(*lsin)) + goto sockaddr_short; + lsin = (struct sockaddr_in *)(void *)sa; + inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof(addr)); + fprintf(fp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port)); break; case AF_INET6: - lsin6 = (struct sockaddr_in6 *)&ss; + if (len < sizeof(*lsin6)) + goto sockaddr_short; + lsin6 = (struct sockaddr_in6 *)(void *)sa; inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, - sizeof addr); - asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, + sizeof(addr)); + fprintf(fp, "{ AF_INET6 [%s]:%d }", addr, htons(lsin6->sin6_port)); break; case AF_UNIX: - sun = (struct sockaddr_un *)&ss; - asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); + sun = (struct sockaddr_un *)sa; + fprintf(fp, "{ AF_UNIX \"%.*s\" }", + (int)(len - offsetof(struct sockaddr_un, sun_path)), + sun->sun_path); break; default: - sa = (struct sockaddr *)&ss; - asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data " - "= {%n%*s } }", (int)sa->sa_len, (int)sa->sa_family, - &i, 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - - (char *)sa)), ""); - if (tmp != NULL) { - p = tmp + i; - for (q = (u_char *)&sa->sa_data; - q < (u_char *)sa + sa->sa_len; q++) - p += sprintf(p, " %#02x,", *q); - } + sockaddr_short: + fprintf(fp, + "{ sa_len = %d, sa_family = %d, sa_data = {", + (int)sa->sa_len, (int)sa->sa_family); + for (q = (u_char *)sa->sa_data; + q < (u_char *)sa + len; q++) + fprintf(fp, "%s 0x%02x", + q == (u_char *)sa->sa_data ? "" : ",", + *q); + fputs(" } }", fp); } + free(sa); break; } case Sigaction: { struct sigaction sa; - char *hand; - const char *h; if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) != -1) { - asprintf(&hand, "%p", sa.sa_handler); + fputs("{ ", fp); if (sa.sa_handler == SIG_DFL) - h = "SIG_DFL"; + fputs("SIG_DFL", fp); else if (sa.sa_handler == SIG_IGN) - h = "SIG_IGN"; + fputs("SIG_IGN", fp); else - h = hand; - - asprintf(&tmp, "{ %s %s ss_t }", h, + fprintf(fp, "%p", sa.sa_handler); + fprintf(fp, " %s ss_t }", xlookup_bits(sigaction_flags, sa.sa_flags)); - free(hand); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Kevent: { /* - * XXX XXX: the size of the array is determined by either the - * next syscall argument, or by the syscall returnvalue, + * XXX XXX: The size of the array is determined by either the + * next syscall argument, or by the syscall return value, * depending on which argument number we are. This matches the * kevent syscall, but luckily that's the only syscall that uses * them. */ struct kevent *ke; int numevents = -1; - int bytes = 0; - int i, tmpsize, u, used; - const int per_ke = 100; + size_t bytes; + int i; if (sc->offset == 1) numevents = args[sc->offset+1]; - else if (sc->offset == 3 && retval != -1) - numevents = retval; + else if (sc->offset == 3 && retval[0] != -1) + numevents = retval[0]; - if (numevents >= 0) + if (numevents >= 0) { bytes = sizeof(struct kevent) * numevents; - if ((ke = malloc(bytes)) == NULL) - err(1, "Cannot malloc %d bytes for kevent array", - bytes); + if ((ke = malloc(bytes)) == NULL) + err(1, + "Cannot malloc %zu bytes for kevent array", + bytes); + } else + ke = NULL; if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], ke, bytes) != -1) { - used = 0; - tmpsize = 1 + per_ke * numevents + 2; - if ((tmp = malloc(tmpsize)) == NULL) - err(1, "Cannot alloc %d bytes for kevent " - "output", tmpsize); - - tmp[used++] = '{'; + fputc('{', fp); for (i = 0; i < numevents; i++) { - u = snprintf(tmp + used, per_ke, - "%s%p,%s,%s,%d,%p,%p", - i > 0 ? " " : "", - (void *)ke[i].ident, - xlookup(kevent_filters, ke[i].filter), - xlookup_bits(kevent_flags, ke[i].flags), - ke[i].fflags, - (void *)ke[i].data, - (void *)ke[i].udata); - if (u > 0) - used += u < per_ke ? u : per_ke; + fputc(' ', fp); + print_kevent(fp, &ke[i], sc->offset == 1); } - tmp[used++] = '}'; - tmp[used++] = '\0'; + fputs(" }", fp); } else { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); } free(ke); break; } case Stat: { struct stat st; + if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) != -1) { char mode[12]; + strmode(st.st_mode, mode); - asprintf(&tmp, - "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode, - (intmax_t)st.st_ino, (intmax_t)st.st_size, + fprintf(fp, + "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode, + (uintmax_t)st.st_ino, (intmax_t)st.st_size, (long)st.st_blksize); } else { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); } break; } + case StatFs: { + unsigned int i; + struct statfs buf; + + if (get_struct(pid, (void *)args[sc->offset], &buf, + sizeof(buf)) != -1) { + char fsid[17]; + + bzero(fsid, sizeof(fsid)); + if (buf.f_fsid.val[0] != 0 || buf.f_fsid.val[1] != 0) { + for (i = 0; i < sizeof(buf.f_fsid); i++) + snprintf(&fsid[i*2], + sizeof(fsid) - (i*2), "%02x", + ((u_char *)&buf.f_fsid)[i]); + } + fprintf(fp, + "{ fstypename=%s,mntonname=%s,mntfromname=%s," + "fsid=%s }", buf.f_fstypename, buf.f_mntonname, + buf.f_mntfromname, fsid); + } else + fprintf(fp, "0x%lx", args[sc->offset]); + break; + } + case Rusage: { struct rusage ru; + if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) != -1) { - asprintf(&tmp, - "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }", - (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, - (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, + fprintf(fp, + "{ u=%jd.%06ld,s=%jd.%06ld,in=%ld,out=%ld }", + (intmax_t)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, + (intmax_t)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, ru.ru_inblock, ru.ru_oublock); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Rlimit: { struct rlimit rl; + if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) != -1) { - asprintf(&tmp, "{ cur=%ju,max=%ju }", + fprintf(fp, "{ cur=%ju,max=%ju }", rl.rlim_cur, rl.rlim_max); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case ExitStatus: { - char *signame; int status; - signame = NULL; + if (get_struct(pid, (void *)args[sc->offset], &status, sizeof(status)) != -1) { + fputs("{ ", fp); if (WIFCONTINUED(status)) - tmp = strdup("{ CONTINUED }"); + fputs("CONTINUED", fp); else if (WIFEXITED(status)) - asprintf(&tmp, "{ EXITED,val=%d }", + fprintf(fp, "EXITED,val=%d", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) - asprintf(&tmp, "{ SIGNALED,sig=%s%s }", - signame = strsig2(WTERMSIG(status)), + fprintf(fp, "SIGNALED,sig=%s%s", + strsig2(WTERMSIG(status)), WCOREDUMP(status) ? ",cored" : ""); else - asprintf(&tmp, "{ STOPPED,sig=%s }", - signame = strsig2(WTERMSIG(status))); + fprintf(fp, "STOPPED,sig=%s", + strsig2(WTERMSIG(status))); + fputs(" }", fp); } else - asprintf(&tmp, "0x%lx", args[sc->offset]); - free(signame); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Waitoptions: - tmp = strdup(xlookup_bits(wait_options, args[sc->offset])); + fputs(xlookup_bits(wait_options, args[sc->offset]), fp); break; case Idtype: - tmp = strdup(xlookup(idtype_arg, args[sc->offset])); + fputs(xlookup(idtype_arg, args[sc->offset]), fp); break; case Procctl: - tmp = strdup(xlookup(procctl_arg, args[sc->offset])); + fputs(xlookup(procctl_arg, args[sc->offset]), fp); break; case Umtxop: - tmp = strdup(xlookup(umtx_ops, args[sc->offset])); + fputs(xlookup(umtx_ops, args[sc->offset]), fp); + break; + case Atfd: + if ((int)args[sc->offset] == AT_FDCWD) + fputs("AT_FDCWD", fp); + else + fprintf(fp, "%d", (int)args[sc->offset]); + break; + case Atflags: + fputs(xlookup_bits(at_flags, args[sc->offset]), fp); + break; + case Accessmode: + if (args[sc->offset] == F_OK) + fputs("F_OK", fp); + else + fputs(xlookup_bits(access_modes, args[sc->offset]), fp); + break; + case Sysarch: + fputs(xlookup(sysarch_ops, args[sc->offset]), fp); + break; + case PipeFds: + /* + * The pipe() system call in the kernel returns its + * two file descriptors via return values. However, + * the interface exposed by libc is that pipe() + * accepts a pointer to an array of descriptors. + * Format the output to match the libc API by printing + * the returned file descriptors as a fake argument. + * + * Overwrite the first retval to signal a successful + * return as well. + */ + fprintf(fp, "{ %ld, %ld }", retval[0], retval[1]); + retval[0] = 0; break; default: errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); } + fclose(fp); return (tmp); } /* - * print_syscall * Print (to outfile) the system call and its arguments. Note that * nargs is the number of arguments (not the number of words; this is * potentially confusing, I know). */ - void print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s_args) @@ -1331,7 +1597,8 @@ print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, len = 0; if (trussinfo->flags & FOLLOWFORKS) - len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); + len += fprintf(trussinfo->outfile, "%5d: ", + trussinfo->curthread->proc->pid); if (name != NULL && (strcmp(name, "execve") == 0 || strcmp(name, "exit") == 0)) { @@ -1341,15 +1608,15 @@ print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, if (trussinfo->flags & ABSOLUTETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->start_time, &timediff); - len += fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, timediff.tv_nsec); + len += fprintf(trussinfo->outfile, "%jd.%09ld ", + (intmax_t)timediff.tv_sec, timediff.tv_nsec); } if (trussinfo->flags & RELATIVETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->curthread->before, &timediff); - len += fprintf(trussinfo->outfile, "%ld.%09ld ", - (long)timediff.tv_sec, timediff.tv_nsec); + len += fprintf(trussinfo->outfile, "%jd.%09ld ", + (intmax_t)timediff.tv_sec, timediff.tv_nsec); } len += fprintf(trussinfo->outfile, "%s(", name); @@ -1370,13 +1637,11 @@ print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, void print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, - char **s_args, int errorp, long retval, struct syscall *sc) + char **s_args, int errorp, long *retval, struct syscall *sc) { struct timespec timediff; if (trussinfo->flags & COUNTONLY) { - if (!sc) - return; clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); timespecsubt(&trussinfo->curthread->after, &trussinfo->curthread->before, &timediff); @@ -1390,17 +1655,24 @@ print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, print_syscall(trussinfo, name, nargs, s_args); fflush(trussinfo->outfile); if (errorp) - fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval, - strerror(retval)); - else { - /* - * Because pipe(2) has a special assembly glue to provide the - * libc API, we have to adjust retval. - */ - if (name != NULL && strcmp(name, "pipe") == 0) - retval = 0; - fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval); + fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval[0], + strerror(retval[0])); +#ifndef __LP64__ + else if (sc->ret_type == 2) { + off_t off; + +#if _BYTE_ORDER == _LITTLE_ENDIAN + off = (off_t)retval[1] << 32 | retval[0]; +#else + off = (off_t)retval[0] << 32 | retval[1]; +#endif + fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", (intmax_t)off, + (intmax_t)off); } +#endif + else + fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval[0], + retval[0]); } void @@ -1413,7 +1685,7 @@ print_summary(struct trussinfo *trussinfo) fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n", "syscall", "seconds", "calls", "errors"); ncall = nerror = 0; - for (sc = syscalls; sc->name != NULL; sc++) + STAILQ_FOREACH(sc, &syscalls, entries) if (sc->ncalls) { fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", sc->name, (intmax_t)sc->time.tv_sec, diff --git a/usr.bin/truss/truss.1 b/usr.bin/truss/truss.1 index 6a01451..b5a1cfe 100644 --- a/usr.bin/truss/truss.1 +++ b/usr.bin/truss/truss.1 @@ -1,6 +1,6 @@ .\" $FreeBSD$ .\" -.Dd May 12, 2009 +.Dd October 9, 2015 .Dt TRUSS 1 .Os .Sh NAME @@ -37,7 +37,7 @@ Show the argument strings that are passed in each .Xr execve 2 system call. .It Fl c -Do not display individual system calls. +Do not display individual system calls or signals. Instead, before exiting, print a summary containing for each system call: the total system time used, the number of times the call was invoked, diff --git a/usr.bin/truss/truss.h b/usr.bin/truss/truss.h index dcc86a5..5a57de7 100644 --- a/usr.bin/truss/truss.h +++ b/usr.bin/truss/truss.h @@ -25,6 +25,7 @@ * $FreeBSD$ */ +#include <sys/linker_set.h> #include <sys/queue.h> #define FOLLOWFORKS 0x00000001 @@ -35,23 +36,63 @@ #define EXECVEENVS 0x00000020 #define COUNTONLY 0x00000040 +struct procinfo; +struct trussinfo; + +struct procabi { + const char *type; + const char **syscallnames; + int nsyscalls; + int (*fetch_args)(struct trussinfo *, u_int); + int (*fetch_retval)(struct trussinfo *, long *, int *); +}; + +#define PROCABI(abi) DATA_SET(procabi, abi) + +/* + * This is confusingly named. It holds per-thread state about the + * currently executing system call. syscall.h defines a struct + * syscall that holds metadata used to format system call arguments. + * + * NB: args[] stores the raw argument values (e.g. from registers) + * passed to the system call. s_args[] stores a string representation + * of a system call's arguments. These do not necessarily map one to + * one. A system call description may omit individual arguments + * (padding) or combine adjacent arguments (e.g. when passing an off_t + * argument on a 32-bit system). The nargs member contains the count + * of valid pointers in s_args[], not args[]. + */ +struct current_syscall { + struct syscall *sc; + const char *name; + int number; + unsigned long args[10]; + unsigned int nargs; + char *s_args[10]; /* the printable arguments */ +}; + struct threadinfo { SLIST_ENTRY(threadinfo) entries; + struct procinfo *proc; lwpid_t tid; int in_syscall; - int in_fork; - void *fsc; + struct current_syscall cs; struct timespec before; struct timespec after; }; +struct procinfo { + LIST_ENTRY(procinfo) entries; + pid_t pid; + struct procabi *abi; + + SLIST_HEAD(, threadinfo) threadlist; +}; + struct trussinfo { - pid_t pid; int flags; - int pr_why; - int pr_data; int strsize; FILE *outfile; @@ -59,7 +100,7 @@ struct trussinfo struct threadinfo *curthread; - SLIST_HEAD(, threadinfo) threadlist; + LIST_HEAD(, procinfo) proclist; }; #define timespecsubt(tvp, uvp, vvp) \ @@ -81,11 +122,3 @@ struct trussinfo (vvp)->tv_nsec -= 1000000000; \ } \ } while (0) - -#define S_NONE 0 -#define S_SCE 1 -#define S_SCX 2 -#define S_EXIT 3 -#define S_SIG 4 -#define S_EXEC 5 -#define S_DETACHED 6 |