main.c revision 101423
131567Ssef/* 231899Ssef * Copryight 1997 Sean Eric Fagan 331899Ssef * 431899Ssef * Redistribution and use in source and binary forms, with or without 531899Ssef * modification, are permitted provided that the following conditions 631899Ssef * are met: 731899Ssef * 1. Redistributions of source code must retain the above copyright 831899Ssef * notice, this list of conditions and the following disclaimer. 931899Ssef * 2. Redistributions in binary form must reproduce the above copyright 1031899Ssef * notice, this list of conditions and the following disclaimer in the 1131899Ssef * documentation and/or other materials provided with the distribution. 1231899Ssef * 3. All advertising materials mentioning features or use of this software 1331899Ssef * must display the following acknowledgement: 1431899Ssef * This product includes software developed by Sean Eric Fagan 1531899Ssef * 4. Neither the name of the author may be used to endorse or promote 1631899Ssef * products derived from this software without specific prior written 1731899Ssef * permission. 1831899Ssef * 1931899Ssef * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2031899Ssef * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2131899Ssef * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2231899Ssef * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2331899Ssef * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2431899Ssef * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2531899Ssef * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2631899Ssef * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2731899Ssef * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2831899Ssef * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2931899Ssef * SUCH DAMAGE. 3031899Ssef */ 3131899Ssef 3232275Scharnier#ifndef lint 3332275Scharnierstatic const char rcsid[] = 3450477Speter "$FreeBSD: head/usr.bin/truss/main.c 101423 2002-08-06 12:46:14Z mdodd $"; 3532275Scharnier#endif /* not lint */ 3632275Scharnier 3731899Ssef/* 3831567Ssef * The main module for truss. Suprisingly simple, but, then, the other 3931567Ssef * files handle the bulk of the work. And, of course, the kernel has to 4031567Ssef * do a lot of the work :). 4131567Ssef */ 4231567Ssef 4385301Sdes#include <sys/param.h> 4485301Sdes#include <sys/ioctl.h> 4585301Sdes#include <sys/pioctl.h> 4685301Sdes 4732275Scharnier#include <err.h> 4832275Scharnier#include <errno.h> 4932275Scharnier#include <fcntl.h> 5032275Scharnier#include <signal.h> 5131567Ssef#include <stdio.h> 5231567Ssef#include <stdlib.h> 5331567Ssef#include <string.h> 54101423Smdodd#include <time.h> 5531579Speter#include <unistd.h> 5631567Ssef 57101282Smdodd#include "truss.h" 5887703Smarkm#include "extern.h" 5931567Ssef 6031567Ssef/* 61101282Smdodd * It's difficult to parameterize this because it must be 62101282Smdodd * accessible in a signal handler. 6331567Ssef */ 6431567Ssef 6531567Ssefint Procfd; 6631567Ssef 67100357Smarkmstatic __inline void 6832275Scharnierusage(void) 6932275Scharnier{ 7032275Scharnier fprintf(stderr, "%s\n%s\n", 71101289Smdodd "usage: truss [-faedDS] [-o file] -p pid", 72101289Smdodd " truss [-faedDS] [-o file] command [args]"); 7331567Ssef exit(1); 7431567Ssef} 7531567Ssef 7638897Ssef/* 7738897Ssef * WARNING! "FreeBSD a.out" must be first, or set_etype will not 7838897Ssef * work correctly. 7938897Ssef */ 8031567Ssefstruct ex_types { 8187703Smarkm const char *type; 82101282Smdodd void (*enter_syscall)(struct trussinfo *, int); 83101282Smdodd int (*exit_syscall)(struct trussinfo *, int); 8431567Ssef} ex_types[] = { 8539908Ssef#ifdef __alpha__ 8639908Ssef { "FreeBSD ELF", alpha_syscall_entry, alpha_syscall_exit }, 8739908Ssef#endif 8839908Ssef#ifdef __i386__ 8931567Ssef { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 9031580Speter { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 91101288Smdodd { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, 9231567Ssef { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 9339908Ssef#endif 94101320Sjake#ifdef __sparc64__ 95101320Sjake { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, 96101320Sjake#endif 9731567Ssef { 0, 0, 0 }, 9831567Ssef}; 9931567Ssef 10031567Ssef/* 10131567Ssef * Set the execution type. This is called after every exec, and when 10231567Ssef * a process is first monitored. The procfs pseudo-file "etype" has 10331567Ssef * the execution module type -- see /proc/curproc/etype for an example. 10431567Ssef */ 10531567Ssef 10631567Ssefstatic struct ex_types * 107101282Smdoddset_etype(struct trussinfo *trussinfo) { 10831567Ssef struct ex_types *funcs; 10931567Ssef char etype[24]; 11087703Smarkm char progt[32]; 11131567Ssef int fd; 11231567Ssef 113101282Smdodd sprintf(etype, "/proc/%d/etype", trussinfo->pid); 11431567Ssef if ((fd = open(etype, O_RDONLY)) == -1) { 11587703Smarkm strcpy(progt, "FreeBSD a.out"); 11631567Ssef } else { 11787703Smarkm int len = read(fd, progt, sizeof(progt)); 11887703Smarkm progt[len-1] = '\0'; 11931580Speter close(fd); 12031567Ssef } 12131567Ssef 12231567Ssef for (funcs = ex_types; funcs->type; funcs++) 12387703Smarkm if (!strcmp(funcs->type, progt)) 12431567Ssef break; 12531567Ssef 12690401Sdes if (funcs->type == NULL) { 12738897Ssef funcs = &ex_types[0]; 12890401Sdes warn("Execution type %s is not supported -- using %s\n", 12990401Sdes progt, funcs->type); 13038897Ssef } 13131567Ssef return funcs; 13231567Ssef} 13331567Ssef 13432275Scharnierint 13531567Ssefmain(int ac, char **av) { 13631567Ssef int c; 13731567Ssef int i; 13831567Ssef char **command; 13931567Ssef struct procfs_status pfs; 14031567Ssef struct ex_types *funcs; 14131567Ssef int in_exec = 0; 14232275Scharnier char *fname = NULL; 14338520Scracauer int sigexit = 0; 144101282Smdodd struct trussinfo *trussinfo; 14531567Ssef 146101282Smdodd /* Initialize the trussinfo struct */ 147101282Smdodd trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); 148101282Smdodd if (trussinfo == NULL) 149101282Smdodd errx(1, "malloc() failed"); 150101282Smdodd bzero(trussinfo, sizeof(struct trussinfo)); 151101282Smdodd trussinfo->outfile = stderr; 152101282Smdodd 153101289Smdodd while ((c = getopt(ac, av, "p:o:faedDS")) != -1) { 15431567Ssef switch (c) { 15531567Ssef case 'p': /* specified pid */ 156101282Smdodd trussinfo->pid = atoi(optarg); 15731567Ssef break; 158101283Smdodd case 'f': /* Follow fork()'s */ 159101283Smdodd trussinfo->flags |= FOLLOWFORKS; 160101283Smdodd break; 161101289Smdodd case 'a': /* Print execve() argument strings. */ 162101289Smdodd trussinfo->flags |= EXECVEARGS; 163101289Smdodd break; 164101289Smdodd case 'e': /* Print execve() environment strings. */ 165101289Smdodd trussinfo->flags |= EXECVEENVS; 166101289Smdodd break; 167101285Smdodd case 'd': /* Absolute timestamps */ 168101285Smdodd trussinfo->flags |= ABSOLUTETIMESTAMPS; 169101285Smdodd break; 170101285Smdodd case 'D': /* Relative timestamps */ 171101285Smdodd trussinfo->flags |= RELATIVETIMESTAMPS; 172101285Smdodd break; 17331567Ssef case 'o': /* Specified output file */ 17432275Scharnier fname = optarg; 17531567Ssef break; 17631567Ssef case 'S': /* Don't trace signals */ 177101282Smdodd trussinfo->flags |= NOSIGS; 17831567Ssef break; 17931567Ssef default: 18031567Ssef usage(); 18131567Ssef } 18231567Ssef } 18331567Ssef 18431567Ssef ac -= optind; av += optind; 185101282Smdodd if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0)) 18631567Ssef usage(); 18731567Ssef 18832275Scharnier if (fname != NULL) { /* Use output file */ 189101282Smdodd if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 19032275Scharnier errx(1, "cannot open %s", fname); 19132275Scharnier } 19232275Scharnier 19331567Ssef /* 19431567Ssef * If truss starts the process itself, it will ignore some signals -- 19531567Ssef * they should be passed off to the process, which may or may not 19631567Ssef * exit. If, however, we are examining an already-running process, 19731567Ssef * then we restore the event mask on these same signals. 19831567Ssef */ 19931567Ssef 200101282Smdodd if (trussinfo->pid == 0) { /* Start a command ourselves */ 20131567Ssef command = av; 202101282Smdodd trussinfo->pid = setup_and_wait(command); 20331567Ssef signal(SIGINT, SIG_IGN); 20431567Ssef signal(SIGTERM, SIG_IGN); 20531567Ssef signal(SIGQUIT, SIG_IGN); 20631567Ssef } else { 20731567Ssef signal(SIGINT, restore_proc); 20831567Ssef signal(SIGTERM, restore_proc); 20931567Ssef signal(SIGQUIT, restore_proc); 21031567Ssef } 21131567Ssef 21231567Ssef 21331567Ssef /* 21431567Ssef * At this point, if we started the process, it is stopped waiting to 21531567Ssef * be woken up, either in exit() or in execve(). 21631567Ssef */ 21731567Ssef 218101283SmdoddSTART_TRACE: 219101282Smdodd Procfd = start_tracing( 220101282Smdodd trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 221101283Smdodd ((trussinfo->flags & NOSIGS) ? 0 : S_SIG), 222101283Smdodd ((trussinfo->flags & FOLLOWFORKS) ? PF_FORK : 0)); 22355707Ssef if (Procfd == -1) 22455707Ssef return 0; 22555707Ssef 22631567Ssef pfs.why = 0; 22731567Ssef 228101282Smdodd funcs = set_etype(trussinfo); 22931567Ssef /* 23031567Ssef * At this point, it's a simple loop, waiting for the process to 23131567Ssef * stop, finding out why, printing out why, and then continuing it. 23231567Ssef * All of the grunt work is done in the support routines. 23331567Ssef */ 23431567Ssef 235101373Smdodd clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); 236101285Smdodd 23731567Ssef do { 23831567Ssef int val = 0; 23931567Ssef 24031567Ssef if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 24132275Scharnier warn("PIOCWAIT top of loop"); 24231567Ssef else { 24331567Ssef switch(i = pfs.why) { 24431567Ssef case S_SCE: 245101282Smdodd funcs->enter_syscall(trussinfo, pfs.val); 246101373Smdodd clock_gettime(CLOCK_REALTIME, &trussinfo->before); 24731567Ssef break; 24831567Ssef case S_SCX: 249101373Smdodd clock_gettime(CLOCK_REALTIME, &trussinfo->after); 25031567Ssef /* 25131567Ssef * This is so we don't get two messages for an exec -- one 25231567Ssef * for the S_EXEC, and one for the syscall exit. It also, 25331567Ssef * conveniently, ensures that the first message printed out 25431567Ssef * isn't the return-from-syscall used to create the process. 25531567Ssef */ 25631567Ssef 25731567Ssef if (in_exec) { 25831567Ssef in_exec = 0; 25931567Ssef break; 26031567Ssef } 261101283Smdodd 262101283Smdodd if (trussinfo->in_fork && (trussinfo->flags & FOLLOWFORKS)) { 263101283Smdodd int childpid; 264101283Smdodd 265101283Smdodd trussinfo->in_fork = 0; 266101283Smdodd childpid = funcs->exit_syscall(trussinfo, pfs.val); 267101283Smdodd 268101283Smdodd /* 269101283Smdodd * Fork a new copy of ourself to trace the child of the 270101283Smdodd * original traced process. 271101283Smdodd */ 272101283Smdodd if (fork() == 0) { 273101283Smdodd trussinfo->pid = childpid; 274101283Smdodd goto START_TRACE; 275101283Smdodd } 276101283Smdodd break; 277101283Smdodd } 278101282Smdodd funcs->exit_syscall(trussinfo, pfs.val); 27931567Ssef break; 28031567Ssef case S_SIG: 281101282Smdodd fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val); 28238520Scracauer sigexit = pfs.val; 28331567Ssef break; 28431567Ssef case S_EXIT: 285101282Smdodd fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val); 28631567Ssef break; 28731567Ssef case S_EXEC: 288101282Smdodd funcs = set_etype(trussinfo); 28931567Ssef in_exec = 1; 29031567Ssef break; 29131567Ssef default: 292101282Smdodd fprintf (trussinfo->outfile, "Process stopped because of: %d\n", i); 29331567Ssef break; 29431567Ssef } 29531567Ssef } 29655707Ssef if (ioctl(Procfd, PIOCCONT, val) == -1) { 297101282Smdodd if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH) 29855707Ssef break; 29955707Ssef else 30055707Ssef warn("PIOCCONT"); 30155707Ssef } 30231567Ssef } while (pfs.why != S_EXIT); 303101282Smdodd fflush(trussinfo->outfile); 30438520Scracauer if (sigexit) { 30538520Scracauer if (sigexit == SIGQUIT) 30638520Scracauer exit(sigexit); 30738520Scracauer (void) signal(sigexit, SIG_DFL); 30838520Scracauer (void) kill(getpid(), sigexit); 30938520Scracauer } 31031567Ssef return 0; 31131567Ssef} 312