main.c revision 104581
112144Sphk/*
22893Sdfr * Copryight 1997 Sean Eric Fagan
32893Sdfr *
42893Sdfr * Redistribution and use in source and binary forms, with or without
52893Sdfr * modification, are permitted provided that the following conditions
68876Srgrimes * are met:
72893Sdfr * 1. Redistributions of source code must retain the above copyright
82893Sdfr *    notice, this list of conditions and the following disclaimer.
98876Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
102893Sdfr *    notice, this list of conditions and the following disclaimer in the
118876Srgrimes *    documentation and/or other materials provided with the distribution.
122893Sdfr * 3. All advertising materials mentioning features or use of this software
132893Sdfr *    must display the following acknowledgement:
142893Sdfr *	This product includes software developed by Sean Eric Fagan
152893Sdfr * 4. Neither the name of the author may be used to endorse or promote
168876Srgrimes *    products derived from this software without specific prior written
172893Sdfr *    permission.
182893Sdfr *
192893Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
202893Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
212893Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
222893Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
232893Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
242893Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
252893Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
263152Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
277465Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
282893Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
292893Sdfr * SUCH DAMAGE.
302893Sdfr */
312893Sdfr
322893Sdfr#ifndef lint
332893Sdfrstatic const char rcsid[] =
342893Sdfr  "$FreeBSD: head/usr.bin/truss/main.c 104581 2002-10-06 21:46:04Z mike $";
357465Sache#endif /* not lint */
362893Sdfr
3712144Sphk/*
387465Sache * The main module for truss.  Suprisingly simple, but, then, the other
397465Sache * files handle the bulk of the work.  And, of course, the kernel has to
402893Sdfr * do a lot of the work :).
412893Sdfr */
422893Sdfr
437465Sache#include <sys/param.h>
442893Sdfr#include <sys/ioctl.h>
4512144Sphk#include <sys/pioctl.h>
467465Sache#include <sys/time.h>
477465Sache
482893Sdfr#include <err.h>
492893Sdfr#include <errno.h>
502893Sdfr#include <fcntl.h>
512893Sdfr#include <signal.h>
522893Sdfr#include <stdio.h>
532893Sdfr#include <stdlib.h>
542893Sdfr#include <string.h>
552893Sdfr#include <time.h>
562893Sdfr#include <unistd.h>
572893Sdfr
582893Sdfr#include "truss.h"
592893Sdfr#include "extern.h"
602893Sdfr
612893Sdfr/*
622893Sdfr * It's difficult to parameterize this because it must be
632893Sdfr * accessible in a signal handler.
642893Sdfr */
652893Sdfr
662893Sdfrint Procfd;
672893Sdfr
682893Sdfrstatic __inline void
692893Sdfrusage(void)
702893Sdfr{
712893Sdfr  fprintf(stderr, "%s\n%s\n",
722893Sdfr	"usage: truss [-faedDS] [-o file] -p pid",
732893Sdfr	"       truss [-faedDS] [-o file] command [args]");
742893Sdfr  exit(1);
752893Sdfr}
762893Sdfr
772893Sdfr/*
782893Sdfr * WARNING! "FreeBSD a.out" must be first, or set_etype will not
792893Sdfr * work correctly.
807465Sache */
812893Sdfrstruct ex_types {
822893Sdfr  const char *type;
832893Sdfr  void (*enter_syscall)(struct trussinfo *, int);
842893Sdfr  int (*exit_syscall)(struct trussinfo *, int);
852893Sdfr} ex_types[] = {
862893Sdfr#ifdef __alpha__
872893Sdfr  { "FreeBSD ELF", alpha_syscall_entry, alpha_syscall_exit },
882893Sdfr#endif
892893Sdfr#ifdef __i386__
902893Sdfr  { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit },
912893Sdfr  { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit },
922893Sdfr  { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit },
932893Sdfr  { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit },
942893Sdfr#endif
952893Sdfr#ifdef __sparc64__
962893Sdfr  { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit },
972893Sdfr#endif
982893Sdfr  { 0, 0, 0 },
992893Sdfr};
1002893Sdfr
1012893Sdfr/*
1022893Sdfr * Set the execution type.  This is called after every exec, and when
1037465Sache * a process is first monitored.  The procfs pseudo-file "etype" has
1047465Sache * the execution module type -- see /proc/curproc/etype for an example.
1057465Sache */
1067465Sache
1072893Sdfrstatic struct ex_types *
1082893Sdfrset_etype(struct trussinfo *trussinfo) {
1092893Sdfr  struct ex_types *funcs;
1102893Sdfr  char etype[24];
1112893Sdfr  char progt[32];
1122893Sdfr  int fd;
1132893Sdfr
1142893Sdfr  sprintf(etype, "/proc/%d/etype", trussinfo->pid);
1152893Sdfr  if ((fd = open(etype, O_RDONLY)) == -1) {
1162893Sdfr    strcpy(progt, "FreeBSD a.out");
1172893Sdfr  } else {
1182893Sdfr    int len = read(fd, progt, sizeof(progt));
1192893Sdfr    progt[len-1] = '\0';
1202893Sdfr    close(fd);
1212893Sdfr  }
1222893Sdfr
1232893Sdfr  for (funcs = ex_types; funcs->type; funcs++)
1242893Sdfr    if (!strcmp(funcs->type, progt))
1252893Sdfr      break;
1262893Sdfr
1272893Sdfr  if (funcs->type == NULL) {
1282893Sdfr    funcs = &ex_types[0];
1292893Sdfr    warn("Execution type %s is not supported -- using %s\n",
1302893Sdfr      progt, funcs->type);
1312893Sdfr  }
1322893Sdfr  return funcs;
1332893Sdfr}
1342893Sdfr
1352893Sdfrint
1362893Sdfrmain(int ac, char **av) {
1372893Sdfr  int c;
1382893Sdfr  int i;
1392893Sdfr  char **command;
1402893Sdfr  struct procfs_status pfs;
1412893Sdfr  struct ex_types *funcs;
1422893Sdfr  int in_exec = 0;
1432893Sdfr  char *fname = NULL;
14411921Sphk  int sigexit = 0;
14511921Sphk  struct trussinfo *trussinfo;
1462893Sdfr
1472893Sdfr  /* Initialize the trussinfo struct */
1482893Sdfr  trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo));
1495083Sbde  if (trussinfo == NULL)
1502893Sdfr    errx(1, "malloc() failed");
1512893Sdfr  bzero(trussinfo, sizeof(struct trussinfo));
1522893Sdfr  trussinfo->outfile = stderr;
1532893Sdfr
1542893Sdfr  while ((c = getopt(ac, av, "p:o:faedDS")) != -1) {
1552893Sdfr    switch (c) {
1562893Sdfr    case 'p':	/* specified pid */
1572893Sdfr      trussinfo->pid = atoi(optarg);
1582893Sdfr      break;
1592893Sdfr    case 'f': /* Follow fork()'s */
1607465Sache      trussinfo->flags |= FOLLOWFORKS;
1617465Sache      break;
1627465Sache    case 'a': /* Print execve() argument strings. */
1637465Sache      trussinfo->flags |= EXECVEARGS;
1642893Sdfr      break;
1652893Sdfr    case 'e': /* Print execve() environment strings. */
1667465Sache      trussinfo->flags |= EXECVEENVS;
1673152Sphk      break;
1683152Sphk    case 'd': /* Absolute timestamps */
1693152Sphk      trussinfo->flags |= ABSOLUTETIMESTAMPS;
1702893Sdfr      break;
1712893Sdfr    case 'D': /* Relative timestamps */
1727465Sache      trussinfo->flags |= RELATIVETIMESTAMPS;
1737465Sache      break;
1742893Sdfr    case 'o':	/* Specified output file */
1752893Sdfr      fname = optarg;
1762893Sdfr      break;
1772893Sdfr    case 'S':	/* Don't trace signals */
1787465Sache      trussinfo->flags |= NOSIGS;
1792893Sdfr      break;
1802893Sdfr    default:
1812893Sdfr      usage();
1822893Sdfr    }
1832893Sdfr  }
1842893Sdfr
1852893Sdfr  ac -= optind; av += optind;
1862893Sdfr  if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0))
1872893Sdfr    usage();
1882893Sdfr
1892893Sdfr  if (fname != NULL) { /* Use output file */
1902893Sdfr    if ((trussinfo->outfile = fopen(fname, "w")) == NULL)
1912893Sdfr      errx(1, "cannot open %s", fname);
1922893Sdfr  }
1932893Sdfr
1942893Sdfr  /*
1952893Sdfr   * If truss starts the process itself, it will ignore some signals --
1962893Sdfr   * they should be passed off to the process, which may or may not
1972893Sdfr   * exit.  If, however, we are examining an already-running process,
1982893Sdfr   * then we restore the event mask on these same signals.
1992893Sdfr   */
2002893Sdfr
2012893Sdfr  if (trussinfo->pid == 0) {	/* Start a command ourselves */
2022893Sdfr    command = av;
2032893Sdfr    trussinfo->pid = setup_and_wait(command);
2042893Sdfr    signal(SIGINT, SIG_IGN);
2052893Sdfr    signal(SIGTERM, SIG_IGN);
2062893Sdfr    signal(SIGQUIT, SIG_IGN);
2072893Sdfr  } else {
2082893Sdfr    signal(SIGINT, restore_proc);
2092893Sdfr    signal(SIGTERM, restore_proc);
2102893Sdfr    signal(SIGQUIT, restore_proc);
2112893Sdfr  }
2122893Sdfr
2132893Sdfr
2142893Sdfr  /*
2152893Sdfr   * At this point, if we started the process, it is stopped waiting to
2162893Sdfr   * be woken up, either in exit() or in execve().
2172893Sdfr   */
2182893Sdfr
2192893SdfrSTART_TRACE:
2202893Sdfr  Procfd = start_tracing(
2212893Sdfr		trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT |
2222893Sdfr		((trussinfo->flags & NOSIGS) ? 0 : S_SIG),
2232893Sdfr		((trussinfo->flags & FOLLOWFORKS) ? PF_FORK : 0));
2242893Sdfr  if (Procfd == -1)
2252893Sdfr    return 0;
2262893Sdfr
2272893Sdfr  pfs.why = 0;
2282893Sdfr
2292893Sdfr  funcs = set_etype(trussinfo);
2302893Sdfr  /*
2312893Sdfr   * At this point, it's a simple loop, waiting for the process to
2322893Sdfr   * stop, finding out why, printing out why, and then continuing it.
2332893Sdfr   * All of the grunt work is done in the support routines.
2342893Sdfr   */
2352893Sdfr
2362893Sdfr  clock_gettime(CLOCK_REALTIME, &trussinfo->start_time);
2372893Sdfr
2382893Sdfr  do {
2392893Sdfr    int val = 0;
2402893Sdfr
2412893Sdfr    if (ioctl(Procfd, PIOCWAIT, &pfs) == -1)
2422893Sdfr      warn("PIOCWAIT top of loop");
2432893Sdfr    else {
2442893Sdfr      switch(i = pfs.why) {
2452893Sdfr      case S_SCE:
2462893Sdfr	funcs->enter_syscall(trussinfo, pfs.val);
2472893Sdfr	clock_gettime(CLOCK_REALTIME, &trussinfo->before);
2482893Sdfr	break;
2492893Sdfr      case S_SCX:
2502893Sdfr	clock_gettime(CLOCK_REALTIME, &trussinfo->after);
2512893Sdfr	/*
2522893Sdfr	 * This is so we don't get two messages for an exec -- one
2532893Sdfr	 * for the S_EXEC, and one for the syscall exit.  It also,
2542893Sdfr	 * conveniently, ensures that the first message printed out
2552893Sdfr	 * isn't the return-from-syscall used to create the process.
2562893Sdfr	 */
2572893Sdfr
2582893Sdfr	if (in_exec) {
2592893Sdfr	  in_exec = 0;
2602893Sdfr	  break;
2612893Sdfr	}
2622893Sdfr
2632893Sdfr	if (trussinfo->in_fork && (trussinfo->flags & FOLLOWFORKS)) {
2642893Sdfr	  int childpid;
2652893Sdfr
2662893Sdfr	  trussinfo->in_fork = 0;
2672893Sdfr	  childpid = funcs->exit_syscall(trussinfo, pfs.val);
2682893Sdfr
2692893Sdfr	  /*
2702893Sdfr	   * Fork a new copy of ourself to trace the child of the
2712893Sdfr	   * original traced process.
2722893Sdfr	   */
2732893Sdfr	  if (fork() == 0) {
2742893Sdfr	    trussinfo->pid = childpid;
2752893Sdfr	    goto START_TRACE;
2762893Sdfr	  }
2772893Sdfr	  break;
2782893Sdfr	}
2792893Sdfr	funcs->exit_syscall(trussinfo, pfs.val);
2802893Sdfr	break;
2812893Sdfr      case S_SIG:
2822893Sdfr	fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val);
2832893Sdfr	sigexit = pfs.val;
2842893Sdfr	break;
2852893Sdfr      case S_EXIT:
2862893Sdfr	fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val);
2872893Sdfr	break;
2882893Sdfr      case S_EXEC:
2892893Sdfr	funcs = set_etype(trussinfo);
2902893Sdfr	in_exec = 1;
2912893Sdfr	break;
2922893Sdfr      default:
2932893Sdfr	fprintf (trussinfo->outfile, "Process stopped because of:  %d\n", i);
2942893Sdfr	break;
2952893Sdfr      }
2962893Sdfr    }
2972893Sdfr    if (ioctl(Procfd, PIOCCONT, val) == -1) {
2982893Sdfr      if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH)
2992893Sdfr	break;
3002893Sdfr      else
3012893Sdfr	warn("PIOCCONT");
3022893Sdfr    }
3032893Sdfr  } while (pfs.why != S_EXIT);
3042893Sdfr  fflush(trussinfo->outfile);
3052893Sdfr  if (sigexit) {
3062893Sdfr    if (sigexit == SIGQUIT)
3072893Sdfr      exit(sigexit);
3082893Sdfr    (void) signal(sigexit, SIG_DFL);
3092893Sdfr    (void) kill(getpid(), sigexit);
3102893Sdfr  }
3112893Sdfr  return 0;
3122893Sdfr}
3132893Sdfr