From ee3b0f6e2e053c63a22950421e05ff93ebc3cd73 Mon Sep 17 00:00:00 2001 From: Diomidis Spinellis Date: Tue, 12 May 2009 20:42:12 +0000 Subject: [PATCH] Add -c option to summarize number of calls, errors, and system time. Reviewed by: alfred --- usr.bin/truss/amd64-fbsd.c | 3 +- usr.bin/truss/amd64-fbsd32.c | 3 +- usr.bin/truss/amd64-linux32.c | 2 +- usr.bin/truss/i386-fbsd.c | 3 +- usr.bin/truss/i386-linux.c | 2 +- usr.bin/truss/ia64-fbsd.c | 3 +- usr.bin/truss/main.c | 18 +- usr.bin/truss/powerpc-fbsd.c | 3 +- usr.bin/truss/sparc64-fbsd.c | 3 +- usr.bin/truss/syscall.h | 6 +- usr.bin/truss/syscalls.c | 323 ++++++++++++++++++++-------------- usr.bin/truss/truss.1 | 12 +- usr.bin/truss/truss.h | 11 ++ 13 files changed, 247 insertions(+), 145 deletions(-) diff --git a/usr.bin/truss/amd64-fbsd.c b/usr.bin/truss/amd64-fbsd.c index 3a632d637c5e..d43f92912587 100644 --- a/usr.bin/truss/amd64-fbsd.c +++ b/usr.bin/truss/amd64-fbsd.c @@ -323,7 +323,8 @@ amd64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) * but that complicates things considerably. */ - print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, retval); + print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, + retval, fsc.sc); clear_fsc(); return (retval); diff --git a/usr.bin/truss/amd64-fbsd32.c b/usr.bin/truss/amd64-fbsd32.c index 64157e30c7b6..4e6458a08920 100644 --- a/usr.bin/truss/amd64-fbsd32.c +++ b/usr.bin/truss/amd64-fbsd32.c @@ -339,7 +339,8 @@ amd64_fbsd32_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) * but that complicates things considerably. */ - print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, retval); + print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, + retval, fsc.sc); clear_fsc(); return (retval); diff --git a/usr.bin/truss/amd64-linux32.c b/usr.bin/truss/amd64-linux32.c index 163b1417bd64..7f7390aa7688 100644 --- a/usr.bin/truss/amd64-linux32.c +++ b/usr.bin/truss/amd64-linux32.c @@ -309,7 +309,7 @@ amd64_linux32_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused } print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, - errorp ? i : retval); + errorp ? i : retval, fsc.sc); clear_fsc(); return (retval); diff --git a/usr.bin/truss/i386-fbsd.c b/usr.bin/truss/i386-fbsd.c index 4f0eabb20622..a65c7dbaa6ec 100644 --- a/usr.bin/truss/i386-fbsd.c +++ b/usr.bin/truss/i386-fbsd.c @@ -329,7 +329,8 @@ i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) * but that complicates things considerably. */ - print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, retval); + print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, + retval, fsc.sc); clear_fsc(); return (retval); diff --git a/usr.bin/truss/i386-linux.c b/usr.bin/truss/i386-linux.c index 1264439fcef5..de17628c0797 100644 --- a/usr.bin/truss/i386-linux.c +++ b/usr.bin/truss/i386-linux.c @@ -309,7 +309,7 @@ i386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) } print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, - errorp ? i : retval); + errorp ? i : retval, fsc.sc); clear_fsc(); return (retval); diff --git a/usr.bin/truss/ia64-fbsd.c b/usr.bin/truss/ia64-fbsd.c index 70140b6b84de..cd8c69eb234a 100644 --- a/usr.bin/truss/ia64-fbsd.c +++ b/usr.bin/truss/ia64-fbsd.c @@ -294,7 +294,8 @@ ia64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) * but that complicates things considerably. */ - print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, retval); + print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, + fsc.sc, retval); clear_fsc(); return (retval); diff --git a/usr.bin/truss/main.c b/usr.bin/truss/main.c index 9c1589b2e7e9..4333f2df3076 100644 --- a/usr.bin/truss/main.c +++ b/usr.bin/truss/main.c @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include "truss.h" #include "extern.h" +#include "syscall.h" #define MAXARGS 6 @@ -65,8 +66,8 @@ static void usage(void) { fprintf(stderr, "%s\n%s\n", - "usage: truss [-faedDS] [-o file] [-s strsize] -p pid", - " truss [-faedDS] [-o file] [-s strsize] command [args]"); + "usage: truss [-cfaedDS] [-o file] [-s strsize] -p pid", + " truss [-cfaedDS] [-o file] [-s strsize] command [args]"); exit(1); } @@ -188,7 +189,7 @@ main(int ac, char **av) trussinfo->pr_why = S_NONE; trussinfo->curthread = NULL; SLIST_INIT(&trussinfo->threadlist); - while ((c = getopt(ac, av, "p:o:faedDs:S")) != -1) { + while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) { switch (c) { case 'p': /* specified pid */ trussinfo->pid = atoi(optarg); @@ -204,6 +205,9 @@ main(int ac, char **av) 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; @@ -337,6 +341,8 @@ main(int ac, char **av) free(signame); break; case S_EXIT: + if (trussinfo->flags & COUNTONLY) + break; if (trussinfo->flags & FOLLOWFORKS) fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); @@ -360,12 +366,16 @@ main(int ac, char **av) break; } } while (trussinfo->pr_why != S_EXIT); - fflush(trussinfo->outfile); if (trussinfo->flags & FOLLOWFORKS) do { childpid = wait(&status); } while (childpid != -1); + if (trussinfo->flags & COUNTONLY) + print_summary(trussinfo); + + fflush(trussinfo->outfile); + return (0); } diff --git a/usr.bin/truss/powerpc-fbsd.c b/usr.bin/truss/powerpc-fbsd.c index 7b4cfb5ba63f..2316740b32f1 100644 --- a/usr.bin/truss/powerpc-fbsd.c +++ b/usr.bin/truss/powerpc-fbsd.c @@ -331,7 +331,8 @@ powerpc_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) * but that complicates things considerably. */ - print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, retval); + print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, + retval, fsc.sc); clear_fsc(); return (retval); diff --git a/usr.bin/truss/sparc64-fbsd.c b/usr.bin/truss/sparc64-fbsd.c index 896bac749395..31e304af977d 100644 --- a/usr.bin/truss/sparc64-fbsd.c +++ b/usr.bin/truss/sparc64-fbsd.c @@ -336,7 +336,8 @@ sparc64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) { * but that complicates things considerably. */ - print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, retval); + print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp, + retval, fsc.sc); clear_fsc(); return (retval); diff --git a/usr.bin/truss/syscall.h b/usr.bin/truss/syscall.h index 39a796a71370..484bb5d42197 100644 --- a/usr.bin/truss/syscall.h +++ b/usr.bin/truss/syscall.h @@ -57,10 +57,14 @@ struct syscall { 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 */ + int ncalls; /* Number of calls */ + 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 *); void print_syscall(struct trussinfo *, const char *, int, char **); void print_syscall_ret(struct trussinfo *, const char *, int, char **, int, - long); + long, struct syscall *); +void print_summary(struct trussinfo *trussinfo); diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c index d1b390b09d59..d9278156649b 100644 --- a/usr.bin/truss/syscalls.c +++ b/usr.bin/truss/syscalls.c @@ -89,135 +89,162 @@ static const char rcsid[] = /* * This should probably be in its own file, sorted alphabetically. */ - struct syscall syscalls[] = { - { "fcntl", 1, 3, - { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 } } }, - { "readlink", 1, 3, - { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 } } }, - { "lseek", 2, 3, - { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } }, - { "linux_lseek", 2, 3, - { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, - { "mmap", 2, 6, - { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } }, - { "mprotect", 1, 3, - { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, - { "open", 1, 3, - { { Name | IN, 0 } , { Open, 1 }, { Octal, 2 } } }, - { "mkdir", 1, 2, - { { Name, 0 } , { Octal, 1 } } }, - { "linux_open", 1, 3, - { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, - { "close", 1, 1, - { { Int, 0 } } }, - { "link", 0, 2, - { { Name, 0 }, { Name, 1 } } }, - { "unlink", 0, 1, - { { Name, 0 } } }, - { "chdir", 0, 1, - { { Name, 0 } } }, - { "chroot", 0, 1, - { { Name, 0 } } }, - { "mknod", 0, 3, - { { Name, 0 }, { Octal, 1 }, { Int, 3 } } }, - { "chmod", 0, 2, - { { Name, 0 }, { Octal, 1 } } }, - { "chown", 0, 3, - { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, - { "mount", 0, 4, - { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, - { "umount", 0, 2, - { { Name, 0 }, { Int, 2 } } }, - { "fstat", 1, 2, - { { Int, 0 }, { Stat | OUT , 1 } } }, - { "stat", 1, 2, - { { Name | IN, 0 }, { Stat | OUT, 1 } } }, - { "lstat", 1, 2, - { { Name | IN, 0 }, { Stat | OUT, 1 } } }, - { "linux_newstat", 1, 2, - { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, - { "linux_newfstat", 1, 2, - { { Int, 0 }, { Ptr | OUT, 1 } } }, - { "write", 1, 3, - { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, - { "ioctl", 1, 3, - { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, - { "break", 1, 1, { { Ptr, 0 } } }, - { "exit", 0, 1, { { Hex, 0 } } }, - { "access", 1, 2, { { Name | IN, 0 }, { Int, 1 } } }, - { "sigaction", 1, 3, - { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } }, - { "accept", 1, 3, - { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, - { "bind", 1, 3, - { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, - { "connect", 1, 3, - { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, - { "getpeername", 1, 3, - { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, - { "getsockname", 1, 3, - { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, - { "recvfrom", 1, 6, - { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, - { "sendto", 1, 6, - { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, - { "execve", 1, 3, - { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, - { "linux_execve", 1, 3, - { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, - { "kldload", 0, 1, { { Name | IN, 0 } } }, - { "kldunload", 0, 1, { { Int, 0 } } }, - { "kldfind", 0, 1, { { Name | IN, 0 } } }, - { "kldnext", 0, 1, { { Int, 0 } } }, - { "kldstat", 0, 2, { { Int, 0 }, { Ptr, 1 } } }, - { "kldfirstmod", 0, 1, { { Int, 0 } } }, - { "nanosleep", 0, 1, { { Timespec, 0 } } }, - { "select", 1, 5, { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 } } }, - { "poll", 1, 3, { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, - { "gettimeofday", 1, 2, { { Timeval | OUT, 0 }, { Ptr, 1 } } }, - { "clock_gettime", 1, 2, { { Int, 0 }, { Timespec | OUT, 1 } } }, - { "getitimer", 1, 2, { { Int, 0 }, { Itimerval | OUT, 2 } } }, - { "setitimer", 1, 3, { { Int, 0 }, { Itimerval, 1 } , { Itimerval | OUT, 2 } } }, - { "kse_release", 0, 1, { { Timespec, 0 } } }, - { "kevent", 0, 6, { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 } } }, - { "_umtx_lock", 0, 1, { { Umtx, 0 } } }, - { "_umtx_unlock", 0, 1, { { Umtx, 0 } } }, - { "sigprocmask", 0, 3, { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, - { "unmount", 1, 2, { { Name, 0 }, { Int, 1 } } }, - { "socket", 1, 3, { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, - { "getrusage", 1, 2, { { Int, 0 }, { Rusage | OUT, 1 } } }, - { "__getcwd", 1, 2, { { Name | OUT, 0 }, { Int, 1 } } }, - { "shutdown", 1, 2, { { Int, 0 }, { Shutdown, 1 } } }, - { "getrlimit", 1, 2, { { Resource, 0 }, { Rlimit | OUT, 1 } } }, - { "setrlimit", 1, 2, { { Resource, 0 }, { Rlimit | IN, 1 } } }, - { "utimes", 1, 2, - { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, - { "lutimes", 1, 2, - { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, - { "futimes", 1, 2, - { { Int, 0 }, { Timeval | IN, 1 } } }, - { "chflags", 1, 2, - { { Name | IN, 0 }, { Hex, 1 } } }, - { "lchflags", 1, 2, - { { Name | IN, 0 }, { Hex, 1 } } }, - { "pathconf", 1, 2, - { { Name | IN, 0 }, { Pathconf, 1 } } }, - { "truncate", 1, 3, - { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, - { "ftruncate", 1, 3, - { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, - { "kill", 1, 2, - { { Int | IN, 0 }, { Signal | IN, 1 } } }, - { "munmap", 1, 2, - { { Ptr, 0 }, { Int, 1 } } }, - { "read", 1, 3, - { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } }, - { "rename", 1, 2, - { { Name , 0 } , { Name, 1 } } }, - { "symlink", 1, 2, - { { Name , 0 } , { Name, 1 } } }, - { 0, 0, 0, { { 0, 0 } } }, + { .name = "fcntl", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 } } }, + { .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 = "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 = "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_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 } } }, + { .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 = "connect", .ret_type = 1, .nargs = 3, + .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, + { .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 = "_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 = "pathconf", .ret_type = 1, .nargs = 2, + .args = { { Name | IN, 0 }, { Pathconf, 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 = "symlink", .ret_type = 1, .nargs = 2, + .args = { { Name , 0 } , { Name, 1 } } }, + { .name = 0 }, }; /* Xlat idea taken from strace */ @@ -1077,8 +1104,21 @@ print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, char **s void print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, - char **s_args, int errorp, long retval) + 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->after); + timespecsubt(&trussinfo->after, &trussinfo->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); @@ -1088,3 +1128,28 @@ print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval); } } + +void +print_summary(struct trussinfo *trussinfo) +{ + struct syscall *sc; + struct timespec total = {0, 0}; + 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%5d.%09ld%8d%8d\n", + sc->name, 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%5d.%09ld%8d%8d\n", + "", total.tv_sec, total.tv_nsec, ncall, nerror); +} diff --git a/usr.bin/truss/truss.1 b/usr.bin/truss/truss.1 index 28e474e042ea..6a01451a4983 100644 --- a/usr.bin/truss/truss.1 +++ b/usr.bin/truss/truss.1 @@ -1,6 +1,6 @@ .\" $FreeBSD$ .\" -.Dd January 22, 2009 +.Dd May 12, 2009 .Dt TRUSS 1 .Os .Sh NAME @@ -8,12 +8,12 @@ .Nd trace system calls .Sh SYNOPSIS .Nm -.Op Fl faedDS +.Op Fl facedDS .Op Fl o Ar file .Op Fl s Ar strsize .Fl p Ar pid .Nm -.Op Fl faedDS +.Op Fl facedDS .Op Fl o Ar file .Op Fl s Ar strsize .Ar command Op Ar args @@ -36,6 +36,12 @@ etc. Show the argument strings that are passed in each .Xr execve 2 system call. +.It Fl c +Do not display individual system calls. +Instead, before exiting, print a summary containing for each system call: +the total system time used, +the number of times the call was invoked, +and the number of times the call returned with an error. .It Fl e Show the environment strings that are passed in each .Xr execve 2 diff --git a/usr.bin/truss/truss.h b/usr.bin/truss/truss.h index 8533fae36ea9..89198c950ce0 100644 --- a/usr.bin/truss/truss.h +++ b/usr.bin/truss/truss.h @@ -33,6 +33,7 @@ #define NOSIGS 0x00000008 #define EXECVEARGS 0x00000010 #define EXECVEENVS 0x00000020 +#define COUNTONLY 0x00000040 struct threadinfo { @@ -70,6 +71,16 @@ struct trussinfo } \ } while (0) +#define timespecadd(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_nsec; \ + if ((vvp)->tv_nsec > 1000000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_nsec -= 1000000000; \ + } \ + } while (0) + #define S_NONE 0 #define S_SCE 1 #define S_SCX 2