main.c revision 241162
1132718Skan/*- 290075Sobrien * Copyright 1997 Sean Eric Fagan 3169689Skan * 4169689Skan * Redistribution and use in source and binary forms, with or without 590075Sobrien * modification, are permitted provided that the following conditions 6132718Skan * are met: 790075Sobrien * 1. Redistributions of source code must retain the above copyright 8132718Skan * notice, this list of conditions and the following disclaimer. 990075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1090075Sobrien * notice, this list of conditions and the following disclaimer in the 1190075Sobrien * documentation and/or other materials provided with the distribution. 1290075Sobrien * 3. All advertising materials mentioning features or use of this software 13132718Skan * must display the following acknowledgement: 1490075Sobrien * This product includes software developed by Sean Eric Fagan 1590075Sobrien * 4. Neither the name of the author may be used to endorse or promote 1690075Sobrien * products derived from this software without specific prior written 1790075Sobrien * permission. 1890075Sobrien * 19132718Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2290075Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2390075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24132718Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25132718Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2690075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27132718Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28132718Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29132718Skan * SUCH DAMAGE. 3090075Sobrien */ 31132718Skan 32132718Skan#include <sys/cdefs.h> 33132718Skan__FBSDID("$FreeBSD: stable/9/usr.bin/truss/main.c 241162 2012-10-03 14:28:55Z zont $"); 34132718Skan 3590075Sobrien/* 36132718Skan * The main module for truss. Surprisingly simple, but, then, the other 37132718Skan * files handle the bulk of the work. And, of course, the kernel has to 3890075Sobrien * do a lot of the work :). 3990075Sobrien */ 40132718Skan 4190075Sobrien#include <sys/param.h> 42132718Skan#include <sys/types.h> 43132718Skan#include <sys/time.h> 44132718Skan#include <sys/resource.h> 45132718Skan#include <sys/sysctl.h> 46169689Skan#include <sys/wait.h> 47132718Skan 48132718Skan#include <err.h> 49132718Skan#include <errno.h> 50132718Skan#include <fcntl.h> 51132718Skan#include <signal.h> 5290075Sobrien#include <stdio.h> 53169689Skan#include <stdlib.h> 54169689Skan#include <string.h> 55132718Skan#include <time.h> 5690075Sobrien#include <unistd.h> 57132718Skan 58132718Skan#include "truss.h" 59132718Skan#include "extern.h" 60132718Skan#include "syscall.h" 61132718Skan 62132718Skan#define MAXARGS 6 6390075Sobrien 64132718Skanstatic void 65132718Skanusage(void) 66132718Skan{ 67132718Skan fprintf(stderr, "%s\n%s\n", 6890075Sobrien "usage: truss [-cfaedDS] [-o file] [-s strsize] -p pid", 69132718Skan " truss [-cfaedDS] [-o file] [-s strsize] command [args]"); 70132718Skan exit(1); 7190075Sobrien} 72169689Skan 73132718Skan/* 74169689Skan * WARNING! "FreeBSD a.out" must be first, or set_etype will not 75169689Skan * work correctly. 76132718Skan */ 7790075Sobrienstruct ex_types { 78132718Skan const char *type; 79132718Skan void (*enter_syscall)(struct trussinfo *, int); 80132718Skan long (*exit_syscall)(struct trussinfo *, int); 81169689Skan} ex_types[] = { 82169689Skan#ifdef __amd64__ 83169689Skan { "FreeBSD ELF64", amd64_syscall_entry, amd64_syscall_exit }, 84169689Skan { "FreeBSD ELF32", amd64_fbsd32_syscall_entry, amd64_fbsd32_syscall_exit }, 85169689Skan { "Linux ELF32", amd64_linux32_syscall_entry, amd64_linux32_syscall_exit }, 86169689Skan#endif 87132718Skan#ifdef __i386__ 88132718Skan { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 89132718Skan { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 90132718Skan { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, 91132718Skan { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 92169689Skan#endif 93169689Skan#ifdef __ia64__ 94169689Skan { "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit }, 95169689Skan#endif 96169689Skan#ifdef __powerpc__ 97169689Skan { "FreeBSD ELF", powerpc_syscall_entry, powerpc_syscall_exit }, 98132718Skan { "FreeBSD ELF32", powerpc_syscall_entry, powerpc_syscall_exit }, 99132718Skan#ifdef __powerpc64__ 100132718Skan { "FreeBSD ELF64", powerpc64_syscall_entry, powerpc64_syscall_exit }, 101132718Skan#endif 102132718Skan#endif 103132718Skan#ifdef __sparc64__ 104132718Skan { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, 105132718Skan#endif 106169689Skan#ifdef __mips__ 107169689Skan { "FreeBSD ELF", mips_syscall_entry, mips_syscall_exit }, 108132718Skan { "FreeBSD ELF32", mips_syscall_entry, mips_syscall_exit }, 109132718Skan { "FreeBSD ELF64", mips_syscall_entry, mips_syscall_exit }, // XXX 110132718Skan#endif 111132718Skan { 0, 0, 0 }, 112132718Skan}; 113169689Skan 114169689Skan/* 115169689Skan * Set the execution type. This is called after every exec, and when 116132718Skan * a process is first monitored. 117132718Skan */ 11890075Sobrien 119169689Skanstatic struct ex_types * 120132718Skanset_etype(struct trussinfo *trussinfo) 121132718Skan{ 12290075Sobrien struct ex_types *funcs; 123132718Skan size_t len; 12490075Sobrien int error; 125132718Skan int mib[4]; 126132718Skan char progt[32]; 127132718Skan 128132718Skan len = sizeof(progt); 129132718Skan mib[0] = CTL_KERN; 130132718Skan mib[1] = KERN_PROC; 131132718Skan mib[2] = KERN_PROC_SV_NAME; 13290075Sobrien mib[3] = trussinfo->pid; 133132718Skan error = sysctl(mib, 4, progt, &len, NULL, 0); 134132718Skan if (error != 0) 135169689Skan err(2, "can not get etype"); 136169689Skan 137169689Skan for (funcs = ex_types; funcs->type; funcs++) 138169689Skan if (strcmp(funcs->type, progt) == 0) 139132718Skan break; 140132718Skan 141132718Skan if (funcs->type == NULL) { 142132718Skan funcs = &ex_types[0]; 143169689Skan warn("execution type %s is not supported -- using %s", 144169689Skan progt, funcs->type); 145132718Skan } 146169689Skan return (funcs); 147132718Skan} 148169689Skan 149169689Skanchar * 150132718Skanstrsig(int sig) 151132718Skan{ 152132718Skan char *ret; 153132718Skan 154169689Skan ret = NULL; 15590075Sobrien if (sig > 0 && sig < NSIG) { 156169689Skan asprintf(&ret, "SIG%s", sys_signame[sig]); 15790075Sobrien if (ret == NULL) 158169689Skan return (NULL); 159169689Skan } 16090075Sobrien return (ret); 161132718Skan} 162132718Skan 163132718Skanint 164132718Skanmain(int ac, char **av) 165169689Skan{ 16690075Sobrien struct timespec timediff; 16790075Sobrien struct sigaction sa; 16890075Sobrien struct ex_types *funcs; 16990075Sobrien struct trussinfo *trussinfo; 17090075Sobrien char *fname; 171132718Skan char *signame; 17290075Sobrien char **command; 173132718Skan pid_t childpid; 174132718Skan int c, initial_open, status; 175132718Skan 176132718Skan fname = NULL; 177169689Skan initial_open = 1; 17890075Sobrien 179132718Skan /* Initialize the trussinfo struct */ 180169689Skan trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo)); 181132718Skan if (trussinfo == NULL) 18290075Sobrien errx(1, "calloc() failed"); 183117395Skan 184132718Skan trussinfo->outfile = stderr; 185169689Skan trussinfo->strsize = 32; 186117395Skan trussinfo->pr_why = S_NONE; 187169689Skan trussinfo->curthread = NULL; 188169689Skan SLIST_INIT(&trussinfo->threadlist); 189169689Skan while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) { 190169689Skan switch (c) { 191169689Skan case 'p': /* specified pid */ 192117395Skan trussinfo->pid = atoi(optarg); 193132718Skan /* make sure i don't trace me */ 194132718Skan if (trussinfo->pid == getpid()) { 195132718Skan fprintf(stderr, "attempt to grab self.\n"); 196132718Skan exit(2); 197169689Skan } 198132718Skan break; 199132718Skan case 'f': /* Follow fork()'s */ 200132718Skan trussinfo->flags |= FOLLOWFORKS; 201132718Skan break; 202132718Skan case 'a': /* Print execve() argument strings. */ 203132718Skan trussinfo->flags |= EXECVEARGS; 204169689Skan break; 205169689Skan case 'c': /* Count number of system calls and time. */ 206169689Skan trussinfo->flags |= COUNTONLY; 207169689Skan break; 208169689Skan case 'e': /* Print execve() environment strings. */ 209169689Skan trussinfo->flags |= EXECVEENVS; 210169689Skan break; 211169689Skan case 'd': /* Absolute timestamps */ 212169689Skan trussinfo->flags |= ABSOLUTETIMESTAMPS; 213169689Skan break; 214169689Skan case 'D': /* Relative timestamps */ 215169689Skan trussinfo->flags |= RELATIVETIMESTAMPS; 216169689Skan break; 217169689Skan case 'o': /* Specified output file */ 218169689Skan fname = optarg; 219169689Skan break; 220169689Skan case 's': /* Specified string size */ 221169689Skan trussinfo->strsize = atoi(optarg); 222169689Skan break; 223169689Skan case 'S': /* Don't trace signals */ 224169689Skan trussinfo->flags |= NOSIGS; 225169689Skan break; 226169689Skan default: 227169689Skan usage(); 228169689Skan } 229169689Skan } 230169689Skan 231169689Skan ac -= optind; av += optind; 232169689Skan if ((trussinfo->pid == 0 && ac == 0) || 233169689Skan (trussinfo->pid != 0 && ac != 0)) 234169689Skan usage(); 235169689Skan 236169689Skan if (fname != NULL) { /* Use output file */ 237 if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 238 errx(1, "cannot open %s", fname); 239 /* 240 * Set FD_CLOEXEC, so that the output file is not shared with 241 * the traced process. 242 */ 243 if (fcntl(fileno(trussinfo->outfile), F_SETFD, FD_CLOEXEC) == 244 -1) 245 warn("fcntl()"); 246 } 247 248 /* 249 * If truss starts the process itself, it will ignore some signals -- 250 * they should be passed off to the process, which may or may not 251 * exit. If, however, we are examining an already-running process, 252 * then we restore the event mask on these same signals. 253 */ 254 255 if (trussinfo->pid == 0) { /* Start a command ourselves */ 256 command = av; 257 trussinfo->pid = setup_and_wait(command); 258 signal(SIGINT, SIG_IGN); 259 signal(SIGTERM, SIG_IGN); 260 signal(SIGQUIT, SIG_IGN); 261 } else { 262 sa.sa_handler = restore_proc; 263 sa.sa_flags = 0; 264 sigemptyset(&sa.sa_mask); 265 sigaction(SIGINT, &sa, NULL); 266 sigaction(SIGQUIT, &sa, NULL); 267 sigaction(SIGTERM, &sa, NULL); 268 start_tracing(trussinfo->pid); 269 } 270 271 272 /* 273 * At this point, if we started the process, it is stopped waiting to 274 * be woken up, either in exit() or in execve(). 275 */ 276 277START_TRACE: 278 funcs = set_etype(trussinfo); 279 280 initial_open = 0; 281 /* 282 * At this point, it's a simple loop, waiting for the process to 283 * stop, finding out why, printing out why, and then continuing it. 284 * All of the grunt work is done in the support routines. 285 */ 286 287 clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); 288 289 do { 290 waitevent(trussinfo); 291 292 switch (trussinfo->pr_why) { 293 case S_SCE: 294 funcs->enter_syscall(trussinfo, MAXARGS); 295 clock_gettime(CLOCK_REALTIME, 296 &trussinfo->curthread->before); 297 break; 298 case S_SCX: 299 clock_gettime(CLOCK_REALTIME, 300 &trussinfo->curthread->after); 301 302 if (trussinfo->curthread->in_fork && 303 (trussinfo->flags & FOLLOWFORKS)) { 304 trussinfo->curthread->in_fork = 0; 305 childpid = funcs->exit_syscall(trussinfo, 306 trussinfo->pr_data); 307 308 /* 309 * Fork a new copy of ourself to trace 310 * the child of the original traced 311 * process. 312 */ 313 if (fork() == 0) { 314 trussinfo->pid = childpid; 315 start_tracing(trussinfo->pid); 316 goto START_TRACE; 317 } 318 break; 319 } 320 funcs->exit_syscall(trussinfo, MAXARGS); 321 break; 322 case S_SIG: 323 if (trussinfo->flags & NOSIGS) 324 break; 325 if (trussinfo->flags & FOLLOWFORKS) 326 fprintf(trussinfo->outfile, "%5d: ", 327 trussinfo->pid); 328 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 329 timespecsubt(&trussinfo->curthread->after, 330 &trussinfo->start_time, &timediff); 331 fprintf(trussinfo->outfile, "%ld.%09ld ", 332 (long)timediff.tv_sec, 333 timediff.tv_nsec); 334 } 335 if (trussinfo->flags & RELATIVETIMESTAMPS) { 336 timespecsubt(&trussinfo->curthread->after, 337 &trussinfo->curthread->before, &timediff); 338 fprintf(trussinfo->outfile, "%ld.%09ld ", 339 (long)timediff.tv_sec, 340 timediff.tv_nsec); 341 } 342 signame = strsig(trussinfo->pr_data); 343 fprintf(trussinfo->outfile, 344 "SIGNAL %u (%s)\n", trussinfo->pr_data, 345 signame == NULL ? "?" : signame); 346 free(signame); 347 break; 348 case S_EXIT: 349 if (trussinfo->flags & COUNTONLY) 350 break; 351 if (trussinfo->flags & FOLLOWFORKS) 352 fprintf(trussinfo->outfile, "%5d: ", 353 trussinfo->pid); 354 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 355 timespecsubt(&trussinfo->curthread->after, 356 &trussinfo->start_time, &timediff); 357 fprintf(trussinfo->outfile, "%ld.%09ld ", 358 (long)timediff.tv_sec, 359 timediff.tv_nsec); 360 } 361 if (trussinfo->flags & RELATIVETIMESTAMPS) { 362 timespecsubt(&trussinfo->curthread->after, 363 &trussinfo->curthread->before, &timediff); 364 fprintf(trussinfo->outfile, "%ld.%09ld ", 365 (long)timediff.tv_sec, timediff.tv_nsec); 366 } 367 fprintf(trussinfo->outfile, 368 "process exit, rval = %u\n", trussinfo->pr_data); 369 break; 370 default: 371 break; 372 } 373 } while (trussinfo->pr_why != S_EXIT && 374 trussinfo->pr_why != S_DETACHED); 375 376 if (trussinfo->flags & FOLLOWFORKS) { 377 do { 378 childpid = wait(&status); 379 } while (childpid != -1); 380 } 381 382 if (trussinfo->flags & COUNTONLY) 383 print_summary(trussinfo); 384 385 fflush(trussinfo->outfile); 386 387 return (0); 388} 389