main.c revision 191005
169783Smsmith/*- 2223520Sjhb * Copryight 1997 Sean Eric Fagan 3223520Sjhb * 469783Smsmith * Redistribution and use in source and binary forms, with or without 569783Smsmith * modification, are permitted provided that the following conditions 669783Smsmith * are met: 769783Smsmith * 1. Redistributions of source code must retain the above copyright 869783Smsmith * notice, this list of conditions and the following disclaimer. 969783Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1069783Smsmith * notice, this list of conditions and the following disclaimer in the 1169783Smsmith * documentation and/or other materials provided with the distribution. 1269783Smsmith * 3. All advertising materials mentioning features or use of this software 1369783Smsmith * must display the following acknowledgement: 1469783Smsmith * This product includes software developed by Sean Eric Fagan 1569783Smsmith * 4. Neither the name of the author may be used to endorse or promote 1669783Smsmith * products derived from this software without specific prior written 1769783Smsmith * permission. 1869783Smsmith * 1969783Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2069783Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2169783Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2269783Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2369783Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2469783Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2569783Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2669783Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2769783Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28119418Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29119418Sobrien * SUCH DAMAGE. 30119418Sobrien */ 3169783Smsmith 32223520Sjhb#include <sys/cdefs.h> 33223520Sjhb__FBSDID("$FreeBSD: head/usr.bin/truss/main.c 191005 2009-04-13 16:23:32Z delphij $"); 3469783Smsmith 3569783Smsmith/* 36224069Sjhb * The main module for truss. Suprisingly simple, but, then, the other 37221393Sjhb * files handle the bulk of the work. And, of course, the kernel has to 38107546Simp * do a lot of the work :). 39224069Sjhb */ 4069783Smsmith 41223520Sjhb#include <sys/param.h> 42119285Simp#include <sys/types.h> 43119285Simp#include <sys/time.h> 4469783Smsmith#include <sys/resource.h> 4569783Smsmith#include <sys/sysctl.h> 46107172Sjhb#include <sys/wait.h> 47107172Sjhb 48107172Sjhb#include <ctype.h> 49107172Sjhb#include <err.h> 50107172Sjhb#include <errno.h> 51119266Simp#include <fcntl.h> 52107172Sjhb#include <signal.h> 53119266Simp#include <stdio.h> 54107172Sjhb#include <stdlib.h> 55107172Sjhb#include <string.h> 56107248Sjhb#include <time.h> 57107172Sjhb#include <unistd.h> 58107172Sjhb 59107172Sjhb#include "truss.h" 60107172Sjhb#include "extern.h" 61107172Sjhb 62107172Sjhb#define MAXARGS 6 63107172Sjhb 64107172Sjhbstatic void 65107172Sjhbusage(void) 66107172Sjhb{ 67107172Sjhb fprintf(stderr, "%s\n%s\n", 68107172Sjhb "usage: truss [-faedDS] [-o file] [-s strsize] -p pid", 69107172Sjhb " truss [-faedDS] [-o file] [-s strsize] command [args]"); 70107172Sjhb exit(1); 71107172Sjhb} 72107172Sjhb 73107172Sjhb/* 74107172Sjhb * WARNING! "FreeBSD a.out" must be first, or set_etype will not 75107172Sjhb * work correctly. 76107172Sjhb */ 77107172Sjhbstruct ex_types { 78107172Sjhb const char *type; 79107172Sjhb void (*enter_syscall)(struct trussinfo *, int); 80107172Sjhb long (*exit_syscall)(struct trussinfo *, int); 81107172Sjhb} ex_types[] = { 82107172Sjhb#ifdef __amd64__ 83107172Sjhb { "FreeBSD ELF64", amd64_syscall_entry, amd64_syscall_exit }, 84107172Sjhb { "FreeBSD ELF32", amd64_fbsd32_syscall_entry, amd64_fbsd32_syscall_exit }, 85107172Sjhb { "Linux ELF32", amd64_linux32_syscall_entry, amd64_linux32_syscall_exit }, 86107172Sjhb#endif 87107248Sjhb#ifdef __i386__ 88107172Sjhb { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 89107172Sjhb { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 90107172Sjhb { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, 91107248Sjhb { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 92107172Sjhb#endif 93107172Sjhb#ifdef __ia64__ 94107172Sjhb { "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit }, 95107248Sjhb#endif 96107172Sjhb#ifdef __powerpc__ 97107172Sjhb { "FreeBSD ELF", powerpc_syscall_entry, powerpc_syscall_exit }, 98107172Sjhb { "FreeBSD ELF32", powerpc_syscall_entry, powerpc_syscall_exit }, 99107248Sjhb#endif 100107172Sjhb#ifdef __sparc64__ 101107172Sjhb { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, 102107172Sjhb#endif 103107172Sjhb#ifdef __mips__ 104107172Sjhb { "FreeBSD ELF", mips_syscall_entry, mips_syscall_exit }, 105107172Sjhb { "FreeBSD ELF32", mips_syscall_entry, mips_syscall_exit }, 106107172Sjhb { "FreeBSD ELF64", mips_syscall_entry, mips_syscall_exit }, // XXX 107107172Sjhb#endif 108107172Sjhb { 0, 0, 0 }, 109107172Sjhb}; 110107172Sjhb 111107172Sjhb/* 112107172Sjhb * Set the execution type. This is called after every exec, and when 113107172Sjhb * a process is first monitored. 114215820Sjhb */ 115107172Sjhb 116215820Sjhbstatic struct ex_types * 117107172Sjhbset_etype(struct trussinfo *trussinfo) 118107172Sjhb{ 119107172Sjhb struct ex_types *funcs; 120144110Sjhb char progt[32]; 121144110Sjhb 122144110Sjhb size_t len = sizeof(progt); 123144110Sjhb int mib[4]; 124144110Sjhb int error; 125107172Sjhb 126107172Sjhb mib[0] = CTL_KERN; 127107172Sjhb mib[1] = KERN_PROC; 128107172Sjhb mib[2] = KERN_PROC_SV_NAME; 129107172Sjhb mib[3] = trussinfo->pid; 130107172Sjhb error = sysctl(mib, 4, progt, &len, NULL, 0); 131107172Sjhb if (error != 0) 132224069Sjhb err(2, "can not get etype"); 133224069Sjhb 134224069Sjhb for (funcs = ex_types; funcs->type; funcs++) 135224069Sjhb if (!strcmp(funcs->type, progt)) 136224069Sjhb break; 137224069Sjhb 138224069Sjhb if (funcs->type == NULL) { 139224069Sjhb funcs = &ex_types[0]; 140224069Sjhb warn("execution type %s is not supported -- using %s", 141224069Sjhb progt, funcs->type); 142224069Sjhb } 143224069Sjhb return (funcs); 144224069Sjhb} 145224069Sjhb 146224069Sjhbchar * 147224069Sjhbstrsig(int sig) 148224069Sjhb{ 149224069Sjhb char *ret; 150224069Sjhb 151224069Sjhb ret = NULL; 152224069Sjhb if (sig > 0 && sig < NSIG) { 153224069Sjhb int i; 154261523Sjhb asprintf(&ret, "sig%s", sys_signame[sig]); 155224069Sjhb if (ret == NULL) 156224069Sjhb return (NULL); 157224069Sjhb for (i = 0; ret[i] != '\0'; ++i) 158224069Sjhb ret[i] = toupper(ret[i]); 159224069Sjhb } 160224069Sjhb return (ret); 161224069Sjhb} 162224069Sjhb 163224069Sjhbint 164224069Sjhbmain(int ac, char **av) 165224069Sjhb{ 166224069Sjhb int c; 167224069Sjhb int i; 168224069Sjhb pid_t childpid; 169224069Sjhb int status; 170224069Sjhb char **command; 171224069Sjhb struct ex_types *funcs; 172224069Sjhb int initial_open; 173224069Sjhb char *fname; 174224069Sjhb struct trussinfo *trussinfo; 175224069Sjhb char *signame; 176224069Sjhb 177224069Sjhb fname = NULL; 178224069Sjhb initial_open = 1; 179224069Sjhb 180224069Sjhb /* Initialize the trussinfo struct */ 181224069Sjhb trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); 182224069Sjhb if (trussinfo == NULL) 183224069Sjhb errx(1, "malloc() failed"); 184224069Sjhb bzero(trussinfo, sizeof(struct trussinfo)); 185224069Sjhb 186224069Sjhb trussinfo->outfile = stderr; 187224069Sjhb trussinfo->strsize = 32; 188224069Sjhb trussinfo->pr_why = S_NONE; 189224069Sjhb trussinfo->curthread = NULL; 190224069Sjhb SLIST_INIT(&trussinfo->threadlist); 191224069Sjhb while ((c = getopt(ac, av, "p:o:faedDs:S")) != -1) { 192224069Sjhb switch (c) { 193224069Sjhb case 'p': /* specified pid */ 194224069Sjhb trussinfo->pid = atoi(optarg); 195224069Sjhb /* make sure i don't trace me */ 196224069Sjhb if(trussinfo->pid == getpid()) { 197224069Sjhb fprintf(stderr, "attempt to grab self.\n"); 198224069Sjhb exit(2); 199224069Sjhb } 200224069Sjhb break; 201224069Sjhb case 'f': /* Follow fork()'s */ 202224069Sjhb trussinfo->flags |= FOLLOWFORKS; 203224069Sjhb break; 204224069Sjhb case 'a': /* Print execve() argument strings. */ 205224069Sjhb trussinfo->flags |= EXECVEARGS; 206224069Sjhb break; 207224069Sjhb case 'e': /* Print execve() environment strings. */ 208224069Sjhb trussinfo->flags |= EXECVEENVS; 209224069Sjhb break; 210224069Sjhb case 'd': /* Absolute timestamps */ 211224069Sjhb trussinfo->flags |= ABSOLUTETIMESTAMPS; 212224069Sjhb break; 213224069Sjhb case 'D': /* Relative timestamps */ 214224069Sjhb trussinfo->flags |= RELATIVETIMESTAMPS; 215224069Sjhb break; 216224069Sjhb case 'o': /* Specified output file */ 217224069Sjhb fname = optarg; 218224069Sjhb break; 219224069Sjhb case 's': /* Specified string size */ 220224069Sjhb trussinfo->strsize = atoi(optarg); 221224069Sjhb break; 222224069Sjhb case 'S': /* Don't trace signals */ 223224069Sjhb trussinfo->flags |= NOSIGS; 224224069Sjhb break; 225224069Sjhb default: 226224069Sjhb usage(); 227224069Sjhb } 228224069Sjhb } 229224069Sjhb 230224069Sjhb ac -= optind; av += optind; 231224069Sjhb if ((trussinfo->pid == 0 && ac == 0) || 232224069Sjhb (trussinfo->pid != 0 && ac != 0)) 233224069Sjhb usage(); 234224069Sjhb 235224069Sjhb if (fname != NULL) { /* Use output file */ 236224069Sjhb if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 237224069Sjhb errx(1, "cannot open %s", fname); 238224069Sjhb } 239224069Sjhb 240224069Sjhb /* 241224069Sjhb * If truss starts the process itself, it will ignore some signals -- 242224069Sjhb * they should be passed off to the process, which may or may not 243224069Sjhb * exit. If, however, we are examining an already-running process, 244224069Sjhb * then we restore the event mask on these same signals. 245224069Sjhb */ 246224069Sjhb 247224069Sjhb if (trussinfo->pid == 0) { /* Start a command ourselves */ 248224069Sjhb command = av; 249224069Sjhb trussinfo->pid = setup_and_wait(command); 250224069Sjhb signal(SIGINT, SIG_IGN); 251224069Sjhb signal(SIGTERM, SIG_IGN); 252224069Sjhb signal(SIGQUIT, SIG_IGN); 253224069Sjhb } else { 254224069Sjhb start_tracing(trussinfo->pid); 255224069Sjhb signal(SIGINT, restore_proc); 256224069Sjhb signal(SIGTERM, restore_proc); 257224069Sjhb signal(SIGQUIT, restore_proc); 258224069Sjhb } 259224069Sjhb 260224069Sjhb 261224069Sjhb /* 262224069Sjhb * At this point, if we started the process, it is stopped waiting to 263224069Sjhb * be woken up, either in exit() or in execve(). 264224069Sjhb */ 265224069Sjhb 266224069SjhbSTART_TRACE: 267224069Sjhb funcs = set_etype(trussinfo); 268224069Sjhb 269224069Sjhb initial_open = 0; 270224069Sjhb /* 271224069Sjhb * At this point, it's a simple loop, waiting for the process to 272224069Sjhb * stop, finding out why, printing out why, and then continuing it. 273224069Sjhb * All of the grunt work is done in the support routines. 274224069Sjhb */ 275224069Sjhb 276224069Sjhb clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); 277224069Sjhb 278224069Sjhb do { 279224069Sjhb struct timespec timediff; 280224069Sjhb waitevent(trussinfo); 281224069Sjhb 282224069Sjhb switch(i = trussinfo->pr_why) { 283224069Sjhb case S_SCE: 284224069Sjhb funcs->enter_syscall(trussinfo, MAXARGS); 285224069Sjhb clock_gettime(CLOCK_REALTIME, 286 &trussinfo->before); 287 break; 288 case S_SCX: 289 clock_gettime(CLOCK_REALTIME, 290 &trussinfo->after); 291 292 if (trussinfo->curthread->in_fork && 293 (trussinfo->flags & FOLLOWFORKS)) { 294 trussinfo->curthread->in_fork = 0; 295 childpid = 296 funcs->exit_syscall(trussinfo, 297 trussinfo->pr_data); 298 299 /* 300 * Fork a new copy of ourself to trace 301 * the child of the original traced 302 * process. 303 */ 304 if (fork() == 0) { 305 trussinfo->pid = childpid; 306 start_tracing(trussinfo->pid); 307 goto START_TRACE; 308 } 309 break; 310 } 311 funcs->exit_syscall(trussinfo, MAXARGS); 312 break; 313 case S_SIG: 314 if (trussinfo->flags & NOSIGS) 315 break; 316 if (trussinfo->flags & FOLLOWFORKS) 317 fprintf(trussinfo->outfile, "%5d: ", 318 trussinfo->pid); 319 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 320 timespecsubt(&trussinfo->after, 321 &trussinfo->start_time, &timediff); 322 fprintf(trussinfo->outfile, "%ld.%09ld ", 323 (long)timediff.tv_sec, 324 timediff.tv_nsec); 325 } 326 if (trussinfo->flags & RELATIVETIMESTAMPS) { 327 timespecsubt(&trussinfo->after, 328 &trussinfo->before, &timediff); 329 fprintf(trussinfo->outfile, "%ld.%09ld ", 330 (long)timediff.tv_sec, 331 timediff.tv_nsec); 332 } 333 signame = strsig(trussinfo->pr_data); 334 fprintf(trussinfo->outfile, 335 "SIGNAL %u (%s)\n", trussinfo->pr_data, 336 signame == NULL ? "?" : signame); 337 free(signame); 338 break; 339 case S_EXIT: 340 if (trussinfo->flags & FOLLOWFORKS) 341 fprintf(trussinfo->outfile, "%5d: ", 342 trussinfo->pid); 343 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 344 timespecsubt(&trussinfo->after, 345 &trussinfo->start_time, &timediff); 346 fprintf(trussinfo->outfile, "%ld.%09ld ", 347 (long)timediff.tv_sec, 348 timediff.tv_nsec); 349 } 350 if (trussinfo->flags & RELATIVETIMESTAMPS) { 351 timespecsubt(&trussinfo->after, 352 &trussinfo->before, &timediff); 353 fprintf(trussinfo->outfile, "%ld.%09ld ", 354 (long)timediff.tv_sec, timediff.tv_nsec); 355 } 356 fprintf(trussinfo->outfile, 357 "process exit, rval = %u\n", trussinfo->pr_data); 358 break; 359 default: 360 break; 361 } 362 } while (trussinfo->pr_why != S_EXIT); 363 fflush(trussinfo->outfile); 364 365 if (trussinfo->flags & FOLLOWFORKS) 366 do { 367 childpid = wait(&status); 368 } while (childpid != -1); 369 370 return (0); 371} 372