main.c revision 191005
1/*- 2 * Copryight 1997 Sean Eric Fagan 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by Sean Eric Fagan 15 * 4. Neither the name of the author may be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/usr.bin/truss/main.c 191005 2009-04-13 16:23:32Z delphij $"); 34 35/* 36 * The main module for truss. Suprisingly simple, but, then, the other 37 * files handle the bulk of the work. And, of course, the kernel has to 38 * do a lot of the work :). 39 */ 40 41#include <sys/param.h> 42#include <sys/types.h> 43#include <sys/time.h> 44#include <sys/resource.h> 45#include <sys/sysctl.h> 46#include <sys/wait.h> 47 48#include <ctype.h> 49#include <err.h> 50#include <errno.h> 51#include <fcntl.h> 52#include <signal.h> 53#include <stdio.h> 54#include <stdlib.h> 55#include <string.h> 56#include <time.h> 57#include <unistd.h> 58 59#include "truss.h" 60#include "extern.h" 61 62#define MAXARGS 6 63 64static void 65usage(void) 66{ 67 fprintf(stderr, "%s\n%s\n", 68 "usage: truss [-faedDS] [-o file] [-s strsize] -p pid", 69 " truss [-faedDS] [-o file] [-s strsize] command [args]"); 70 exit(1); 71} 72 73/* 74 * WARNING! "FreeBSD a.out" must be first, or set_etype will not 75 * work correctly. 76 */ 77struct ex_types { 78 const char *type; 79 void (*enter_syscall)(struct trussinfo *, int); 80 long (*exit_syscall)(struct trussinfo *, int); 81} ex_types[] = { 82#ifdef __amd64__ 83 { "FreeBSD ELF64", amd64_syscall_entry, amd64_syscall_exit }, 84 { "FreeBSD ELF32", amd64_fbsd32_syscall_entry, amd64_fbsd32_syscall_exit }, 85 { "Linux ELF32", amd64_linux32_syscall_entry, amd64_linux32_syscall_exit }, 86#endif 87#ifdef __i386__ 88 { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 89 { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 90 { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, 91 { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 92#endif 93#ifdef __ia64__ 94 { "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit }, 95#endif 96#ifdef __powerpc__ 97 { "FreeBSD ELF", powerpc_syscall_entry, powerpc_syscall_exit }, 98 { "FreeBSD ELF32", powerpc_syscall_entry, powerpc_syscall_exit }, 99#endif 100#ifdef __sparc64__ 101 { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, 102#endif 103#ifdef __mips__ 104 { "FreeBSD ELF", mips_syscall_entry, mips_syscall_exit }, 105 { "FreeBSD ELF32", mips_syscall_entry, mips_syscall_exit }, 106 { "FreeBSD ELF64", mips_syscall_entry, mips_syscall_exit }, // XXX 107#endif 108 { 0, 0, 0 }, 109}; 110 111/* 112 * Set the execution type. This is called after every exec, and when 113 * a process is first monitored. 114 */ 115 116static struct ex_types * 117set_etype(struct trussinfo *trussinfo) 118{ 119 struct ex_types *funcs; 120 char progt[32]; 121 122 size_t len = sizeof(progt); 123 int mib[4]; 124 int error; 125 126 mib[0] = CTL_KERN; 127 mib[1] = KERN_PROC; 128 mib[2] = KERN_PROC_SV_NAME; 129 mib[3] = trussinfo->pid; 130 error = sysctl(mib, 4, progt, &len, NULL, 0); 131 if (error != 0) 132 err(2, "can not get etype"); 133 134 for (funcs = ex_types; funcs->type; funcs++) 135 if (!strcmp(funcs->type, progt)) 136 break; 137 138 if (funcs->type == NULL) { 139 funcs = &ex_types[0]; 140 warn("execution type %s is not supported -- using %s", 141 progt, funcs->type); 142 } 143 return (funcs); 144} 145 146char * 147strsig(int sig) 148{ 149 char *ret; 150 151 ret = NULL; 152 if (sig > 0 && sig < NSIG) { 153 int i; 154 asprintf(&ret, "sig%s", sys_signame[sig]); 155 if (ret == NULL) 156 return (NULL); 157 for (i = 0; ret[i] != '\0'; ++i) 158 ret[i] = toupper(ret[i]); 159 } 160 return (ret); 161} 162 163int 164main(int ac, char **av) 165{ 166 int c; 167 int i; 168 pid_t childpid; 169 int status; 170 char **command; 171 struct ex_types *funcs; 172 int initial_open; 173 char *fname; 174 struct trussinfo *trussinfo; 175 char *signame; 176 177 fname = NULL; 178 initial_open = 1; 179 180 /* Initialize the trussinfo struct */ 181 trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); 182 if (trussinfo == NULL) 183 errx(1, "malloc() failed"); 184 bzero(trussinfo, sizeof(struct trussinfo)); 185 186 trussinfo->outfile = stderr; 187 trussinfo->strsize = 32; 188 trussinfo->pr_why = S_NONE; 189 trussinfo->curthread = NULL; 190 SLIST_INIT(&trussinfo->threadlist); 191 while ((c = getopt(ac, av, "p:o:faedDs:S")) != -1) { 192 switch (c) { 193 case 'p': /* specified pid */ 194 trussinfo->pid = atoi(optarg); 195 /* make sure i don't trace me */ 196 if(trussinfo->pid == getpid()) { 197 fprintf(stderr, "attempt to grab self.\n"); 198 exit(2); 199 } 200 break; 201 case 'f': /* Follow fork()'s */ 202 trussinfo->flags |= FOLLOWFORKS; 203 break; 204 case 'a': /* Print execve() argument strings. */ 205 trussinfo->flags |= EXECVEARGS; 206 break; 207 case 'e': /* Print execve() environment strings. */ 208 trussinfo->flags |= EXECVEENVS; 209 break; 210 case 'd': /* Absolute timestamps */ 211 trussinfo->flags |= ABSOLUTETIMESTAMPS; 212 break; 213 case 'D': /* Relative timestamps */ 214 trussinfo->flags |= RELATIVETIMESTAMPS; 215 break; 216 case 'o': /* Specified output file */ 217 fname = optarg; 218 break; 219 case 's': /* Specified string size */ 220 trussinfo->strsize = atoi(optarg); 221 break; 222 case 'S': /* Don't trace signals */ 223 trussinfo->flags |= NOSIGS; 224 break; 225 default: 226 usage(); 227 } 228 } 229 230 ac -= optind; av += optind; 231 if ((trussinfo->pid == 0 && ac == 0) || 232 (trussinfo->pid != 0 && ac != 0)) 233 usage(); 234 235 if (fname != NULL) { /* Use output file */ 236 if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 237 errx(1, "cannot open %s", fname); 238 } 239 240 /* 241 * If truss starts the process itself, it will ignore some signals -- 242 * they should be passed off to the process, which may or may not 243 * exit. If, however, we are examining an already-running process, 244 * then we restore the event mask on these same signals. 245 */ 246 247 if (trussinfo->pid == 0) { /* Start a command ourselves */ 248 command = av; 249 trussinfo->pid = setup_and_wait(command); 250 signal(SIGINT, SIG_IGN); 251 signal(SIGTERM, SIG_IGN); 252 signal(SIGQUIT, SIG_IGN); 253 } else { 254 start_tracing(trussinfo->pid); 255 signal(SIGINT, restore_proc); 256 signal(SIGTERM, restore_proc); 257 signal(SIGQUIT, restore_proc); 258 } 259 260 261 /* 262 * At this point, if we started the process, it is stopped waiting to 263 * be woken up, either in exit() or in execve(). 264 */ 265 266START_TRACE: 267 funcs = set_etype(trussinfo); 268 269 initial_open = 0; 270 /* 271 * At this point, it's a simple loop, waiting for the process to 272 * stop, finding out why, printing out why, and then continuing it. 273 * All of the grunt work is done in the support routines. 274 */ 275 276 clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); 277 278 do { 279 struct timespec timediff; 280 waitevent(trussinfo); 281 282 switch(i = trussinfo->pr_why) { 283 case S_SCE: 284 funcs->enter_syscall(trussinfo, MAXARGS); 285 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