Index: usr.bin/truss/main.c =================================================================== --- usr.bin/truss/main.c +++ usr.bin/truss/main.c @@ -59,8 +59,8 @@ usage(void) { fprintf(stderr, "%s\n%s\n", - "usage: truss [-cfaedDHS] [-o file] [-s strsize] -p pid", - " truss [-cfaedDHS] [-o file] [-s strsize] command [args]"); + "usage: truss [-cfaedDHS] [-o file] [-s strsize] [-t timeout] -p pid", + " truss [-cfaedDHS] [-o file] [-s strsize] [-t timeout] command [args]"); exit(1); } @@ -84,10 +84,11 @@ pid = 0; trussinfo->outfile = stderr; trussinfo->strsize = 32; + trussinfo->timeout = 0; trussinfo->curthread = NULL; LIST_INIT(&trussinfo->proclist); init_syscalls(); - while ((c = getopt(ac, av, "p:o:facedDs:SH")) != -1) { + while ((c = getopt(ac, av, "p:o:facedDs:t:SH")) != -1) { switch (c) { case 'p': /* specified pid */ pid = atoi(optarg); @@ -120,6 +121,9 @@ case 's': /* Specified string size */ trussinfo->strsize = atoi(optarg); break; + case 't': /* Pending syscall timeout */ + trussinfo->timeout = atoi(optarg); + break; case 'S': /* Don't trace signals */ trussinfo->flags |= NOSIGS; break; Index: usr.bin/truss/setup.c =================================================================== --- usr.bin/truss/setup.c +++ usr.bin/truss/setup.c @@ -62,7 +62,7 @@ SET_DECLARE(procabi, struct procabi); -static sig_atomic_t detaching; +static sig_atomic_t detaching, alarm_tripped; static void enter_syscall(struct trussinfo *, struct threadinfo *, struct ptrace_lwpinfo *); @@ -146,6 +146,59 @@ } /* + * A signal handler so we can break out of wait after a timeout. + */ +static void +trap_alarm(int signo __unused) +{ + alarm_tripped = 1; +} + +static bool +set_alarm(struct trussinfo *info) +{ + struct procinfo *p; + struct threadinfo *t; + + LIST_FOREACH(p, &info->proclist, entries) { + LIST_FOREACH(t, &p->threadlist, entries) { + if (t->in_syscall) { + alarm(info->timeout); + alarm_tripped = 0; + return (true); + } + } + } + return (false); +} + + +/* + * Prints names and arguments of any threads currently in a syscall. + */ +static void +print_sleeping_threads(struct trussinfo *info) +{ + struct procinfo *p; + struct threadinfo *t; + + LIST_FOREACH(p, &info->proclist, entries) { + LIST_FOREACH(t, &p->threadlist, entries) { + if (t->in_syscall) { + info->curthread = t; + if (info->flags & (RELATIVETIMESTAMPS | + ABSOLUTETIMESTAMPS)) + clock_gettime(CLOCK_REALTIME, + &t->after); + print_syscall(info); + fprintf(info->outfile, " \n"); + } + } + } +} + + +/* * Determine the ABI. This is called after every exec, and when * a process is first monitored. */ @@ -484,6 +537,7 @@ } } + t->in_syscall = 0; print_syscall_ret(info, errorp, retval); free_syscall(t); @@ -669,6 +723,10 @@ struct ptrace_lwpinfo pl; siginfo_t si; int pending_signal; + bool alarm_set = false; + + signal(SIGALRM, trap_alarm); + siginterrupt(SIGALRM, 1); while (!LIST_EMPTY(&info->proclist)) { if (detaching) { @@ -676,11 +734,34 @@ return; } - if (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) { - if (errno == EINTR) + if (info->timeout) + alarm_set = set_alarm(info); + + while (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) { + if (errno == EINTR) { + if (detaching) + break; + if (info->timeout && alarm_tripped) { + /* + * The timer interrupted us, so we + * must be waiting on slow syscalls. + * Print their names and continue + * waiting, this time forever. + */ + print_sleeping_threads(info); + fflush(info->outfile); + alarm(0); + alarm_set = false; + } continue; + } err(1, "Unexpected error from waitid"); } + if (detaching) + continue; + + if (alarm_set) + alarm(0); assert(si.si_signo == SIGCHLD); Index: usr.bin/truss/syscalls.c =================================================================== --- usr.bin/truss/syscalls.c +++ usr.bin/truss/syscalls.c @@ -2618,8 +2618,8 @@ if (s_args[i] != NULL) len += fprintf(trussinfo->outfile, "%s", s_args[i]); else - len += fprintf(trussinfo->outfile, - ""); + len += fprintf(trussinfo->outfile, t->in_syscall ? + "" : ""); len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? "," : ""); } Index: usr.bin/truss/truss.h =================================================================== --- usr.bin/truss/truss.h +++ usr.bin/truss/truss.h @@ -111,6 +111,7 @@ { int flags; int strsize; + int timeout; FILE *outfile; struct timespec start_time; Index: usr.bin/truss/truss.1 =================================================================== --- usr.bin/truss/truss.1 +++ usr.bin/truss/truss.1 @@ -11,11 +11,13 @@ .Op Fl facedDHS .Op Fl o Ar file .Op Fl s Ar strsize +.Op Fl t Ar timeout .Fl p Ar pid .Nm .Op Fl facedDHS .Op Fl o Ar file .Op Fl s Ar strsize +.Op Fl t Ar timeout .Ar command Op Ar args .Sh DESCRIPTION The @@ -77,6 +79,12 @@ The default .Ar strsize is 32. +.It Fl t Ar timeout +After +.Ar timeout +seconds with no syscall activity, print the syscall names and arguments +for any threads currently waiting in the kernel. +A value of 0 (the default) will disable the timeout. .It Fl p Ar pid Follow the process specified by .Ar pid