main.c revision 119852
1259701Sdim/* 2259701Sdim * Copryight 1997 Sean Eric Fagan 3259701Sdim * 4259701Sdim * Redistribution and use in source and binary forms, with or without 5259701Sdim * modification, are permitted provided that the following conditions 6259701Sdim * are met: 7259701Sdim * 1. Redistributions of source code must retain the above copyright 8259701Sdim * notice, this list of conditions and the following disclaimer. 9259701Sdim * 2. Redistributions in binary form must reproduce the above copyright 10259701Sdim * notice, this list of conditions and the following disclaimer in the 11259701Sdim * documentation and/or other materials provided with the distribution. 12259701Sdim * 3. All advertising materials mentioning features or use of this software 13259701Sdim * must display the following acknowledgement: 14259701Sdim * This product includes software developed by Sean Eric Fagan 15259701Sdim * 4. Neither the name of the author may be used to endorse or promote 16259701Sdim * products derived from this software without specific prior written 17259701Sdim * permission. 18259701Sdim * 19259701Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20259701Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21259701Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22259701Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23259701Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24259701Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25259701Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26259701Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27259701Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28259701Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29259701Sdim * SUCH DAMAGE. 30259701Sdim */ 31259701Sdim 32259701Sdim#include <sys/cdefs.h> 33259701Sdim__FBSDID("$FreeBSD: head/usr.bin/truss/main.c 119852 2003-09-07 15:50:43Z charnier $"); 34259701Sdim 35259701Sdim/* 36259701Sdim * The main module for truss. Suprisingly simple, but, then, the other 37259701Sdim * files handle the bulk of the work. And, of course, the kernel has to 38259701Sdim * do a lot of the work :). 39259701Sdim */ 40259701Sdim 41259701Sdim#include <sys/param.h> 42259701Sdim#include <sys/ioctl.h> 43259701Sdim#include <sys/pioctl.h> 44259701Sdim#include <sys/time.h> 45259701Sdim 46259701Sdim#include <err.h> 47259701Sdim#include <errno.h> 48259701Sdim#include <fcntl.h> 49259701Sdim#include <signal.h> 50259701Sdim#include <stdio.h> 51259701Sdim#include <stdlib.h> 52259701Sdim#include <string.h> 53259701Sdim#include <time.h> 54259701Sdim#include <unistd.h> 55259701Sdim 56259701Sdim#include "truss.h" 57259701Sdim#include "extern.h" 58259701Sdim 59259701Sdim/* 60259701Sdim * It's difficult to parameterize this because it must be 61259701Sdim * accessible in a signal handler. 62259701Sdim */ 63259701Sdim 64259701Sdimint Procfd; 65259701Sdim 66259701Sdimstatic __inline void 67259701Sdimusage(void) 68259701Sdim{ 69259701Sdim fprintf(stderr, "%s\n%s\n", 70259701Sdim "usage: truss [-faedDS] [-o file] -p pid", 71259701Sdim " truss [-faedDS] [-o file] command [args]"); 72259701Sdim exit(1); 73259701Sdim} 74259701Sdim 75259701Sdim/* 76259701Sdim * WARNING! "FreeBSD a.out" must be first, or set_etype will not 77259701Sdim * work correctly. 78259701Sdim */ 79259701Sdimstruct ex_types { 80259701Sdim const char *type; 81259701Sdim void (*enter_syscall)(struct trussinfo *, int); 82259701Sdim int (*exit_syscall)(struct trussinfo *, int); 83259701Sdim} ex_types[] = { 84259701Sdim#ifdef __alpha__ 85259701Sdim { "FreeBSD ELF", alpha_syscall_entry, alpha_syscall_exit }, 86259701Sdim#endif 87259701Sdim#ifdef __i386__ 88259701Sdim { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 89259701Sdim { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 90259701Sdim { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, 91259701Sdim { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 92259701Sdim#endif 93259701Sdim#ifdef __ia64__ 94259701Sdim { "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit }, 95259701Sdim#endif 96259701Sdim#ifdef __sparc64__ 97259701Sdim { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, 98259701Sdim#endif 99259701Sdim { 0, 0, 0 }, 100259701Sdim}; 101259701Sdim 102259701Sdim/* 103259701Sdim * Set the execution type. This is called after every exec, and when 104259701Sdim * a process is first monitored. The procfs pseudo-file "etype" has 105259701Sdim * the execution module type -- see /proc/curproc/etype for an example. 106259701Sdim */ 107259701Sdim 108259701Sdimstatic struct ex_types * 109259701Sdimset_etype(struct trussinfo *trussinfo) { 110259701Sdim struct ex_types *funcs; 111259701Sdim char etype[24]; 112259701Sdim char progt[32]; 113259701Sdim int fd; 114259701Sdim 115259701Sdim sprintf(etype, "/proc/%d/etype", trussinfo->pid); 116259701Sdim if ((fd = open(etype, O_RDONLY)) == -1) { 117259701Sdim strcpy(progt, "FreeBSD a.out"); 118259701Sdim } else { 119259701Sdim int len = read(fd, progt, sizeof(progt)); 120259701Sdim progt[len-1] = '\0'; 121259701Sdim close(fd); 122259701Sdim } 123259701Sdim 124259701Sdim for (funcs = ex_types; funcs->type; funcs++) 125259701Sdim if (!strcmp(funcs->type, progt)) 126259701Sdim break; 127259701Sdim 128259701Sdim if (funcs->type == NULL) { 129259701Sdim funcs = &ex_types[0]; 130259701Sdim warn("execution type %s is not supported -- using %s", 131259701Sdim progt, funcs->type); 132259701Sdim } 133259701Sdim return funcs; 134259701Sdim} 135259701Sdim 136259701Sdimint 137259701Sdimmain(int ac, char **av) { 138259701Sdim int c; 139259701Sdim int i; 140259701Sdim char **command; 141259701Sdim struct procfs_status pfs; 142259701Sdim struct ex_types *funcs; 143259701Sdim int in_exec = 0; 144259701Sdim char *fname = NULL; 145259701Sdim int sigexit = 0; 146259701Sdim struct trussinfo *trussinfo; 147259701Sdim 148259701Sdim /* Initialize the trussinfo struct */ 149259701Sdim trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); 150259701Sdim if (trussinfo == NULL) 151259701Sdim errx(1, "malloc() failed"); 152259701Sdim bzero(trussinfo, sizeof(struct trussinfo)); 153259701Sdim trussinfo->outfile = stderr; 154259701Sdim 155259701Sdim while ((c = getopt(ac, av, "p:o:faedDS")) != -1) { 156259701Sdim switch (c) { 157259701Sdim case 'p': /* specified pid */ 158259701Sdim trussinfo->pid = atoi(optarg); 159259701Sdim break; 160259701Sdim case 'f': /* Follow fork()'s */ 161259701Sdim trussinfo->flags |= FOLLOWFORKS; 162259701Sdim break; 163259701Sdim case 'a': /* Print execve() argument strings. */ 164259701Sdim trussinfo->flags |= EXECVEARGS; 165259701Sdim break; 166259701Sdim case 'e': /* Print execve() environment strings. */ 167259701Sdim trussinfo->flags |= EXECVEENVS; 168259701Sdim break; 169259701Sdim case 'd': /* Absolute timestamps */ 170259701Sdim trussinfo->flags |= ABSOLUTETIMESTAMPS; 171259701Sdim break; 172259701Sdim case 'D': /* Relative timestamps */ 173259701Sdim trussinfo->flags |= RELATIVETIMESTAMPS; 174259701Sdim break; 175259701Sdim case 'o': /* Specified output file */ 176259701Sdim fname = optarg; 177259701Sdim break; 178259701Sdim case 'S': /* Don't trace signals */ 179259701Sdim trussinfo->flags |= NOSIGS; 180259701Sdim break; 181259701Sdim default: 182259701Sdim usage(); 183259701Sdim } 184259701Sdim } 185259701Sdim 186259701Sdim ac -= optind; av += optind; 187259701Sdim if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0)) 188259701Sdim usage(); 189259701Sdim 190259701Sdim if (fname != NULL) { /* Use output file */ 191259701Sdim if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 192259701Sdim errx(1, "cannot open %s", fname); 193259701Sdim } 194259701Sdim 195259701Sdim /* 196259701Sdim * If truss starts the process itself, it will ignore some signals -- 197259701Sdim * they should be passed off to the process, which may or may not 198259701Sdim * exit. If, however, we are examining an already-running process, 199259701Sdim * then we restore the event mask on these same signals. 200259701Sdim */ 201259701Sdim 202259701Sdim if (trussinfo->pid == 0) { /* Start a command ourselves */ 203259701Sdim command = av; 204259701Sdim trussinfo->pid = setup_and_wait(command); 205259701Sdim signal(SIGINT, SIG_IGN); 206259701Sdim signal(SIGTERM, SIG_IGN); 207259701Sdim signal(SIGQUIT, SIG_IGN); 208259701Sdim } else { 209259701Sdim signal(SIGINT, restore_proc); 210259701Sdim signal(SIGTERM, restore_proc); 211259701Sdim signal(SIGQUIT, restore_proc); 212259701Sdim } 213259701Sdim 214259701Sdim 215259701Sdim /* 216259701Sdim * At this point, if we started the process, it is stopped waiting to 217259701Sdim * be woken up, either in exit() or in execve(). 218259701Sdim */ 219259701Sdim 220259701SdimSTART_TRACE: 221 Procfd = start_tracing( 222 trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 223 ((trussinfo->flags & NOSIGS) ? 0 : S_SIG), 224 ((trussinfo->flags & FOLLOWFORKS) ? PF_FORK : 0)); 225 if (Procfd == -1) 226 return 0; 227 228 pfs.why = 0; 229 230 funcs = set_etype(trussinfo); 231 /* 232 * At this point, it's a simple loop, waiting for the process to 233 * stop, finding out why, printing out why, and then continuing it. 234 * All of the grunt work is done in the support routines. 235 */ 236 237 clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); 238 239 do { 240 int val = 0; 241 242 if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 243 warn("PIOCWAIT top of loop"); 244 else { 245 switch(i = pfs.why) { 246 case S_SCE: 247 funcs->enter_syscall(trussinfo, pfs.val); 248 clock_gettime(CLOCK_REALTIME, &trussinfo->before); 249 break; 250 case S_SCX: 251 clock_gettime(CLOCK_REALTIME, &trussinfo->after); 252 /* 253 * This is so we don't get two messages for an exec -- one 254 * for the S_EXEC, and one for the syscall exit. It also, 255 * conveniently, ensures that the first message printed out 256 * isn't the return-from-syscall used to create the process. 257 */ 258 259 if (in_exec) { 260 in_exec = 0; 261 break; 262 } 263 264 if (trussinfo->in_fork && (trussinfo->flags & FOLLOWFORKS)) { 265 int childpid; 266 267 trussinfo->in_fork = 0; 268 childpid = funcs->exit_syscall(trussinfo, pfs.val); 269 270 /* 271 * Fork a new copy of ourself to trace the child of the 272 * original traced process. 273 */ 274 if (fork() == 0) { 275 trussinfo->pid = childpid; 276 goto START_TRACE; 277 } 278 break; 279 } 280 funcs->exit_syscall(trussinfo, pfs.val); 281 break; 282 case S_SIG: 283 fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val); 284 sigexit = pfs.val; 285 break; 286 case S_EXIT: 287 fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val); 288 break; 289 case S_EXEC: 290 funcs = set_etype(trussinfo); 291 in_exec = 1; 292 break; 293 default: 294 fprintf (trussinfo->outfile, "Process stopped because of: %d\n", i); 295 break; 296 } 297 } 298 if (ioctl(Procfd, PIOCCONT, val) == -1) { 299 if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH) 300 break; 301 else 302 warn("PIOCCONT"); 303 } 304 } while (pfs.why != S_EXIT); 305 fflush(trussinfo->outfile); 306 if (sigexit) { 307 if (sigexit == SIGQUIT) 308 exit(sigexit); 309 (void) signal(sigexit, SIG_DFL); 310 (void) kill(getpid(), sigexit); 311 } 312 return 0; 313} 314