int main(int ac, char **av) { int c, i, mntsize; char **command; struct procfs_status pfs; struct ex_types *funcs; struct statfs *mntbuf; int in_exec = 0; char *fname = NULL; int sigexit = 0; struct trussinfo *trussinfo; /* Initialize the trussinfo struct */ trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); if (trussinfo == NULL) errx(1, "malloc() failed"); bzero(trussinfo, sizeof(struct trussinfo)); trussinfo->outfile = stderr; /* Check where procfs is mounted if it is mounted */ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) err(1, "getmntinfo"); for (i = 0; i < mntsize; i++) { if (strcasecmp(mntbuf[i].f_mntfromname, "procfs") == 0) { strlcpy(procfs_path, mntbuf[i].f_mntonname, sizeof(procfs_path)); have_procfs = 1; break; } } if (!have_procfs) { errno = 2; err(1, "You must have a mounted procfs to use truss"); } while ((c = getopt(ac, av, "p:o:S")) != -1) { switch (c) { case 'p': /* specified pid */ trussinfo->pid = atoi(optarg); if (trussinfo->pid == getpid()) { /* make sure truss doesn't trace itself */ fprintf(stderr, "truss: attempt to self trace: %d\n", trussinfo->pid); exit(2); } break; case 'o': /* Specified output file */ fname = 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 */ if ((trussinfo->outfile = fopen(fname, "w")) == NULL) errx(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 { signal(SIGINT, restore_proc); signal(SIGTERM, restore_proc); signal(SIGQUIT, restore_proc); } /* * At this point, if we started the process, it is stopped waiting to * be woken up, either in exit() or in execve(). */ Procfd = start_tracing( trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | ((trussinfo->flags & NOSIGS) ? 0 : S_SIG)); if (Procfd == -1) return 0; pfs.why = 0; funcs = set_etype(trussinfo); /* * 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. */ do { int val = 0; if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) warn("PIOCWAIT top of loop"); else { switch(i = pfs.why) { case S_SCE: funcs->enter_syscall(trussinfo, pfs.val); break; case S_SCX: /* * This is so we don't get two messages for an exec -- one * for the S_EXEC, and one for the syscall exit. It also, * conveniently, ensures that the first message printed out * isn't the return-from-syscall used to create the process. */ if (in_exec) { in_exec = 0; break; } funcs->exit_syscall(trussinfo, pfs.val); break; case S_SIG: fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val); sigexit = pfs.val; break; case S_EXIT: fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val); break; case S_EXEC: funcs = set_etype(trussinfo); in_exec = 1; break; default: fprintf (trussinfo->outfile, "Process stopped because of: %d\n", i); break; } } if (ioctl(Procfd, PIOCCONT, val) == -1) { if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH) break; else warn("PIOCCONT"); } } while (pfs.why != S_EXIT); fflush(trussinfo->outfile); if (sigexit) { if (sigexit == SIGQUIT) exit(sigexit); (void) signal(sigexit, SIG_DFL); (void) kill(getpid(), sigexit); } return 0; }
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, "%jd.%09ld ", (intmax_t)timediff.tv_sec, timediff.tv_nsec); } if (trussinfo->flags & RELATIVETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->curthread->before, &timediff); fprintf(trussinfo->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, timediff.tv_nsec); } signame = strsig(trussinfo->pr_data); fprintf(trussinfo->outfile, "SIGNAL %u (%s)\n", trussinfo->pr_data, signame == NULL ? "?" : 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, "%jd.%09ld ", (intmax_t)timediff.tv_sec, timediff.tv_nsec); } if (trussinfo->flags & RELATIVETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->curthread->before, &timediff); fprintf(trussinfo->outfile, "%jd.%09ld ", (intmax_t)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); }