main.c revision 130394
1329462Skib/* 21817Sdg * Copryight 1997 Sean Eric Fagan 31817Sdg * 41817Sdg * Redistribution and use in source and binary forms, with or without 51817Sdg * modification, are permitted provided that the following conditions 6329462Skib * are met: 7329462Skib * 1. Redistributions of source code must retain the above copyright 8329462Skib * notice, this list of conditions and the following disclaimer. 9329462Skib * 2. Redistributions in binary form must reproduce the above copyright 10329462Skib * notice, this list of conditions and the following disclaimer in the 11329462Skib * documentation and/or other materials provided with the distribution. 12329462Skib * 3. All advertising materials mentioning features or use of this software 131817Sdg * must display the following acknowledgement: 141817Sdg * This product includes software developed by Sean Eric Fagan 151817Sdg * 4. Neither the name of the author may be used to endorse or promote 161817Sdg * products derived from this software without specific prior written 171817Sdg * permission. 181817Sdg * 191817Sdg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 201817Sdg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 211817Sdg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 221817Sdg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 231817Sdg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 241817Sdg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 251817Sdg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 261817Sdg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 271817Sdg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 281817Sdg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 291817Sdg * SUCH DAMAGE. 301817Sdg */ 311817Sdg 321817Sdg#include <sys/cdefs.h> 331817Sdg__FBSDID("$FreeBSD: head/usr.bin/truss/main.c 130394 2004-06-12 22:49:06Z dwmalone $"); 341817Sdg 351817Sdg/* 361817Sdg * The main module for truss. Suprisingly simple, but, then, the other 3750477Speter * files handle the bulk of the work. And, of course, the kernel has to 381817Sdg * do a lot of the work :). 391817Sdg */ 402579Sbde 412579Sbde#include <sys/param.h> 422123Sjkh#include <sys/ioctl.h> 4316029Speter#include <sys/pioctl.h> 442579Sbde#include <sys/types.h> 4518961Sbde#include <sys/time.h> 4613107Sbde#include <sys/resource.h> 4725083Sjdp 48163726Sbde#include <err.h> 4925083Sjdp#include <errno.h> 5025083Sjdp#include <fcntl.h> 51163726Sbde#include <signal.h> 5225083Sjdp#include <stdio.h> 5325083Sjdp#include <stdlib.h> 5425083Sjdp#include <string.h> 55114349Speter#include <time.h> 5622636Sbde#include <unistd.h> 5725083Sjdp 5822636Sbde#include "truss.h" 59114349Speter#include "extern.h" 6022636Sbde 6125083Sjdp/* 62757Sdg * It's difficult to parameterize this because it must be 6325083Sjdp * accessible in a signal handler. 6446548Sbde */ 6513107Sbde 6618961Sbdeint Procfd; 67757Sdg 68171914Sjkoshystatic __inline void 69171914Sjkoshyusage(void) 70757Sdg{ 71757Sdg fprintf(stderr, "%s\n%s\n", 7246548Sbde "usage: truss [-faedDS] [-o file] -p pid", 7313107Sbde " truss [-faedDS] [-o file] command [args]"); 7413107Sbde exit(1); 7513107Sbde} 7613107Sbde 7713107Sbde/* 7813107Sbde * WARNING! "FreeBSD a.out" must be first, or set_etype will not 7913107Sbde * work correctly. 8013107Sbde */ 8146548Sbdestruct ex_types { 8218961Sbde const char *type; 8318961Sbde void (*enter_syscall)(struct trussinfo *, int); 8446548Sbde long (*exit_syscall)(struct trussinfo *, int); 8518961Sbde} ex_types[] = { 8618961Sbde#ifdef __alpha__ 8713107Sbde { "FreeBSD ELF", alpha_syscall_entry, alpha_syscall_exit }, 8846548Sbde#endif 8946548Sbde#ifdef __amd64__ 9013107Sbde { "FreeBSD ELF64", amd64_syscall_entry, amd64_syscall_exit }, 9118961Sbde#endif 9246548Sbde#ifdef __i386__ 9346548Sbde { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 9418961Sbde { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 9518961Sbde { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, 9613107Sbde { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 9713107Sbde#endif 9813107Sbde#ifdef __ia64__ 9913107Sbde { "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit }, 10013107Sbde#endif 10113107Sbde#ifdef __sparc64__ 10213107Sbde { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, 10313107Sbde#endif 10413107Sbde { 0, 0, 0 }, 10518961Sbde}; 10618961Sbde 10718961Sbde/* 10818961Sbde * Set the execution type. This is called after every exec, and when 109757Sdg * a process is first monitored. The procfs pseudo-file "etype" has 11013107Sbde * the execution module type -- see /proc/curproc/etype for an example. 11118961Sbde */ 11218961Sbde 11318961Sbdestatic struct ex_types * 11418961Sbdeset_etype(struct trussinfo *trussinfo) { 11513107Sbde struct ex_types *funcs; 116122940Speter char etype[24]; 11713107Sbde char progt[32]; 11813107Sbde int fd; 119163722Sbde 120163726Sbde sprintf(etype, "/proc/%d/etype", trussinfo->pid); 12118961Sbde if ((fd = open(etype, O_RDONLY)) == -1) { 122163722Sbde strcpy(progt, "FreeBSD a.out"); 123163722Sbde } else { 124163722Sbde int len = read(fd, progt, sizeof(progt)); 12518961Sbde progt[len-1] = '\0'; 12618961Sbde close(fd); 127757Sdg } 128757Sdg 129757Sdg for (funcs = ex_types; funcs->type; funcs++) 13013107Sbde if (!strcmp(funcs->type, progt)) 13113107Sbde break; 132757Sdg 13313107Sbde if (funcs->type == NULL) { 13418961Sbde funcs = &ex_types[0]; 13518961Sbde warn("execution type %s is not supported -- using %s", 13613107Sbde progt, funcs->type); 13713107Sbde } 1381321Sdg return funcs; 13913107Sbde} 14013107Sbde 14113107Sbdeint 142757Sdgmain(int ac, char **av) { 143274489Sscottl int c; 144274489Sscottl int i; 145274489Sscottl char **command; 146274489Sscottl struct procfs_status pfs; 147274489Sscottl struct ex_types *funcs; 148274489Sscottl int in_exec = 0; 149274489Sscottl char *fname = NULL; 150274489Sscottl int sigexit = 0; 151274489Sscottl struct trussinfo *trussinfo; 152274489Sscottl 153122849Speter /* Initialize the trussinfo struct */ 154122849Speter trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); 155329462Skib if (trussinfo == NULL) 156329462Skib errx(1, "malloc() failed"); 157329462Skib bzero(trussinfo, sizeof(struct trussinfo)); 158329462Skib trussinfo->outfile = stderr; 159329462Skib 160329462Skib while ((c = getopt(ac, av, "p:o:faedDS")) != -1) { 161329462Skib switch (c) { 162329462Skib case 'p': /* specified pid */ 163156699Speter trussinfo->pid = atoi(optarg); 164122849Speter break; 165122849Speter case 'f': /* Follow fork()'s */ 166122849Speter trussinfo->flags |= FOLLOWFORKS; 167122849Speter break; 168329462Skib case 'a': /* Print execve() argument strings. */ 169329462Skib trussinfo->flags |= EXECVEARGS; 170329462Skib break; 171329462Skib case 'e': /* Print execve() environment strings. */ 172329462Skib trussinfo->flags |= EXECVEENVS; 173329462Skib break; 174153241Sjhb case 'd': /* Absolute timestamps */ 175329462Skib trussinfo->flags |= ABSOLUTETIMESTAMPS; 176329462Skib break; 177329462Skib case 'D': /* Relative timestamps */ 178329462Skib trussinfo->flags |= RELATIVETIMESTAMPS; 179329462Skib break; 180329462Skib case 'o': /* Specified output file */ 181329462Skib fname = optarg; 182329462Skib break; 183153241Sjhb case 'S': /* Don't trace signals */ 184329462Skib trussinfo->flags |= NOSIGS; 185329462Skib break; 186329462Skib default: 187329462Skib usage(); 188329462Skib } 189329462Skib } 190329462Skib 191329462Skib ac -= optind; av += optind; 192329462Skib if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0)) 193329462Skib usage(); 194153241Sjhb 195329462Skib if (fname != NULL) { /* Use output file */ 196329462Skib if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 197329462Skib errx(1, "cannot open %s", fname); 198329462Skib } 199329462Skib 200329462Skib /* 201329462Skib * If truss starts the process itself, it will ignore some signals -- 202329462Skib * they should be passed off to the process, which may or may not 203329462Skib * exit. If, however, we are examining an already-running process, 204329462Skib * then we restore the event mask on these same signals. 205329462Skib */ 206329462Skib 207329462Skib if (trussinfo->pid == 0) { /* Start a command ourselves */ 208329462Skib command = av; 209329462Skib trussinfo->pid = setup_and_wait(command); 210329462Skib signal(SIGINT, SIG_IGN); 211329462Skib signal(SIGTERM, SIG_IGN); 212329462Skib signal(SIGQUIT, SIG_IGN); 213329462Skib } else { 214329462Skib signal(SIGINT, restore_proc); 215329462Skib signal(SIGTERM, restore_proc); 216329462Skib signal(SIGQUIT, restore_proc); 217329462Skib } 218329462Skib 219329462Skib 220329462Skib /* 221329462Skib * At this point, if we started the process, it is stopped waiting to 222329462Skib * be woken up, either in exit() or in execve(). 223329462Skib */ 224329462Skib 225329462SkibSTART_TRACE: 226329462Skib Procfd = start_tracing( 227329462Skib trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 228329462Skib ((trussinfo->flags & NOSIGS) ? 0 : S_SIG), 229329462Skib ((trussinfo->flags & FOLLOWFORKS) ? PF_FORK : 0)); 230329462Skib if (Procfd == -1) 231329462Skib return 0; 232329462Skib 233329462Skib pfs.why = 0; 234329462Skib 235329462Skib funcs = set_etype(trussinfo); 236329462Skib /* 237329462Skib * At this point, it's a simple loop, waiting for the process to 238329462Skib * stop, finding out why, printing out why, and then continuing it. 239329462Skib * All of the grunt work is done in the support routines. 240329462Skib */ 241329462Skib 242329462Skib clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); 243329462Skib 244329462Skib do { 245329462Skib int val = 0; 246329462Skib 247329462Skib if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 248329462Skib warn("PIOCWAIT top of loop"); 249329462Skib else { 250329462Skib switch(i = pfs.why) { 251329462Skib case S_SCE: 252329462Skib funcs->enter_syscall(trussinfo, pfs.val); 253329462Skib clock_gettime(CLOCK_REALTIME, &trussinfo->before); 254329462Skib break; 255329462Skib case S_SCX: 256329462Skib clock_gettime(CLOCK_REALTIME, &trussinfo->after); 257329462Skib /* 258329462Skib * This is so we don't get two messages for an exec -- one 259329462Skib * for the S_EXEC, and one for the syscall exit. It also, 260329462Skib * conveniently, ensures that the first message printed out 261329462Skib * isn't the return-from-syscall used to create the process. 262329462Skib */ 263329462Skib 264329462Skib if (in_exec) { 265329462Skib in_exec = 0; 266329462Skib break; 267329462Skib } 268329462Skib 269329462Skib if (trussinfo->in_fork && (trussinfo->flags & FOLLOWFORKS)) { 270329462Skib int childpid; 271329462Skib 272329462Skib trussinfo->in_fork = 0; 273329462Skib childpid = funcs->exit_syscall(trussinfo, pfs.val); 274329462Skib 275329462Skib /* 276329462Skib * Fork a new copy of ourself to trace the child of the 277329462Skib * original traced process. 278329462Skib */ 279329462Skib if (fork() == 0) { 280329462Skib trussinfo->pid = childpid; 281329462Skib goto START_TRACE; 282329462Skib } 283329462Skib break; 284329462Skib } 285122849Speter funcs->exit_syscall(trussinfo, pfs.val); 286122849Speter break; 287263002Sroyger case S_SIG: 288263002Sroyger fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val); 289263002Sroyger sigexit = pfs.val; 290263002Sroyger break; 291263002Sroyger case S_EXIT: 292263002Sroyger fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val); 293263002Sroyger break; 294263002Sroyger case S_EXEC: 295263002Sroyger funcs = set_etype(trussinfo); 296263002Sroyger in_exec = 1; 297263002Sroyger break; 298263002Sroyger default: 299263002Sroyger fprintf (trussinfo->outfile, "Process stopped because of: %d\n", i); 300263002Sroyger break; 301263002Sroyger } 302263002Sroyger } 303263002Sroyger if (ioctl(Procfd, PIOCCONT, val) == -1) { 304263002Sroyger if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH) 305263002Sroyger break; 306263002Sroyger else 307263002Sroyger warn("PIOCCONT"); 308263002Sroyger } 309263002Sroyger } while (pfs.why != S_EXIT); 310263002Sroyger fflush(trussinfo->outfile); 311263002Sroyger if (sigexit) { 312263002Sroyger struct rlimit rlp; 3132579Sbde 314 rlp.rlim_cur = 0; 315 rlp.rlim_max = 0; 316 setrlimit(RLIMIT_CORE, &rlp); 317 (void) signal(sigexit, SIG_DFL); 318 (void) kill(getpid(), sigexit); 319 } 320 return 0; 321} 322