Index: head/usr.bin/truss/main.c =================================================================== --- head/usr.bin/truss/main.c (revision 286912) +++ head/usr.bin/truss/main.c (revision 286913) @@ -1,385 +1,382 @@ /*- * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * The main module for truss. Surprisingly simple, but, then, the other * files handle the bulk of the work. And, of course, the kernel has to * do a lot of the work :). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "truss.h" #include "extern.h" #include "syscall.h" #define MAXARGS 6 static void usage(void) { fprintf(stderr, "%s\n%s\n", "usage: truss [-cfaedDS] [-o file] [-s strsize] -p pid", " truss [-cfaedDS] [-o file] [-s strsize] command [args]"); 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 __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; fname = NULL; initial_open = 1; /* Initialize the trussinfo struct */ trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo)); if (trussinfo == NULL) errx(1, "calloc() failed"); trussinfo->outfile = stderr; trussinfo->strsize = 32; trussinfo->pr_why = S_NONE; trussinfo->curthread = NULL; SLIST_INIT(&trussinfo->threadlist); while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) { switch (c) { case 'p': /* specified pid */ trussinfo->pid = atoi(optarg); /* make sure i don't trace me */ if (trussinfo->pid == getpid()) { fprintf(stderr, "attempt to grab self.\n"); exit(2); } break; case 'f': /* Follow fork()'s */ trussinfo->flags |= FOLLOWFORKS; break; case 'a': /* Print execve() argument strings. */ trussinfo->flags |= EXECVEARGS; break; case 'c': /* Count number of system calls and time. */ trussinfo->flags |= COUNTONLY; break; case 'e': /* Print execve() environment strings. */ trussinfo->flags |= EXECVEENVS; break; case 'd': /* Absolute timestamps */ trussinfo->flags |= ABSOLUTETIMESTAMPS; break; case 'D': /* Relative timestamps */ trussinfo->flags |= RELATIVETIMESTAMPS; break; case 'o': /* Specified output file */ fname = optarg; break; case 's': /* Specified string size */ trussinfo->strsize = atoi(optarg); break; case 'S': /* Don't trace signals */ trussinfo->flags |= NOSIGS; break; default: usage(); } } ac -= optind; av += optind; if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0)) usage(); if (fname != NULL) { /* Use output file */ /* * Set close-on-exec ('e'), so that the output file is not * shared with the traced process. */ if ((trussinfo->outfile = fopen(fname, "we")) == NULL) err(1, "cannot open %s", fname); } /* * If truss starts the process itself, it will ignore some signals -- * they should be passed off to the process, which may or may not * 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 */ command = av; trussinfo->pid = setup_and_wait(command); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); } else { sa.sa_handler = restore_proc; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); 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(). */ 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); } if (trussinfo->flags & COUNTONLY) print_summary(trussinfo); fflush(trussinfo->outfile); return (0); } Index: head/usr.bin/truss/syscalls.c =================================================================== --- head/usr.bin/truss/syscalls.c (revision 286912) +++ head/usr.bin/truss/syscalls.c (revision 286913) @@ -1,1571 +1,1508 @@ /* * Copyright 1997 Sean Eric Fagan * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Sean Eric Fagan * 4. Neither the name of the author may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /* * This file has routines used to print out system calls and their * arguments. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "truss.h" #include "extern.h" #include "syscall.h" /* 64-bit alignment on 32-bit platforms. */ #ifdef __powerpc__ #define QUAD_ALIGN 1 #else #define QUAD_ALIGN 0 #endif /* Number of slots needed for a 64-bit argument. */ #ifdef __LP64__ #define QUAD_SLOTS 1 #else #define QUAD_SLOTS 2 #endif /* * 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, 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 = "issetugid", .ret_type = 1, .nargs = 0 }, { .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 = "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 = "openat", .ret_type = 1, .nargs = 4, .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 }, { Octal, 3 } } }, { .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 = "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 = "linkat", .ret_type = 0, .nargs = 5, .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 }, { Atflags, 4 } } }, { .name = "unlink", .ret_type = 0, .nargs = 1, .args = { { Name, 0 } } }, { .name = "unlinkat", .ret_type = 0, .nargs = 3, .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } }, { .name = "chdir", .ret_type = 0, .nargs = 1, .args = { { Name, 0 } } }, { .name = "chroot", .ret_type = 0, .nargs = 1, .args = { { Name, 0 } } }, { .name = "mkfifo", .ret_type = 0, .nargs = 2, .args = { { Name, 0 }, { Octal, 1 } } }, { .name = "mkfifoat", .ret_type = 0, .nargs = 3, .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } }, { .name = "mknod", .ret_type = 0, .nargs = 3, .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } }, { .name = "mknodat", .ret_type = 0, .nargs = 4, .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } }, { .name = "chmod", .ret_type = 0, .nargs = 2, .args = { { Name, 0 }, { Octal, 1 } } }, { .name = "fchmod", .ret_type = 0, .nargs = 2, .args = { { Int, 0 }, { Octal, 1 } } }, { .name = "lchmod", .ret_type = 0, .nargs = 2, .args = { { Name, 0 }, { Octal, 1 } } }, { .name = "fchmodat", .ret_type = 0, .nargs = 4, .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } }, { .name = "chown", .ret_type = 0, .nargs = 3, .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, { .name = "fchown", .ret_type = 0, .nargs = 3, .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } }, { .name = "lchown", .ret_type = 0, .nargs = 3, .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, { .name = "fchownat", .ret_type = 0, .nargs = 5, .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 }, { Atflags, 4 } } }, { .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 = "fstatat", .ret_type = 1, .nargs = 4, .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 }, { Atflags, 3 } } }, { .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 }, { Accessmode, 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 }, { Accessmode, 1 } } }, { .name = "eaccess", .ret_type = 1, .nargs = 2, .args = { { Name | IN, 0 }, { Accessmode, 1 } } }, { .name = "faccessat", .ret_type = 1, .nargs = 4, .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 }, { Atflags, 3 } } }, { .name = "sigaction", .ret_type = 1, .nargs = 3, .args = { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } }, { .name = "accept", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, { .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 = "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 = "getpeername", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, { .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, .args = { { Name | IN, 0 } } }, { .name = "kldunload", .ret_type = 0, .nargs = 1, .args = { { Int, 0 } } }, { .name = "kldfind", .ret_type = 0, .nargs = 1, .args = { { Name | IN, 0 } } }, { .name = "kldnext", .ret_type = 0, .nargs = 1, .args = { { Int, 0 } } }, { .name = "kldstat", .ret_type = 0, .nargs = 2, .args = { { Int, 0 }, { Ptr, 1 } } }, { .name = "kldfirstmod", .ret_type = 0, .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 = "sigpending", .ret_type = 0, .nargs = 1, .args = { { Sigset | OUT, 0 } } }, { .name = "sigprocmask", .ret_type = 0, .nargs = 3, .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, { .name = "sigqueue", .ret_type = 0, .nargs = 3, .args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } }, { .name = "sigreturn", .ret_type = 0, .nargs = 1, .args = { { Ptr, 0 } } }, { .name = "sigsuspend", .ret_type = 0, .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 = "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 }, { Timeval2 | IN, 1 } } }, { .name = "futimesat", .ret_type = 1, .nargs = 3, .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } }, { .name = "futimens", .ret_type = 1, .nargs = 2, .args = { { Int, 0 }, { Timespec2 | IN, 1 } } }, { .name = "utimensat", .ret_type = 1, .nargs = 4, .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timespec2 | IN, 2 }, { Atflags, 3 } } }, { .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 = "pathconf", .ret_type = 1, .nargs = 2, .args = { { Name | IN, 0 }, { Pathconf, 1 } } }, { .name = "pipe", .ret_type = 1, .nargs = 1, .args = { { Ptr, 0 } } }, { .name = "pipe2", .ret_type = 1, .nargs = 2, .args = { { Ptr, 0 }, { Open, 1 } } }, { .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 } } }, { .name = "read", .ret_type = 1, .nargs = 3, .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } }, { .name = "rename", .ret_type = 1, .nargs = 2, .args = { { Name, 0 }, { Name, 1 } } }, { .name = "renameat", .ret_type = 1, .nargs = 4, .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } }, { .name = "symlink", .ret_type = 1, .nargs = 2, .args = { { Name, 0 }, { Name, 1 } } }, { .name = "symlinkat", .ret_type = 1, .nargs = 3, .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } }, { .name = "posix_openpt", .ret_type = 1, .nargs = 1, .args = { { Open, 0 } } }, { .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 = "sysarch", .ret_type = 1, .nargs = 2, .args = { { Sysarch, 0 }, { Ptr, 1 } } }, { .name = "_umtx_op", .ret_type = 1, .nargs = 5, .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 }, { Ptr, 4 } } }, { .name = "thr_kill", .ret_type = 0, .nargs = 2, .args = { { Long, 0 }, { Signal, 1 } } }, { .name = "thr_self", .ret_type = 0, .nargs = 1, .args = { { Ptr, 0 } } }, { .name = 0 }, }; /* Xlat idea taken from strace */ struct xlat { int val; const char *str; }; #define X(a) { a, #a }, #define XEND { 0, NULL } 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_PROCDESC) X(EVFILT_FS) X(EVFILT_LIO) X(EVFILT_USER) X(EVFILT_SENDFILE) 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_RECEIPT) X(EV_DISPATCH) X(EV_FORCEONESHOT) X(EV_DROP) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND }; static struct xlat poll_flags[] = { X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR) X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND) X(POLLWRBAND) X(POLLINIGNEOF) XEND }; static struct xlat mmap_flags[] = { X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RESERVED0020) X(MAP_RESERVED0040) X(MAP_RESERVED0080) X(MAP_RESERVED0100) X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON) X(MAP_EXCL) X(MAP_NOCORE) X(MAP_PREFAULT_READ) #ifdef MAP_32BIT X(MAP_32BIT) #endif XEND }; static struct xlat mprot_flags[] = { X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND }; static struct xlat whence_arg[] = { X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) X(SEEK_DATA) X(SEEK_HOLE) XEND }; static struct xlat sigaction_flags[] = { X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP) X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND }; 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_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[] = { X(FD_CLOEXEC) XEND }; static struct xlat fcntlfl_arg[] = { X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW) X(FRDAHEAD) X(O_DIRECT) XEND }; static struct xlat sockdomain_arg[] = { X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK) X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI) X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet) X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE) 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) X(PF_IEEE80211) X(PF_INET_SDP) X(PF_INET6_SDP) XEND }; static struct xlat socktype_arg[] = { X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM) X(SOCK_SEQPACKET) XEND }; 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) X(O_VERIFY) XEND }; static struct xlat shutdown_arg[] = { X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND }; 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) X(RLIMIT_NPTS) X(RLIMIT_SWAP) X(RLIMIT_KQUEUES) XEND }; static struct xlat pathconf_arg[] = { X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT) X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF) X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE) X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO) X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS) X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE) 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) X(_PC_ACL_NFS4) X(_PC_MIN_HOLE_SIZE) XEND }; static struct xlat rfork_flags[] = { 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[] = { X(WNOHANG) X(WUNTRACED) X(WCONTINUED) X(WNOWAIT) X(WEXITED) X(WTRAPPED) XEND }; static struct xlat idtype_arg[] = { X(P_PID) X(P_PPID) X(P_PGID) X(P_SID) X(P_CID) X(P_UID) X(P_GID) X(P_ALL) X(P_LWPID) X(P_TASKID) X(P_PROJID) X(P_POOLID) X(P_JAILID) X(P_CTID) X(P_CPUID) X(P_PSETID) XEND }; static struct xlat procctl_arg[] = { X(PROC_SPROTECT) XEND }; static struct xlat umtx_ops[] = { X(UMTX_OP_RESERVED0) X(UMTX_OP_RESERVED1) X(UMTX_OP_WAIT) X(UMTX_OP_WAKE) X(UMTX_OP_MUTEX_TRYLOCK) X(UMTX_OP_MUTEX_LOCK) X(UMTX_OP_MUTEX_UNLOCK) X(UMTX_OP_SET_CEILING) X(UMTX_OP_CV_WAIT) X(UMTX_OP_CV_SIGNAL) X(UMTX_OP_CV_BROADCAST) X(UMTX_OP_WAIT_UINT) X(UMTX_OP_RW_RDLOCK) X(UMTX_OP_RW_WRLOCK) X(UMTX_OP_RW_UNLOCK) X(UMTX_OP_WAIT_UINT_PRIVATE) X(UMTX_OP_WAKE_PRIVATE) X(UMTX_OP_MUTEX_WAIT) X(UMTX_OP_MUTEX_WAKE) X(UMTX_OP_SEM_WAIT) X(UMTX_OP_SEM_WAKE) X(UMTX_OP_NWAKE_PRIVATE) X(UMTX_OP_MUTEX_WAKE2) X(UMTX_OP_SEM2_WAIT) X(UMTX_OP_SEM2_WAKE) 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 /* * Searches an xlat array for a value, and returns it if found. Otherwise * return a string representation. */ static const char * lookup(struct xlat *xlat, int val, int base) { static char tmp[16]; for (; xlat->str != NULL; xlat++) if (xlat->val == val) return (xlat->str); switch (base) { case 8: sprintf(tmp, "0%o", val); break; case 16: sprintf(tmp, "0x%x", val); break; case 10: sprintf(tmp, "%u", val); break; default: errx(1,"Unknown lookup base"); break; } return (tmp); } static const char * 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 */ static char * xlookup_bits(struct xlat *xlat, int val) { int len, rem; static char str[512]; len = 0; 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 */ 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 (rem || len == 0) len += sprintf(str + len, "0x%x", rem); if (len && str[len - 1] == '|') len--; str[len] = 0; return (str); } /* * 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) { struct syscall *sc; sc = syscalls; if (name == NULL) return (NULL); while (sc->name) { if (strcmp(name, sc->name) == 0) return (sc); sc++; } return (NULL); } /* * get_struct * * Copy a fixed amount of bytes from the process. */ static int get_struct(pid_t pid, void *offset, void *buf, int len) { struct ptrace_io_desc iorequest; iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = offset; iorequest.piod_addr = buf; iorequest.piod_len = len; if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) return (-1); return (0); } #define MAXSIZE 4096 /* * 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 *addr, int max) { struct ptrace_io_desc iorequest; char *buf, *nbuf; size_t offset, size, totalsize; 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 (;;) { iorequest.piod_op = PIOD_READ_D; 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); } 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); } buf = nbuf; totalsize += size; } else { buf[totalsize - 1] = '\0'; return (buf); } } } 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); } /* * 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 * 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, struct trussinfo *trussinfo) { + FILE *fp; char *tmp; + size_t tmplen; pid_t pid; - tmp = NULL; + fp = open_memstream(&tmp, &tmplen); pid = trussinfo->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]); + fprintf(fp, "0x%lx", args[sc->offset]); break; case Long: - asprintf(&tmp, "%ld", args[sc->offset]); + 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. */ int max_string = trussinfo->strsize; char tmp2[max_string+1], *tmp3; int len; int truncated = 0; if (sc->type & OUT) len = retval; 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. */ if (len > max_string) { len = max_string; truncated = 1; } if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) != -1) { tmp3 = malloc(len * 4 + 1); while (len) { if (strvisx(tmp3, tmp2, len, VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string) break; 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 StringArray: { int num, size, i; char *tmp2; 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 += 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) ? ' ' : ','); free(string); } tmp2 += sprintf(tmp2, "]"); break; } #ifdef __LP64__ case Quad: - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; #else case Quad: { unsigned long long ll; ll = *(unsigned long long *)(args + sc->offset); - asprintf(&tmp, "0x%llx", ll); + 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 == -1) break; - } tmp2 = get_string(pid, (void*)args[sc->offset], retval); - asprintf(&tmp, "\"%s\"", tmp2); + fprintf(fp, "\"%s\"", tmp2); free(tmp2); break; } case Ioctl: { const char *temp = ioctlname(args[sc->offset]); 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 }", + fprintf(fp, "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)); } 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, "{ %ld.%09ld }", (long)ts.tv_sec, ts.tv_nsec); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Timespec2: { struct timespec ts[2]; - FILE *fp; - size_t len; const char *sep; unsigned int i; if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts)) != -1) { - fp = open_memstream(&tmp, &len); fputs("{ ", fp); sep = ""; for (i = 0; i < nitems(ts); i++) { fputs(sep, fp); sep = ", "; switch (ts[i].tv_nsec) { case UTIME_NOW: fprintf(fp, "UTIME_NOW"); break; case UTIME_OMIT: fprintf(fp, "UTIME_OMIT"); break; default: fprintf(fp, "%ld.%09ld", (long)ts[i].tv_sec, ts[i].tv_nsec); break; } } fputs(" }", fp); - fclose(fp); } 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, "{ %ld.%06ld }", (long)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 }", + fprintf(fp, "{ %ld.%06ld, %ld.%06ld }", (long)tv[0].tv_sec, tv[0].tv_usec, (long)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 }", + fprintf(fp, "{ %ld.%06ld, %ld.%06ld }", (long)itv.it_interval.tv_sec, itv.it_interval.tv_usec, (long)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) - asprintf(&tmp, "{ %s, 0x%lx }", + fprintf(fp, "{ %s, 0x%lx }", lookup(linux_socketcall_ops, largs.what, 10), (long unsigned int)largs.args); else - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); break; } case Pollfd: { /* * XXX: A Pollfd argument expects the /next/ syscall argument * to be the number of fds in the array. This matches the poll * 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++] = '{'; - 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++] = '}'; - tmp[used++] = '\0'; + fputs(" }", fp); } else { - asprintf(&tmp, "0x%lx", args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); } free(pfd); break; } case Fd_set: { /* * XXX: A Fd_set argument expects the /first/ syscall argument * to be the number of fds in the array. This matches the * select syscall. */ 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++] = '{'; - 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); } - 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 + 2); /* 7 bytes avg per signal name */ - used = 0; - tmp[used++] = '{'; - tmp[used++] = ' '; + 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 (tmp[used - 1] == '|') - used--; - tmp[used++] = ' '; - tmp[used++] = '}'; - tmp[used++] = '\0'; + if (!first) + fputc(' ', fp); + fputc('}', fp); break; } case Sigprocmask: { - tmp = strdup(xlookup(sigprocmask_ops, 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]) { 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; /* * MAP_ALIGNED can't be handled by xlookup_bits(), so * generate that string manually and prepend it to the * string from xlookup_bits(). Have to be careful to * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is * the only flag. */ flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK; 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: { - FILE *fp; - size_t len; int type, flags; flags = args[sc->offset] & (SOCK_CLOEXEC | SOCK_NONBLOCK); type = args[sc->offset] & ~flags; - fp = open_memstream(&tmp, &len); fputs(xlookup(socktype_arg, type), fp); if (flags & SOCK_CLOEXEC) fprintf(fp, "|SOCK_CLOEXEC"); if (flags & SOCK_NONBLOCK) fprintf(fp, "|SOCK_NONBLOCK"); - fclose(fp); 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; 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]); + sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) { + fprintf(fp, "0x%lx", args[sc->offset]); + break; + } + /* * 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. */ 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_INET6: + ss.ss_len = sizeof(*lsin6); + break; case AF_UNIX: ss.ss_len = sizeof(*sun); break; default: - /* hurrrr */ break; } } - if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, + if (ss.ss_len != 0 && + get_struct(pid, (void *)args[sc->offset], (void *)&ss, ss.ss_len) == -1) { - err(2, "get_struct %p", (void *)args[sc->offset]); + fprintf(fp, "0x%lx", args[sc->offset]); + break; } switch (ss.ss_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, + fprintf(fp, "{ AF_INET %s:%d }", addr, htons(lsin->sin_port)); break; case AF_INET6: lsin6 = (struct sockaddr_in6 *)&ss; inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, sizeof addr); - asprintf(&tmp, "{ AF_INET6 [%s]:%d }", 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); + fprintf(fp, "{ AF_UNIX \"%s\" }", 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); - } + 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 + sa->sa_len; q++) + fprintf(fp, "%s 0x%02x", + q == (u_char *)sa->sa_data ? "" : ",", + *q); + fputs(" } }", fp); } 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, * 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; - 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++] = '{'; - tmp[used++] = ' '; - for (i = 0; i < numevents; i++) { - u = snprintf(tmp + used, per_ke, - "%s%p,%s,%s,%d,%p,%p", - i > 0 ? " " : "", + fputc('{', fp); + for (i = 0; i < numevents; i++) + fprintf(fp, " %p,%s,%s,%d,%p,%p", (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; - } - tmp[used++] = ' '; - 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, + fprintf(fp, "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode, (intmax_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 Rusage: { struct rusage ru; if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) != -1) { - asprintf(&tmp, + fprintf(fp, "{ 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, 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) - tmp = strdup("AT_FDCWD"); + fputs("AT_FDCWD", fp); else - asprintf(&tmp, "%d", (int)args[sc->offset]); + fprintf(fp, "%d", (int)args[sc->offset]); break; case Atflags: - tmp = strdup(xlookup_bits(at_flags, args[sc->offset])); + fputs(xlookup_bits(at_flags, args[sc->offset]), fp); break; case Accessmode: if (args[sc->offset] == F_OK) - tmp = strdup("F_OK"); + fputs("F_OK", fp); else - tmp = strdup(xlookup_bits(access_modes, - args[sc->offset])); + fputs(xlookup_bits(access_modes, args[sc->offset]), fp); break; case Sysarch: - tmp = strdup(xlookup(sysarch_ops, args[sc->offset])); + fputs(xlookup(sysarch_ops, args[sc->offset]), fp); 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) { struct timespec timediff; int i, len; len = 0; if (trussinfo->flags & FOLLOWFORKS) len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); if (name != NULL && (strcmp(name, "execve") == 0 || strcmp(name, "exit") == 0)) { clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); } 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); } 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, "%s(", name); for (i = 0; i < nargs; i++) { if (s_args[i]) len += fprintf(trussinfo->outfile, "%s", s_args[i]); else len += fprintf(trussinfo->outfile, ""); len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : ""); } len += fprintf(trussinfo->outfile, ")"); for (i = 0; i < 6 - (len / 8); i++) fprintf(trussinfo->outfile, "\t"); } void print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, 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); timespecadd(&sc->time, &timediff, &sc->time); sc->ncalls++; if (errorp) sc->nerror++; return; } 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); } } void print_summary(struct trussinfo *trussinfo) { struct timespec total = {0, 0}; struct syscall *sc; int ncall, nerror; fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n", "syscall", "seconds", "calls", "errors"); ncall = nerror = 0; for (sc = syscalls; sc->name != NULL; sc++) if (sc->ncalls) { fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", sc->name, (intmax_t)sc->time.tv_sec, sc->time.tv_nsec, sc->ncalls, sc->nerror); timespecadd(&total, &sc->time, &total); ncall += sc->ncalls; nerror += sc->nerror; } fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n", "", "-------------", "-------", "-------"); fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); }