Пример #1
0
int
main(int ac, char **av) {
  int c, i, mntsize;
  char **command;
  struct procfs_status pfs;
  struct ex_types *funcs;
  struct statfs *mntbuf;
  int in_exec = 0;
  char *fname = NULL;
  int sigexit = 0;
  struct trussinfo *trussinfo;

  /* Initialize the trussinfo struct */
  trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo));
  if (trussinfo == NULL)
	  errx(1, "malloc() failed");
  bzero(trussinfo, sizeof(struct trussinfo));
  trussinfo->outfile = stderr;

  /* Check where procfs is mounted if it is mounted */
  if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
	  err(1, "getmntinfo");
  for (i = 0; i < mntsize; i++) {
	  if (strcasecmp(mntbuf[i].f_mntfromname, "procfs") == 0) {
		  strlcpy(procfs_path, mntbuf[i].f_mntonname, sizeof(procfs_path));
		  have_procfs = 1;
		  break;
	  }
  }
  if (!have_procfs) {
	  errno = 2;
	  err(1, "You must have a mounted procfs to use truss");
  }

  while ((c = getopt(ac, av, "p:o:S")) != -1) {
    switch (c) {
    case 'p':	/* specified pid */
      trussinfo->pid = atoi(optarg);
      if (trussinfo->pid == getpid()) {
	      /* make sure truss doesn't trace itself */
	      fprintf(stderr, "truss: attempt to self trace: %d\n", trussinfo->pid);
	      exit(2);
      }
      break;
    case 'o':	/* Specified output file */
      fname = optarg;
      break;
    case 'S':	/* Don't trace signals */ 
      trussinfo->flags |= NOSIGS;
      break;
    default:
      usage();
    }
  }

  ac -= optind; av += optind;
  if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0))
    usage();

  if (fname != NULL) { /* Use output file */
    if ((trussinfo->outfile = fopen(fname, "w")) == NULL)
      errx(1, "cannot open %s", fname);
  }

  /*
   * If truss starts the process itself, it will ignore some signals --
   * they should be passed off to the process, which may or may not
   * exit.  If, however, we are examining an already-running process,
   * then we restore the event mask on these same signals.
   */

  if (trussinfo->pid == 0) {	/* Start a command ourselves */
    command = av;
    trussinfo->pid = setup_and_wait(command);
    signal(SIGINT, SIG_IGN);
    signal(SIGTERM, SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
  } else {
    signal(SIGINT, restore_proc);
    signal(SIGTERM, restore_proc);
    signal(SIGQUIT, restore_proc);
  }


  /*
   * At this point, if we started the process, it is stopped waiting to
   * be woken up, either in exit() or in execve().
   */

  Procfd = start_tracing(
      trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT |
		     ((trussinfo->flags & NOSIGS) ? 0 : S_SIG));
  if (Procfd == -1)
    return 0;

  pfs.why = 0;

  funcs = set_etype(trussinfo);
  /*
   * At this point, it's a simple loop, waiting for the process to
   * stop, finding out why, printing out why, and then continuing it.
   * All of the grunt work is done in the support routines.
   */

  do {
    int val = 0;

    if (ioctl(Procfd, PIOCWAIT, &pfs) == -1)
      warn("PIOCWAIT top of loop");
    else {
      switch(i = pfs.why) {
      case S_SCE:
	funcs->enter_syscall(trussinfo, pfs.val);
	break;
      case S_SCX:
	/*
	 * This is so we don't get two messages for an exec -- one
	 * for the S_EXEC, and one for the syscall exit.  It also,
	 * conveniently, ensures that the first message printed out
	 * isn't the return-from-syscall used to create the process.
	 */

	if (in_exec) {
	  in_exec = 0;
	  break;
	}
	funcs->exit_syscall(trussinfo, pfs.val);
	break;
      case S_SIG:
	fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val);
	sigexit = pfs.val;
	break;
      case S_EXIT:
	fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val);
	break;
      case S_EXEC:
	funcs = set_etype(trussinfo);
	in_exec = 1;
	break;
      default:
	fprintf (trussinfo->outfile, "Process stopped because of:  %d\n", i);
	break;
      }
    }
    if (ioctl(Procfd, PIOCCONT, val) == -1) {
      if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH)
	break;
      else
	warn("PIOCCONT");
    }
  } while (pfs.why != S_EXIT);
  fflush(trussinfo->outfile);
  if (sigexit) {
    if (sigexit == SIGQUIT)
      exit(sigexit);
    (void) signal(sigexit, SIG_DFL);
    (void) kill(getpid(), sigexit);
  }
  return 0;
}
Пример #2
0
int
main(int ac, char **av)
{
	struct sigaction sa;
	struct trussinfo *trussinfo;
	char *fname;
	char **command;
	pid_t pid;
	int c;

	fname = NULL;

	/* Initialize the trussinfo struct */
	trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo));
	if (trussinfo == NULL)
		errx(1, "calloc() failed");

	pid = 0;
	trussinfo->outfile = stderr;
	trussinfo->strsize = 32;
	trussinfo->curthread = NULL;
	LIST_INIT(&trussinfo->proclist);
	init_syscalls();
	while ((c = getopt(ac, av, "p:o:facedDs:SH")) != -1) {
		switch (c) {
		case 'p':	/* specified pid */
			pid = atoi(optarg);
			/* make sure i don't trace me */
			if (pid == getpid()) {
				errx(2, "attempt to grab self.");
			}
			break;
		case 'f': /* Follow fork()'s */
			trussinfo->flags |= FOLLOWFORKS;
			break;
		case 'a': /* Print execve() argument strings. */
			trussinfo->flags |= EXECVEARGS;
			break;
		case 'c': /* Count number of system calls and time. */
			trussinfo->flags |= (COUNTONLY | NOSIGS);
			break;
		case 'e': /* Print execve() environment strings. */
			trussinfo->flags |= EXECVEENVS;
			break;
		case 'd': /* Absolute timestamps */
			trussinfo->flags |= ABSOLUTETIMESTAMPS;
			break;
		case 'D': /* Relative timestamps */
			trussinfo->flags |= RELATIVETIMESTAMPS;
			break;
		case 'o':	/* Specified output file */
			fname = optarg;
			break;
		case 's':	/* Specified string size */
			trussinfo->strsize = atoi(optarg);
			break;
		case 'S':	/* Don't trace signals */
			trussinfo->flags |= NOSIGS;
			break;
		case 'H':
			trussinfo->flags |= DISPLAYTIDS;
			break;
		default:
			usage();
		}
	}

	ac -= optind; av += optind;
	if ((pid == 0 && ac == 0) ||
	    (pid != 0 && ac != 0))
		usage();

	if (fname != NULL) { /* Use output file */
		/*
		 * Set close-on-exec ('e'), so that the output file is not
		 * shared with the traced process.
		 */
		if ((trussinfo->outfile = fopen(fname, "we")) == NULL)
			err(1, "cannot open %s", fname);
	}

	/*
	 * If truss starts the process itself, it will ignore some signals --
	 * they should be passed off to the process, which may or may not
	 * exit.  If, however, we are examining an already-running process,
	 * then we restore the event mask on these same signals.
	 */
	if (pid == 0) {
		/* Start a command ourselves */
		command = av;
		setup_and_wait(trussinfo, command);
		signal(SIGINT, SIG_IGN);
		signal(SIGTERM, SIG_IGN);
		signal(SIGQUIT, SIG_IGN);
	} else {
		sa.sa_handler = restore_proc;
		sa.sa_flags = 0;
		sigemptyset(&sa.sa_mask);
		sigaction(SIGINT, &sa, NULL);
		sigaction(SIGQUIT, &sa, NULL);
		sigaction(SIGTERM, &sa, NULL);
		start_tracing(trussinfo, pid);
	}

	/*
	 * At this point, if we started the process, it is stopped waiting to
	 * be woken up, either in exit() or in execve().
	 */
	if (LIST_FIRST(&trussinfo->proclist)->abi == NULL) {
		/*
		 * If we are not able to handle this ABI, detach from the
		 * process and exit.  If we just created a new process to
		 * run a command, kill the new process rather than letting
		 * it run untraced.
		 */
		if (pid == 0)
			kill(LIST_FIRST(&trussinfo->proclist)->pid, SIGKILL);
		ptrace(PT_DETACH, LIST_FIRST(&trussinfo->proclist)->pid, NULL,
		    0);
		return (1);
	}
	ptrace(PT_SYSCALL, LIST_FIRST(&trussinfo->proclist)->pid, (caddr_t)1,
	    0);

	/*
	 * At this point, it's a simple loop, waiting for the process to
	 * stop, finding out why, printing out why, and then continuing it.
	 * All of the grunt work is done in the support routines.
	 */
	clock_gettime(CLOCK_REALTIME, &trussinfo->start_time);

	eventloop(trussinfo);

	if (trussinfo->flags & COUNTONLY)
		print_summary(trussinfo);

	fflush(trussinfo->outfile);

	return (0);
}
Пример #3
0
int
main(int ac, char **av)
{
	struct timespec timediff;
	struct sigaction sa;
	struct ex_types *funcs;
	struct trussinfo *trussinfo;
	char *fname;
	char *signame;
	char **command;
	pid_t childpid;
	int c, initial_open, status;

	fname = NULL;
	initial_open = 1;

	/* Initialize the trussinfo struct */
	trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo));
	if (trussinfo == NULL)
		errx(1, "calloc() failed");

	trussinfo->outfile = stderr;
	trussinfo->strsize = 32;
	trussinfo->pr_why = S_NONE;
	trussinfo->curthread = NULL;
	SLIST_INIT(&trussinfo->threadlist);
	while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) {
		switch (c) {
		case 'p':	/* specified pid */
			trussinfo->pid = atoi(optarg);
			/* make sure i don't trace me */
			if (trussinfo->pid == getpid()) {
				fprintf(stderr, "attempt to grab self.\n");
				exit(2);
			}
			break;
		case 'f': /* Follow fork()'s */
			trussinfo->flags |= FOLLOWFORKS;
			break;
		case 'a': /* Print execve() argument strings. */
			trussinfo->flags |= EXECVEARGS;
			break;
		case 'c': /* Count number of system calls and time. */
			trussinfo->flags |= COUNTONLY;
			break;
		case 'e': /* Print execve() environment strings. */
			trussinfo->flags |= EXECVEENVS;
			break;
		case 'd': /* Absolute timestamps */
			trussinfo->flags |= ABSOLUTETIMESTAMPS;
			break;
		case 'D': /* Relative timestamps */
			trussinfo->flags |= RELATIVETIMESTAMPS;
			break;
		case 'o':	/* Specified output file */
			fname = optarg;
			break;
		case 's':	/* Specified string size */
			trussinfo->strsize = atoi(optarg);
			break;
		case 'S':	/* Don't trace signals */
			trussinfo->flags |= NOSIGS;
			break;
		default:
			usage();
		}
	}

	ac -= optind; av += optind;
	if ((trussinfo->pid == 0 && ac == 0) ||
	    (trussinfo->pid != 0 && ac != 0))
		usage();

	if (fname != NULL) { /* Use output file */
		/*
		 * Set close-on-exec ('e'), so that the output file is not
		 * shared with the traced process.
		 */
		if ((trussinfo->outfile = fopen(fname, "we")) == NULL)
			err(1, "cannot open %s", fname);
	}

	/*
	 * If truss starts the process itself, it will ignore some signals --
	 * they should be passed off to the process, which may or may not
	 * exit.  If, however, we are examining an already-running process,
	 * then we restore the event mask on these same signals.
	 */

	if (trussinfo->pid == 0) {	/* Start a command ourselves */
		command = av;
		trussinfo->pid = setup_and_wait(command);
		signal(SIGINT, SIG_IGN);
		signal(SIGTERM, SIG_IGN);
		signal(SIGQUIT, SIG_IGN);
	} else {
		sa.sa_handler = restore_proc;
		sa.sa_flags = 0;
		sigemptyset(&sa.sa_mask);
		sigaction(SIGINT, &sa, NULL);
		sigaction(SIGQUIT, &sa, NULL);
		sigaction(SIGTERM, &sa, NULL);
		start_tracing(trussinfo->pid);
	}


	/*
	 * At this point, if we started the process, it is stopped waiting to
	 * be woken up, either in exit() or in execve().
	 */

START_TRACE:
	funcs = set_etype(trussinfo);

	initial_open = 0;
	/*
	 * At this point, it's a simple loop, waiting for the process to
	 * stop, finding out why, printing out why, and then continuing it.
	 * All of the grunt work is done in the support routines.
	 */

	clock_gettime(CLOCK_REALTIME, &trussinfo->start_time);

	do {
		waitevent(trussinfo);

		switch (trussinfo->pr_why) {
		case S_SCE:
			funcs->enter_syscall(trussinfo, MAXARGS);
			clock_gettime(CLOCK_REALTIME,
			    &trussinfo->curthread->before);
			break;
		case S_SCX:
			clock_gettime(CLOCK_REALTIME,
			    &trussinfo->curthread->after);

			if (trussinfo->curthread->in_fork &&
			    (trussinfo->flags & FOLLOWFORKS)) {
				trussinfo->curthread->in_fork = 0;
				childpid = funcs->exit_syscall(trussinfo,
				    trussinfo->pr_data);

				/*
				 * Fork a new copy of ourself to trace
				 * the child of the original traced
				 * process.
				 */
				if (fork() == 0) {
					trussinfo->pid = childpid;
					start_tracing(trussinfo->pid);
					goto START_TRACE;
				}
				break;
			}
			funcs->exit_syscall(trussinfo, MAXARGS);
			break;
		case S_SIG:
			if (trussinfo->flags & NOSIGS)
				break;
			if (trussinfo->flags & FOLLOWFORKS)
				fprintf(trussinfo->outfile, "%5d: ",
				    trussinfo->pid);
			if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
				timespecsubt(&trussinfo->curthread->after,
				    &trussinfo->start_time, &timediff);
				fprintf(trussinfo->outfile, "%jd.%09ld ",
				    (intmax_t)timediff.tv_sec,
				    timediff.tv_nsec);
			}
			if (trussinfo->flags & RELATIVETIMESTAMPS) {
				timespecsubt(&trussinfo->curthread->after,
				    &trussinfo->curthread->before, &timediff);
				fprintf(trussinfo->outfile, "%jd.%09ld ",
				    (intmax_t)timediff.tv_sec,
				    timediff.tv_nsec);
			}
			signame = strsig(trussinfo->pr_data);
			fprintf(trussinfo->outfile,
			    "SIGNAL %u (%s)\n", trussinfo->pr_data,
			    signame == NULL ? "?" : signame);
			break;
		case S_EXIT:
			if (trussinfo->flags & COUNTONLY)
				break;
			if (trussinfo->flags & FOLLOWFORKS)
				fprintf(trussinfo->outfile, "%5d: ",
				    trussinfo->pid);
			if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
				timespecsubt(&trussinfo->curthread->after,
				    &trussinfo->start_time, &timediff);
				fprintf(trussinfo->outfile, "%jd.%09ld ",
				    (intmax_t)timediff.tv_sec,
				    timediff.tv_nsec);
			}
			if (trussinfo->flags & RELATIVETIMESTAMPS) {
				timespecsubt(&trussinfo->curthread->after,
				    &trussinfo->curthread->before, &timediff);
				fprintf(trussinfo->outfile, "%jd.%09ld ",
				    (intmax_t)timediff.tv_sec,
				    timediff.tv_nsec);
			}
			fprintf(trussinfo->outfile,
			    "process exit, rval = %u\n", trussinfo->pr_data);
			break;
		default:
			break;
		}
	} while (trussinfo->pr_why != S_EXIT &&
	    trussinfo->pr_why != S_DETACHED);

	if (trussinfo->flags & FOLLOWFORKS) {
		do {
			childpid = wait(&status);
		} while (childpid != -1);
	}

	if (trussinfo->flags & COUNTONLY)
		print_summary(trussinfo);

	fflush(trussinfo->outfile);

	return (0);
}