main.c revision 104581
112144Sphk/* 22893Sdfr * Copryight 1997 Sean Eric Fagan 32893Sdfr * 42893Sdfr * Redistribution and use in source and binary forms, with or without 52893Sdfr * modification, are permitted provided that the following conditions 68876Srgrimes * are met: 72893Sdfr * 1. Redistributions of source code must retain the above copyright 82893Sdfr * notice, this list of conditions and the following disclaimer. 98876Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 102893Sdfr * notice, this list of conditions and the following disclaimer in the 118876Srgrimes * documentation and/or other materials provided with the distribution. 122893Sdfr * 3. All advertising materials mentioning features or use of this software 132893Sdfr * must display the following acknowledgement: 142893Sdfr * This product includes software developed by Sean Eric Fagan 152893Sdfr * 4. Neither the name of the author may be used to endorse or promote 168876Srgrimes * products derived from this software without specific prior written 172893Sdfr * permission. 182893Sdfr * 192893Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 202893Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 212893Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 222893Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 232893Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 242893Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 252893Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 263152Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 277465Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 282893Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 292893Sdfr * SUCH DAMAGE. 302893Sdfr */ 312893Sdfr 322893Sdfr#ifndef lint 332893Sdfrstatic const char rcsid[] = 342893Sdfr "$FreeBSD: head/usr.bin/truss/main.c 104581 2002-10-06 21:46:04Z mike $"; 357465Sache#endif /* not lint */ 362893Sdfr 3712144Sphk/* 387465Sache * The main module for truss. Suprisingly simple, but, then, the other 397465Sache * files handle the bulk of the work. And, of course, the kernel has to 402893Sdfr * do a lot of the work :). 412893Sdfr */ 422893Sdfr 437465Sache#include <sys/param.h> 442893Sdfr#include <sys/ioctl.h> 4512144Sphk#include <sys/pioctl.h> 467465Sache#include <sys/time.h> 477465Sache 482893Sdfr#include <err.h> 492893Sdfr#include <errno.h> 502893Sdfr#include <fcntl.h> 512893Sdfr#include <signal.h> 522893Sdfr#include <stdio.h> 532893Sdfr#include <stdlib.h> 542893Sdfr#include <string.h> 552893Sdfr#include <time.h> 562893Sdfr#include <unistd.h> 572893Sdfr 582893Sdfr#include "truss.h" 592893Sdfr#include "extern.h" 602893Sdfr 612893Sdfr/* 622893Sdfr * It's difficult to parameterize this because it must be 632893Sdfr * accessible in a signal handler. 642893Sdfr */ 652893Sdfr 662893Sdfrint Procfd; 672893Sdfr 682893Sdfrstatic __inline void 692893Sdfrusage(void) 702893Sdfr{ 712893Sdfr fprintf(stderr, "%s\n%s\n", 722893Sdfr "usage: truss [-faedDS] [-o file] -p pid", 732893Sdfr " truss [-faedDS] [-o file] command [args]"); 742893Sdfr exit(1); 752893Sdfr} 762893Sdfr 772893Sdfr/* 782893Sdfr * WARNING! "FreeBSD a.out" must be first, or set_etype will not 792893Sdfr * work correctly. 807465Sache */ 812893Sdfrstruct ex_types { 822893Sdfr const char *type; 832893Sdfr void (*enter_syscall)(struct trussinfo *, int); 842893Sdfr int (*exit_syscall)(struct trussinfo *, int); 852893Sdfr} ex_types[] = { 862893Sdfr#ifdef __alpha__ 872893Sdfr { "FreeBSD ELF", alpha_syscall_entry, alpha_syscall_exit }, 882893Sdfr#endif 892893Sdfr#ifdef __i386__ 902893Sdfr { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 912893Sdfr { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 922893Sdfr { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, 932893Sdfr { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 942893Sdfr#endif 952893Sdfr#ifdef __sparc64__ 962893Sdfr { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, 972893Sdfr#endif 982893Sdfr { 0, 0, 0 }, 992893Sdfr}; 1002893Sdfr 1012893Sdfr/* 1022893Sdfr * Set the execution type. This is called after every exec, and when 1037465Sache * a process is first monitored. The procfs pseudo-file "etype" has 1047465Sache * the execution module type -- see /proc/curproc/etype for an example. 1057465Sache */ 1067465Sache 1072893Sdfrstatic struct ex_types * 1082893Sdfrset_etype(struct trussinfo *trussinfo) { 1092893Sdfr struct ex_types *funcs; 1102893Sdfr char etype[24]; 1112893Sdfr char progt[32]; 1122893Sdfr int fd; 1132893Sdfr 1142893Sdfr sprintf(etype, "/proc/%d/etype", trussinfo->pid); 1152893Sdfr if ((fd = open(etype, O_RDONLY)) == -1) { 1162893Sdfr strcpy(progt, "FreeBSD a.out"); 1172893Sdfr } else { 1182893Sdfr int len = read(fd, progt, sizeof(progt)); 1192893Sdfr progt[len-1] = '\0'; 1202893Sdfr close(fd); 1212893Sdfr } 1222893Sdfr 1232893Sdfr for (funcs = ex_types; funcs->type; funcs++) 1242893Sdfr if (!strcmp(funcs->type, progt)) 1252893Sdfr break; 1262893Sdfr 1272893Sdfr if (funcs->type == NULL) { 1282893Sdfr funcs = &ex_types[0]; 1292893Sdfr warn("Execution type %s is not supported -- using %s\n", 1302893Sdfr progt, funcs->type); 1312893Sdfr } 1322893Sdfr return funcs; 1332893Sdfr} 1342893Sdfr 1352893Sdfrint 1362893Sdfrmain(int ac, char **av) { 1372893Sdfr int c; 1382893Sdfr int i; 1392893Sdfr char **command; 1402893Sdfr struct procfs_status pfs; 1412893Sdfr struct ex_types *funcs; 1422893Sdfr int in_exec = 0; 1432893Sdfr char *fname = NULL; 14411921Sphk int sigexit = 0; 14511921Sphk struct trussinfo *trussinfo; 1462893Sdfr 1472893Sdfr /* Initialize the trussinfo struct */ 1482893Sdfr trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); 1495083Sbde if (trussinfo == NULL) 1502893Sdfr errx(1, "malloc() failed"); 1512893Sdfr bzero(trussinfo, sizeof(struct trussinfo)); 1522893Sdfr trussinfo->outfile = stderr; 1532893Sdfr 1542893Sdfr while ((c = getopt(ac, av, "p:o:faedDS")) != -1) { 1552893Sdfr switch (c) { 1562893Sdfr case 'p': /* specified pid */ 1572893Sdfr trussinfo->pid = atoi(optarg); 1582893Sdfr break; 1592893Sdfr case 'f': /* Follow fork()'s */ 1607465Sache trussinfo->flags |= FOLLOWFORKS; 1617465Sache break; 1627465Sache case 'a': /* Print execve() argument strings. */ 1637465Sache trussinfo->flags |= EXECVEARGS; 1642893Sdfr break; 1652893Sdfr case 'e': /* Print execve() environment strings. */ 1667465Sache trussinfo->flags |= EXECVEENVS; 1673152Sphk break; 1683152Sphk case 'd': /* Absolute timestamps */ 1693152Sphk trussinfo->flags |= ABSOLUTETIMESTAMPS; 1702893Sdfr break; 1712893Sdfr case 'D': /* Relative timestamps */ 1727465Sache trussinfo->flags |= RELATIVETIMESTAMPS; 1737465Sache break; 1742893Sdfr case 'o': /* Specified output file */ 1752893Sdfr fname = optarg; 1762893Sdfr break; 1772893Sdfr case 'S': /* Don't trace signals */ 1787465Sache trussinfo->flags |= NOSIGS; 1792893Sdfr break; 1802893Sdfr default: 1812893Sdfr usage(); 1822893Sdfr } 1832893Sdfr } 1842893Sdfr 1852893Sdfr ac -= optind; av += optind; 1862893Sdfr if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0)) 1872893Sdfr usage(); 1882893Sdfr 1892893Sdfr if (fname != NULL) { /* Use output file */ 1902893Sdfr if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 1912893Sdfr errx(1, "cannot open %s", fname); 1922893Sdfr } 1932893Sdfr 1942893Sdfr /* 1952893Sdfr * If truss starts the process itself, it will ignore some signals -- 1962893Sdfr * they should be passed off to the process, which may or may not 1972893Sdfr * exit. If, however, we are examining an already-running process, 1982893Sdfr * then we restore the event mask on these same signals. 1992893Sdfr */ 2002893Sdfr 2012893Sdfr if (trussinfo->pid == 0) { /* Start a command ourselves */ 2022893Sdfr command = av; 2032893Sdfr trussinfo->pid = setup_and_wait(command); 2042893Sdfr signal(SIGINT, SIG_IGN); 2052893Sdfr signal(SIGTERM, SIG_IGN); 2062893Sdfr signal(SIGQUIT, SIG_IGN); 2072893Sdfr } else { 2082893Sdfr signal(SIGINT, restore_proc); 2092893Sdfr signal(SIGTERM, restore_proc); 2102893Sdfr signal(SIGQUIT, restore_proc); 2112893Sdfr } 2122893Sdfr 2132893Sdfr 2142893Sdfr /* 2152893Sdfr * At this point, if we started the process, it is stopped waiting to 2162893Sdfr * be woken up, either in exit() or in execve(). 2172893Sdfr */ 2182893Sdfr 2192893SdfrSTART_TRACE: 2202893Sdfr Procfd = start_tracing( 2212893Sdfr trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 2222893Sdfr ((trussinfo->flags & NOSIGS) ? 0 : S_SIG), 2232893Sdfr ((trussinfo->flags & FOLLOWFORKS) ? PF_FORK : 0)); 2242893Sdfr if (Procfd == -1) 2252893Sdfr return 0; 2262893Sdfr 2272893Sdfr pfs.why = 0; 2282893Sdfr 2292893Sdfr funcs = set_etype(trussinfo); 2302893Sdfr /* 2312893Sdfr * At this point, it's a simple loop, waiting for the process to 2322893Sdfr * stop, finding out why, printing out why, and then continuing it. 2332893Sdfr * All of the grunt work is done in the support routines. 2342893Sdfr */ 2352893Sdfr 2362893Sdfr clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); 2372893Sdfr 2382893Sdfr do { 2392893Sdfr int val = 0; 2402893Sdfr 2412893Sdfr if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 2422893Sdfr warn("PIOCWAIT top of loop"); 2432893Sdfr else { 2442893Sdfr switch(i = pfs.why) { 2452893Sdfr case S_SCE: 2462893Sdfr funcs->enter_syscall(trussinfo, pfs.val); 2472893Sdfr clock_gettime(CLOCK_REALTIME, &trussinfo->before); 2482893Sdfr break; 2492893Sdfr case S_SCX: 2502893Sdfr clock_gettime(CLOCK_REALTIME, &trussinfo->after); 2512893Sdfr /* 2522893Sdfr * This is so we don't get two messages for an exec -- one 2532893Sdfr * for the S_EXEC, and one for the syscall exit. It also, 2542893Sdfr * conveniently, ensures that the first message printed out 2552893Sdfr * isn't the return-from-syscall used to create the process. 2562893Sdfr */ 2572893Sdfr 2582893Sdfr if (in_exec) { 2592893Sdfr in_exec = 0; 2602893Sdfr break; 2612893Sdfr } 2622893Sdfr 2632893Sdfr if (trussinfo->in_fork && (trussinfo->flags & FOLLOWFORKS)) { 2642893Sdfr int childpid; 2652893Sdfr 2662893Sdfr trussinfo->in_fork = 0; 2672893Sdfr childpid = funcs->exit_syscall(trussinfo, pfs.val); 2682893Sdfr 2692893Sdfr /* 2702893Sdfr * Fork a new copy of ourself to trace the child of the 2712893Sdfr * original traced process. 2722893Sdfr */ 2732893Sdfr if (fork() == 0) { 2742893Sdfr trussinfo->pid = childpid; 2752893Sdfr goto START_TRACE; 2762893Sdfr } 2772893Sdfr break; 2782893Sdfr } 2792893Sdfr funcs->exit_syscall(trussinfo, pfs.val); 2802893Sdfr break; 2812893Sdfr case S_SIG: 2822893Sdfr fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val); 2832893Sdfr sigexit = pfs.val; 2842893Sdfr break; 2852893Sdfr case S_EXIT: 2862893Sdfr fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val); 2872893Sdfr break; 2882893Sdfr case S_EXEC: 2892893Sdfr funcs = set_etype(trussinfo); 2902893Sdfr in_exec = 1; 2912893Sdfr break; 2922893Sdfr default: 2932893Sdfr fprintf (trussinfo->outfile, "Process stopped because of: %d\n", i); 2942893Sdfr break; 2952893Sdfr } 2962893Sdfr } 2972893Sdfr if (ioctl(Procfd, PIOCCONT, val) == -1) { 2982893Sdfr if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH) 2992893Sdfr break; 3002893Sdfr else 3012893Sdfr warn("PIOCCONT"); 3022893Sdfr } 3032893Sdfr } while (pfs.why != S_EXIT); 3042893Sdfr fflush(trussinfo->outfile); 3052893Sdfr if (sigexit) { 3062893Sdfr if (sigexit == SIGQUIT) 3072893Sdfr exit(sigexit); 3082893Sdfr (void) signal(sigexit, SIG_DFL); 3092893Sdfr (void) kill(getpid(), sigexit); 3102893Sdfr } 3112893Sdfr return 0; 3122893Sdfr} 3132893Sdfr