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$"); 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/ioctl.h> 43 #include <sys/pioctl.h> 44 #include <sys/types.h> 45 #include <sys/time.h> 46 #include <sys/resource.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 /* 63 * It's difficult to parameterize this because it must be 64 * accessible in a signal handler. 65 */ 66 67 int Procfd; 68 69 static void 70 usage(void) 71 { 72 fprintf(stderr, "%s\n%s\n", 73 "usage: truss [-faedDS] [-o file] [-s strsize] -p pid", 74 " truss [-faedDS] [-o file] [-s strsize] command [args]"); 75 exit(1); 76 } 77 78 /* 79 * WARNING! "FreeBSD a.out" must be first, or set_etype will not 80 * work correctly. 81 */ 82 struct ex_types { 83 const char *type; 84 void (*enter_syscall)(struct trussinfo *, int); 85 long (*exit_syscall)(struct trussinfo *, int); 86 } ex_types[] = { 87 #ifdef __alpha__ 88 { "FreeBSD ELF", alpha_syscall_entry, alpha_syscall_exit }, 89 #endif 90 #ifdef __amd64__ 91 { "FreeBSD ELF64", amd64_syscall_entry, amd64_syscall_exit }, 92 #endif 93 #ifdef __i386__ 94 { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit }, 95 { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit }, 96 { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit }, 97 { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit }, 98 #endif 99 #ifdef __ia64__ 100 { "FreeBSD ELF64", ia64_syscall_entry, ia64_syscall_exit }, 101 #endif 102 #ifdef __powerpc__ 103 { "FreeBSD ELF", powerpc_syscall_entry, powerpc_syscall_exit }, 104 { "FreeBSD ELF32", powerpc_syscall_entry, powerpc_syscall_exit }, 105 #endif 106 #ifdef __sparc64__ 107 { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit }, 108 #endif 109 { 0, 0, 0 }, 110 }; 111 112 /* 113 * Set the execution type. This is called after every exec, and when 114 * a process is first monitored. The procfs pseudo-file "etype" has 115 * the execution module type -- see /proc/curproc/etype for an example. 116 */ 117 118 static struct ex_types * 119 set_etype(struct trussinfo *trussinfo) 120 { 121 struct ex_types *funcs; 122 char etype[24]; 123 char progt[32]; 124 int fd; 125 126 sprintf(etype, "/proc/%d/etype", trussinfo->pid); 127 if ((fd = open(etype, O_RDONLY)) == -1) { 128 strcpy(progt, "FreeBSD a.out"); 129 } else { 130 int len = read(fd, progt, sizeof(progt)); 131 progt[len-1] = '\0'; 132 close(fd); 133 } 134 135 for (funcs = ex_types; funcs->type; funcs++) 136 if (!strcmp(funcs->type, progt)) 137 break; 138 139 if (funcs->type == NULL) { 140 funcs = &ex_types[0]; 141 warn("execution type %s is not supported -- using %s", 142 progt, funcs->type); 143 } 144 return (funcs); 145 } 146 147 char * 148 strsig(int sig) 149 { 150 char *ret; 151 152 ret = NULL; 153 if (sig > 0 && sig < NSIG) { 154 int i; 155 asprintf(&ret, "sig%s", sys_signame[sig]); 156 if (ret == NULL) 157 return (NULL); 158 for (i = 0; ret[i] != '\0'; ++i) 159 ret[i] = toupper(ret[i]); 160 } 161 return (ret); 162 } 163 164 int 165 main(int ac, char **av) 166 { 167 int c; 168 int i; 169 char **command; 170 struct procfs_status pfs; 171 struct ex_types *funcs; 172 int in_exec, sigexit, initial_open; 173 char *fname; 174 struct trussinfo *trussinfo; 175 char *signame; 176 177 in_exec = 0; 178 sigexit = 0; 179 fname = NULL; 180 initial_open = 1; 181 182 /* Initialize the trussinfo struct */ 183 trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); 184 if (trussinfo == NULL) 185 errx(1, "malloc() failed"); 186 bzero(trussinfo, sizeof(struct trussinfo)); 187 trussinfo->outfile = stderr; 188 trussinfo->strsize = 32; 189 190 while ((c = getopt(ac, av, "p:o:faedDs:S")) != -1) { 191 switch (c) { 192 case 'p': /* specified pid */ 193 trussinfo->pid = atoi(optarg); 194 break; 195 case 'f': /* Follow fork()'s */ 196 trussinfo->flags |= FOLLOWFORKS; 197 break; 198 case 'a': /* Print execve() argument strings. */ 199 trussinfo->flags |= EXECVEARGS; 200 break; 201 case 'e': /* Print execve() environment strings. */ 202 trussinfo->flags |= EXECVEENVS; 203 break; 204 case 'd': /* Absolute timestamps */ 205 trussinfo->flags |= ABSOLUTETIMESTAMPS; 206 break; 207 case 'D': /* Relative timestamps */ 208 trussinfo->flags |= RELATIVETIMESTAMPS; 209 break; 210 case 'o': /* Specified output file */ 211 fname = optarg; 212 break; 213 case 's': /* Specified string size */ 214 trussinfo->strsize = atoi(optarg); 215 break; 216 case 'S': /* Don't trace signals */ 217 trussinfo->flags |= NOSIGS; 218 break; 219 default: 220 usage(); 221 } 222 } 223 224 ac -= optind; av += optind; 225 if ((trussinfo->pid == 0 && ac == 0) || 226 (trussinfo->pid != 0 && ac != 0)) 227 usage(); 228 229 if (fname != NULL) { /* Use output file */ 230 if ((trussinfo->outfile = fopen(fname, "w")) == NULL) 231 errx(1, "cannot open %s", fname); 232 } 233 234 /* 235 * If truss starts the process itself, it will ignore some signals -- 236 * they should be passed off to the process, which may or may not 237 * exit. If, however, we are examining an already-running process, 238 * then we restore the event mask on these same signals. 239 */ 240 241 if (trussinfo->pid == 0) { /* Start a command ourselves */ 242 command = av; 243 trussinfo->pid = setup_and_wait(command); 244 signal(SIGINT, SIG_IGN); 245 signal(SIGTERM, SIG_IGN); 246 signal(SIGQUIT, SIG_IGN); 247 } else { 248 signal(SIGINT, restore_proc); 249 signal(SIGTERM, restore_proc); 250 signal(SIGQUIT, restore_proc); 251 } 252 253 254 /* 255 * At this point, if we started the process, it is stopped waiting to 256 * be woken up, either in exit() or in execve(). 257 */ 258 259 START_TRACE: 260 Procfd = start_tracing( 261 trussinfo->pid, initial_open, 262 S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | 263 ((trussinfo->flags & NOSIGS) ? 0 : S_SIG), 264 ((trussinfo->flags & FOLLOWFORKS) ? PF_FORK : 0)); 265 initial_open = 0; 266 if (Procfd == -1) 267 return (0); 268 269 pfs.why = 0; 270 271 funcs = set_etype(trussinfo); 272 /* 273 * At this point, it's a simple loop, waiting for the process to 274 * stop, finding out why, printing out why, and then continuing it. 275 * All of the grunt work is done in the support routines. 276 */ 277 278 clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); 279 280 do { 281 int val = 0; 282 struct timespec timediff; 283 284 if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) 285 warn("PIOCWAIT top of loop"); 286 else { 287 switch(i = pfs.why) { 288 case S_SCE: 289 funcs->enter_syscall(trussinfo, pfs.val); 290 clock_gettime(CLOCK_REALTIME, 291 &trussinfo->before); 292 break; 293 case S_SCX: 294 clock_gettime(CLOCK_REALTIME, 295 &trussinfo->after); 296 /* 297 * This is so we don't get two messages for 298 * an exec -- one for the S_EXEC, and one for 299 * the syscall exit. It also, conveniently, 300 * ensures that the first message printed out 301 * isn't the return-from-syscall used to 302 * create the process. 303 */ 304 if (in_exec) { 305 in_exec = 0; 306 break; 307 } 308 309 if (trussinfo->in_fork && 310 (trussinfo->flags & FOLLOWFORKS)) { 311 int childpid; 312 313 trussinfo->in_fork = 0; 314 childpid = 315 funcs->exit_syscall(trussinfo, 316 pfs.val); 317 318 /* 319 * Fork a new copy of ourself to trace 320 * the child of the original traced 321 * process. 322 */ 323 if (fork() == 0) { 324 trussinfo->pid = childpid; 325 goto START_TRACE; 326 } 327 break; 328 } 329 funcs->exit_syscall(trussinfo, pfs.val); 330 break; 331 case S_SIG: 332 if (trussinfo->flags & FOLLOWFORKS) 333 fprintf(trussinfo->outfile, "%5d: ", 334 trussinfo->pid); 335 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 336 timespecsubt(&trussinfo->after, 337 &trussinfo->start_time, &timediff); 338 fprintf(trussinfo->outfile, "%ld.%09ld ", 339 (long)timediff.tv_sec, 340 timediff.tv_nsec); 341 } 342 if (trussinfo->flags & RELATIVETIMESTAMPS) { 343 timespecsubt(&trussinfo->after, 344 &trussinfo->before, &timediff); 345 fprintf(trussinfo->outfile, "%ld.%09ld ", 346 (long)timediff.tv_sec, 347 timediff.tv_nsec); 348 } 349 signame = strsig(pfs.val); 350 fprintf(trussinfo->outfile, 351 "SIGNAL %lu (%s)\n", pfs.val, 352 signame == NULL ? "?" : signame); 353 free(signame); 354 sigexit = pfs.val; 355 break; 356 case S_EXIT: 357 if (trussinfo->flags & FOLLOWFORKS) 358 fprintf(trussinfo->outfile, "%5d: ", 359 trussinfo->pid); 360 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 361 timespecsubt(&trussinfo->after, 362 &trussinfo->start_time, &timediff); 363 fprintf(trussinfo->outfile, "%ld.%09ld ", 364 (long)timediff.tv_sec, 365 timediff.tv_nsec); 366 } 367 if (trussinfo->flags & RELATIVETIMESTAMPS) { 368 timespecsubt(&trussinfo->after, 369 &trussinfo->before, &timediff); 370 fprintf(trussinfo->outfile, "%ld.%09ld ", 371 (long)timediff.tv_sec, timediff.tv_nsec); 372 } 373 fprintf(trussinfo->outfile, 374 "process exit, rval = %lu\n", pfs.val); 375 break; 376 case S_EXEC: 377 funcs = set_etype(trussinfo); 378 in_exec = 1; 379 break; 380 default: 381 fprintf(trussinfo->outfile, 382 "Process stopped because of: %d\n", i); 383 break; 384 } 385 } 386 if (ioctl(Procfd, PIOCCONT, val) == -1) { 387 if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH) 388 break; 389 else 390 warn("PIOCCONT"); 391 } 392 } while (pfs.why != S_EXIT); 393 fflush(trussinfo->outfile); 394 if (sigexit) { 395 struct rlimit rlp; 396 397 rlp.rlim_cur = 0; 398 rlp.rlim_max = 0; 399 setrlimit(RLIMIT_CORE, &rlp); 400 (void) signal(sigexit, SIG_DFL); 401 (void) kill(getpid(), sigexit); 402 } 403 return (0); 404 } 405