main.c revision 123916
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 32119852Scharnier#include <sys/cdefs.h> 33119852Scharnier__FBSDID("$FreeBSD: head/usr.bin/truss/main.c 123916 2003-12-28 01:20:03Z cracauer $"); 3432275Scharnier 3531899Ssef/* 3631567Ssef * The main module for truss. Suprisingly simple, but, then, the other 3731567Ssef * files handle the bulk of the work. And, of course, the kernel has to 3831567Ssef * do a lot of the work :). 3931567Ssef */ 4031567Ssef 4185301Sdes#include <sys/param.h> 4285301Sdes#include <sys/ioctl.h> 4385301Sdes#include <sys/pioctl.h> 44123916Scracauer#include <sys/types.h> 45104581Smike#include <sys/time.h> 46123916Scracauer#include <sys/resource.h> 4785301Sdes 4832275Scharnier#include <err.h> 4932275Scharnier#include <errno.h> 5032275Scharnier#include <fcntl.h> 5132275Scharnier#include <signal.h> 5231567Ssef#include <stdio.h> 5331567Ssef#include <stdlib.h> 5431567Ssef#include <string.h> 55101423Smdodd#include <time.h> 5631579Speter#include <unistd.h> 5731567Ssef 58101282Smdodd#include "truss.h" 5987703Smarkm#include "extern.h" 6031567Ssef 6131567Ssef/* 62101282Smdodd * It's difficult to parameterize this because it must be 63101282Smdodd * accessible in a signal handler. 6431567Ssef */ 6531567Ssef 6631567Ssefint Procfd; 6731567Ssef 68100357Smarkmstatic __inline void 6932275Scharnierusage(void) 7032275Scharnier{ 7132275Scharnier fprintf(stderr, "%s\n%s\n", 72101289Smdodd "usage: truss [-faedDS] [-o file] -p pid", 73101289Smdodd " truss [-faedDS] [-o file] command [args]"); 7431567Ssef exit(1); 7531567Ssef} 7631567Ssef 7738897Ssef/* 7838897Ssef * WARNING! "FreeBSD a.out" must be first, or set_etype will not 7938897Ssef * work correctly. 8038897Ssef */ 8131567Ssefstruct ex_types { 8287703Smarkm const char *type; 83101282Smdodd void (*enter_syscall)(struct trussinfo *, int); 84122348Smarcel long (*exit_syscall)(struct trussinfo *, int); 8531567Ssef} ex_types[] = { 8639908Ssef#ifdef __alpha__ 8739908Ssef { "FreeBSD ELF", alpha_syscall_entry, alpha_syscall_exit }, 8839908Ssef#endif 8939908Ssef#ifdef __i386__ 9031567Ssef { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 9131580Speter { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 92101288Smdodd { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, 9331567Ssef { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 9439908Ssef#endif 95106716Smarcel#ifdef __ia64__ 96106716Smarcel { "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit }, 97106716Smarcel#endif 98101320Sjake#ifdef __sparc64__ 99101320Sjake { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, 100101320Sjake#endif 10131567Ssef { 0, 0, 0 }, 10231567Ssef}; 10331567Ssef 10431567Ssef/* 10531567Ssef * Set the execution type. This is called after every exec, and when 10631567Ssef * a process is first monitored. The procfs pseudo-file "etype" has 10731567Ssef * the execution module type -- see /proc/curproc/etype for an example. 10831567Ssef */ 10931567Ssef 11031567Ssefstatic struct ex_types * 111101282Smdoddset_etype(struct trussinfo *trussinfo) { 11231567Ssef struct ex_types *funcs; 11331567Ssef char etype[24]; 11487703Smarkm char progt[32]; 11531567Ssef int fd; 11631567Ssef 117101282Smdodd sprintf(etype, "/proc/%d/etype", trussinfo->pid); 11831567Ssef if ((fd = open(etype, O_RDONLY)) == -1) { 11987703Smarkm strcpy(progt, "FreeBSD a.out"); 12031567Ssef } else { 12187703Smarkm int len = read(fd, progt, sizeof(progt)); 12287703Smarkm progt[len-1] = '\0'; 12331580Speter close(fd); 12431567Ssef } 12531567Ssef 12631567Ssef for (funcs = ex_types; funcs->type; funcs++) 12787703Smarkm if (!strcmp(funcs->type, progt)) 12831567Ssef break; 12931567Ssef 13090401Sdes if (funcs->type == NULL) { 13138897Ssef funcs = &ex_types[0]; 132119852Scharnier warn("execution type %s is not supported -- using %s", 13390401Sdes progt, funcs->type); 13438897Ssef } 13531567Ssef return funcs; 13631567Ssef} 13731567Ssef 13832275Scharnierint 13931567Ssefmain(int ac, char **av) { 14031567Ssef int c; 14131567Ssef int i; 14231567Ssef char **command; 14331567Ssef struct procfs_status pfs; 14431567Ssef struct ex_types *funcs; 14531567Ssef int in_exec = 0; 14632275Scharnier char *fname = NULL; 14738520Scracauer int sigexit = 0; 148101282Smdodd struct trussinfo *trussinfo; 14931567Ssef 150101282Smdodd /* Initialize the trussinfo struct */ 151101282Smdodd trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); 152101282Smdodd if (trussinfo == NULL) 153101282Smdodd errx(1, "malloc() failed"); 154101282Smdodd bzero(trussinfo, sizeof(struct trussinfo)); 155101282Smdodd trussinfo->outfile = stderr; 156101282Smdodd 157101289Smdodd while ((c = getopt(ac, av, "p:o:faedDS")) != -1) { 15831567Ssef switch (c) { 15931567Ssef case 'p': /* specified pid */ 160101282Smdodd trussinfo->pid = atoi(optarg); 16131567Ssef break; 162101283Smdodd case 'f': /* Follow fork()'s */ 163101283Smdodd trussinfo->flags |= FOLLOWFORKS; 164101283Smdodd break; 165101289Smdodd case 'a': /* Print execve() argument strings. */ 166101289Smdodd trussinfo->flags |= EXECVEARGS; 167101289Smdodd break; 168101289Smdodd case 'e': /* Print execve() environment strings. */ 169101289Smdodd trussinfo->flags |= EXECVEENVS; 170101289Smdodd break; 171101285Smdodd case 'd': /* Absolute timestamps */ 172101285Smdodd trussinfo->flags |= ABSOLUTETIMESTAMPS; 173101285Smdodd break; 174101285Smdodd case 'D': /* Relative timestamps */ 175101285Smdodd trussinfo->flags |= RELATIVETIMESTAMPS; 176101285Smdodd break; 17731567Ssef case 'o': /* Specified output file */ 17832275Scharnier fname = optarg; 17931567Ssef break; 18031567Ssef case 'S': /* Don't trace signals */ 181101282Smdodd trussinfo->flags |= NOSIGS; 18231567Ssef break; 18331567Ssef default: 18431567Ssef usage(); 18531567Ssef } 18631567Ssef } 18731567Ssef 18831567Ssef ac -= optind; av += optind; 189101282Smdodd if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0)) 19031567Ssef usage(); 19131567Ssef 19232275Scharnier if (fname != NULL) { /* Use output file */ 193101282Smdodd if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 19432275Scharnier errx(1, "cannot open %s", fname); 19532275Scharnier } 19632275Scharnier 19731567Ssef /* 19831567Ssef * If truss starts the process itself, it will ignore some signals -- 19931567Ssef * they should be passed off to the process, which may or may not 20031567Ssef * exit. If, however, we are examining an already-running process, 20131567Ssef * then we restore the event mask on these same signals. 20231567Ssef */ 20331567Ssef 204101282Smdodd if (trussinfo->pid == 0) { /* Start a command ourselves */ 20531567Ssef command = av; 206101282Smdodd trussinfo->pid = setup_and_wait(command); 20731567Ssef signal(SIGINT, SIG_IGN); 20831567Ssef signal(SIGTERM, SIG_IGN); 20931567Ssef signal(SIGQUIT, SIG_IGN); 21031567Ssef } else { 21131567Ssef signal(SIGINT, restore_proc); 21231567Ssef signal(SIGTERM, restore_proc); 21331567Ssef signal(SIGQUIT, restore_proc); 21431567Ssef } 21531567Ssef 21631567Ssef 21731567Ssef /* 21831567Ssef * At this point, if we started the process, it is stopped waiting to 21931567Ssef * be woken up, either in exit() or in execve(). 22031567Ssef */ 22131567Ssef 222101283SmdoddSTART_TRACE: 223101282Smdodd Procfd = start_tracing( 224101282Smdodd trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 225101283Smdodd ((trussinfo->flags & NOSIGS) ? 0 : S_SIG), 226101283Smdodd ((trussinfo->flags & FOLLOWFORKS) ? PF_FORK : 0)); 22755707Ssef if (Procfd == -1) 22855707Ssef return 0; 22955707Ssef 23031567Ssef pfs.why = 0; 23131567Ssef 232101282Smdodd funcs = set_etype(trussinfo); 23331567Ssef /* 23431567Ssef * At this point, it's a simple loop, waiting for the process to 23531567Ssef * stop, finding out why, printing out why, and then continuing it. 23631567Ssef * All of the grunt work is done in the support routines. 23731567Ssef */ 23831567Ssef 239101373Smdodd clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); 240101285Smdodd 24131567Ssef do { 24231567Ssef int val = 0; 24331567Ssef 24431567Ssef if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 24532275Scharnier warn("PIOCWAIT top of loop"); 24631567Ssef else { 24731567Ssef switch(i = pfs.why) { 24831567Ssef case S_SCE: 249101282Smdodd funcs->enter_syscall(trussinfo, pfs.val); 250101373Smdodd clock_gettime(CLOCK_REALTIME, &trussinfo->before); 25131567Ssef break; 25231567Ssef case S_SCX: 253101373Smdodd clock_gettime(CLOCK_REALTIME, &trussinfo->after); 25431567Ssef /* 25531567Ssef * This is so we don't get two messages for an exec -- one 25631567Ssef * for the S_EXEC, and one for the syscall exit. It also, 25731567Ssef * conveniently, ensures that the first message printed out 25831567Ssef * isn't the return-from-syscall used to create the process. 25931567Ssef */ 26031567Ssef 26131567Ssef if (in_exec) { 26231567Ssef in_exec = 0; 26331567Ssef break; 26431567Ssef } 265101283Smdodd 266101283Smdodd if (trussinfo->in_fork && (trussinfo->flags & FOLLOWFORKS)) { 267101283Smdodd int childpid; 268101283Smdodd 269101283Smdodd trussinfo->in_fork = 0; 270101283Smdodd childpid = funcs->exit_syscall(trussinfo, pfs.val); 271101283Smdodd 272101283Smdodd /* 273101283Smdodd * Fork a new copy of ourself to trace the child of the 274101283Smdodd * original traced process. 275101283Smdodd */ 276101283Smdodd if (fork() == 0) { 277101283Smdodd trussinfo->pid = childpid; 278101283Smdodd goto START_TRACE; 279101283Smdodd } 280101283Smdodd break; 281101283Smdodd } 282101282Smdodd funcs->exit_syscall(trussinfo, pfs.val); 28331567Ssef break; 28431567Ssef case S_SIG: 285101282Smdodd fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val); 28638520Scracauer sigexit = pfs.val; 28731567Ssef break; 28831567Ssef case S_EXIT: 289101282Smdodd fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val); 29031567Ssef break; 29131567Ssef case S_EXEC: 292101282Smdodd funcs = set_etype(trussinfo); 29331567Ssef in_exec = 1; 29431567Ssef break; 29531567Ssef default: 296101282Smdodd fprintf (trussinfo->outfile, "Process stopped because of: %d\n", i); 29731567Ssef break; 29831567Ssef } 29931567Ssef } 30055707Ssef if (ioctl(Procfd, PIOCCONT, val) == -1) { 301101282Smdodd if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH) 30255707Ssef break; 30355707Ssef else 30455707Ssef warn("PIOCCONT"); 30555707Ssef } 30631567Ssef } while (pfs.why != S_EXIT); 307101282Smdodd fflush(trussinfo->outfile); 30838520Scracauer if (sigexit) { 309123916Scracauer struct rlimit rlp; 310123916Scracauer 311123916Scracauer rlp.rlim_cur = 0; 312123916Scracauer rlp.rlim_max = 0; 313123916Scracauer setrlimit(RLIMIT_CORE, &rlp); 31438520Scracauer (void) signal(sigexit, SIG_DFL); 31538520Scracauer (void) kill(getpid(), sigexit); 31638520Scracauer } 31731567Ssef return 0; 31831567Ssef} 319