home *** CD-ROM | disk | FTP | other *** search
- /*
- * @(#)strace.c 2.5 92/01/10
- */
-
- #include <stdio.h>
- #include <string.h>
- #include <signal.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/varargs.h>
- #include <sys/param.h>
- #include <sys/file.h>
- #include <sys/ptrace.h>
- #include <sys/wait.h>
-
- #include "defs.h"
-
- int debug, verbose, followfork, followvfork, tflag, cflag;
- struct tcb tcbtab[MAXPROC];
- int Nproc;
-
- static FILE *outf;
- static char *outfname;
-
- static struct tcb *findtcb();
- static void cleanup();
-
- extern void exit();
-
- int
- main(argc, argv)
- int argc;
- char *argv[];
- {
- extern int optind;
- extern char *optarg;
- struct tcb *tcp;
- int c, pid = 0;
-
- outf = stderr; /* default output is a standard error */
- while ((c = getopt(argc, argv, "dvo:p:s:fFtc")) != EOF) {
- switch (c) {
- case 'd':
- debug++;
- break;
- case 'v':
- verbose++;
- break;
- case 'F':
- followvfork++;
- /* `-F' implies `-f' */
- /*FALLTHROUGH*/
- case 'f':
- followfork++;
- #ifdef f_IMPLIES_F
- followvfork++;
- #endif
- break;
- case 't':
- tflag++;
- break;
- case 's':
- max_str_len = (unsigned int)atoi(optarg);
- break;
- case 'o':
- if ((outf = fopen(outfname = strdup(optarg), "w")) == NULL)
- perror("strace: fopen"), exit(1);
- break;
- case 'p':
- if ((pid = atoi(optarg)) == 0) {
- (void)fprintf(stderr, "Invalid process id\n");
- break;
- }
- if ((tcp = alloctcb(pid)) == NULLTCB) {
- (void)fprintf(stderr, "tcb table full\n");
- exit(1);
- }
- tcp->flags |= TCB_ATTACHED;
- break;
- case 'c':
- cflag++;
- break;
- default:
- usage:
- (void)fprintf(stderr,
- "Usage: %s [-d][-t][-f][-s strsize][-o file] -p pid | command\n", argv[0]);
- exit(1);
- break;
- }
- }
-
- (void)signal(SIGINT, cleanup);
- (void)signal(SIGTERM, cleanup);
- (void)signal(SIGTTOU, SIG_IGN);
- (void)signal(SIGTTIN, SIG_IGN);
-
- for (c = 0, tcp = tcbtab; c < MAXPROC; c++, tcp++) {
- if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))
- continue;
- if (ptrace(PTRACE_ATTACH, tcp->pid, (char *)1, 0) < 0) {
- perror("attach: ptrace(PTRACE_ATTACH)");
- droptcb(tcp);
- continue;
- }
- (void)fprintf(stderr,
- "Process %u attached - interrupt to quit\n", pid);
- }
-
- if (optind < argc) {
- switch (pid = fork()) {
- case -1:
- perror("strace: fork");
- cleanup();
- exit(1);
- break;
- case 0: {
- if (ptrace(PTRACE_TRACEME, 0, (char *)1, 0) < 0) {
- perror("trace: ptrace(PTRACE_TRACEME)");
- return -1;
- }
- if (debug) (void)kill(getpid(), SIGSTOP);
- (void)execvp(argv[optind], &argv[optind]);
- if (errno == ENOENT)
- (void)fprintf(stderr, "%s: Command not found\n",
- argv[optind]);
- else
- perror("strace: exec");
- _exit(1);
- break;
- }
- default:
- if (alloctcb(pid) == NULLTCB) {
- (void)fprintf(stderr, "tcb table full\n");
- cleanup();
- exit(1);
- }
- break;
- }
- } else
- if (Nproc == 0) /* No valid arguments */ goto usage;
-
- if (trace() < 0)
- exit(1);
- return 0;
- }
-
- struct tcb *
- alloctcb(pid)
- int pid;
- {
- int i;
- struct tcb *tcp;
-
- for (i = 0, tcp = tcbtab; i < MAXPROC; i++, tcp++) {
- if ((tcp->flags & TCB_INUSE) == 0) {
- tcp->pid = pid;
- tcp->parent = NULLTCB;
- tcp->nchildren = 0;
- tcp->flags = TCB_INUSE | TCB_STARTUP;
- tcp->outf = outf; /* Initialise to current out file */
- Nproc++;
- return tcp;
- }
- }
- return NULLTCB;
- }
-
- static struct tcb *
- findtcb(pid)
- int pid;
- {
- int i;
- struct tcb *tcp;
-
- for (i = 0, tcp = tcbtab; i < MAXPROC; i++, tcp++) {
- if ((tcp->pid == pid) && (tcp->flags & TCB_INUSE))
- return tcp;
- }
- return NULLTCB;
- }
-
- void
- droptcb(tcp)
- struct tcb *tcp;
- {
- if (tcp->pid == 0)
- return;
- tcp->pid = 0;
- tcp->flags = 0;
- if (tcp->parent != NULLTCB) {
- tcp->parent->nchildren--;
- tcp->parent = NULLTCB;
- }
- #if 0
- if (tcp->outf != stderr) fclose(tcp->outf);
- #endif
- tcp->outf = stderr;
- Nproc--;
- }
-
- static int
- resume(tcp)
- struct tcb *tcp;
- {
- if (tcp == NULLTCB)
- return -1;
-
- if (!(tcp->flags & TCB_SUSPENDED)) {
- (void)fprintf(stderr, "PANIC: pid %u not suspended\n", tcp->pid);
- return -1;
- }
- tcp->flags &= ~TCB_SUSPENDED;
-
- if (ptrace(PTRACE_SYSCALL, tcp->pid, (char *)1, 0) < 0) {
- perror("resume: ptrace(PTRACE_SYSCALL)");
- return -1;
- }
-
- (void)fprintf(stderr, "Process %u resumed\n", tcp->pid);
- return 0;
- }
-
- static int
- detach(tcp, sig)
- struct tcb *tcp;
- {
- /* detach traced process; continue with sig */
- int error;
-
- if (tcp->flags & TCB_BPTSET)
- sig = SIGKILL;
-
- /* PTRACE_DETACH won't respect `sig' argument, so we post it here */
- if (sig)
- if (kill(tcp->pid, sig) < 0)
- perror("detach: kill");
-
- if ((error = ptrace(PTRACE_DETACH, tcp->pid, (char *)1, sig)) < 0) {
- perror("detach: ptrace(PTRACE_DETACH)");
- }
-
- (void)fprintf(stderr, "\nProcess %u detached\n", tcp->pid);
-
- if (waiting_parent(tcp))
- error = resume(tcp->parent);
- droptcb(tcp);
- return error;
- }
-
- void
- newoutf(tcp)
- struct tcb *tcp;
- {
- char name[MAXPATHLEN];
- FILE *fp;
-
- if (outfname) {
- (void)sprintf(name, "%s.%u", outfname, tcp->pid);
- if ((fp = fopen(name, "w")) == NULL) {
- perror("fopen");
- return;
- }
- tcp->outf = fp;
- }
- return;
- }
-
- void
- tprintf(fmt, va_alist)
- char *fmt;
- va_dcl
- {
- va_list args;
-
- va_start(args);
- (void)vfprintf(outf, fmt, args);
- va_end(args);
- return;
- }
-
- static void
- cleanup()
- {
- int i;
- struct tcb *tcp;
-
- for (i = 0, tcp = tcbtab; i < MAXPROC; i++, tcp++) {
- if (!(tcp->flags & TCB_INUSE))
- continue;
- if (debug)
- (void)fprintf(stderr,
- "cleanup: looking at pid %u\n", tcp->pid);
- if (tcp->flags & TCB_ATTACHED) {
- (void)detach(tcp, 0);
- } else {
- (void)kill(tcp->pid, SIGTERM);
- }
- }
- }
-
- int
- trace()
- {
- int pid;
- int status;
- struct tcb *tcp;
-
- while (1) {
- (void)sigsetmask(0);
- if ((pid = wait(&status)) == -1) {
- if (Nproc == 0)
- break;
- perror("trace: wait");
- return -1;
- }
- (void)sigblock(sigmask(SIGINT)|sigmask(SIGTERM));
- if (debug)
- (void)fprintf(stderr, " [wait(%x) = %u]\n", status, pid);
-
- /* Lookup `pid' in our table */
- if ((tcp = findtcb(pid)) == NULLTCB) {
- (void)fprintf(stderr, "Unknown pid: %u\n", pid);
- if (WIFSTOPPED(status))
- (void)ptrace(PTRACE_CONT, pid, (char *)1, 0);
- continue;
- }
- /* set current output file */
- outf = tcp->outf;
-
- if (tcp->flags & TCB_SUSPENDED) {
- /*
- * Apparently, doing any ptrace() call on a stopped
- * process, provokes the kernel to report the process
- * status again on a subsequent wait(), even if the
- * process has not been actually restarted.
- * Since we have inspected the arguments of suspended
- * processes we end up here testing for this case.
- */
- continue;
- }
- if (WIFSIGNALED(status)) {
- tprintf(
- " + [%s]\n", signals[WTERMSIG(status)]);
- droptcb(tcp);
- continue;
- }
- if(WIFEXITED(status)) {
- if (debug) (void)fprintf(stderr,
- "pid %u exited\n", pid);
- if (tcp->flags & TCB_ATTACHED)
- (void)fprintf(stderr, "PANIC: attached pid %u exited\n", pid);
- droptcb(tcp);
- continue;
- }
- if (! WIFSTOPPED(status)) {
- (void)fprintf(stderr,
- "PANIC: pid %u not stopped\n", pid);
- droptcb(tcp);
- continue;
- }
- if (debug)
- (void)fprintf(stderr, "pid %u stopped, [%s]\n",
- pid, signals[WSTOPSIG(status)]);
-
- if (tcp->flags & TCB_STARTUP) {
- /*
- * This flag is there to keep us in sync
- * Next time this process stops it should
- * really be entering a system call
- */
- tcp->flags &= ~TCB_STARTUP;
- if (tcp->flags & TCB_ATTACHED) {
- if (WSTOPSIG(status) != SIGSTOP) {
- (void)fprintf(stderr,
- "pid %u not stopped\n", pid);
- (void)detach(tcp, (int)WSTOPSIG(status));
- continue;
- }
- } else {
- /* A child of us stopped at exec */
- if (WSTOPSIG(status) == SIGTRAP && followvfork)
- (void)fixvfork(pid);
- }
- if (tcp->flags & TCB_BPTSET) {
- if (clearbpt(tcp) < 0) /* Pretty fatal */ {
- droptcb(tcp);
- cleanup();
- return -1;
- }
- }
- goto tracing;
- }
- if (WSTOPSIG(status) != SIGTRAP) {
- if (WSTOPSIG(status) == SIGSTOP &&
- (tcp->flags & TCB_SIGTRAPPED)) {
- /*
- * Trapped attempt to block SIGTRAP
- * Hope we are back in control now.
- */
- tcp->flags &= ~(TCB_INSYSCALL | TCB_SIGTRAPPED);
- if (ptrace(PTRACE_SYSCALL,
- pid, (char *)1, 0) < 0) {
- perror("trace: ptrace(PTRACE_SYSCALL)");
- cleanup();
- return -1;
- }
- continue;
- }
- if (Nproc > 1)
- tprintf(" -> %u", pid);
- tprintf(" - [%s]\n", signals[WSTOPSIG(status)]);
- if ((tcp->flags & TCB_ATTACHED) &&
- !sigishandled(tcp->pid, WSTOPSIG(status))) {
- if (detach(tcp, (int)WSTOPSIG(status)) < 0);
- continue;
- }
- if (ptrace(PTRACE_SYSCALL, pid, (char *)1,
- (int)WSTOPSIG(status)) < 0) {
- perror("trace: ptrace(PTRACE_SYSCALL)");
- cleanup();
- return -1;
- }
- tcp->flags &= ~(TCB_INSYSCALL | TCB_SUSPENDED);
- continue;
- }
- if (syscall(tcp) < 0) {
- if (tcp->flags & TCB_ATTACHED) {
- if (detach(tcp, 0) < 0);
- } else {
- (void)ptrace(PTRACE_KILL,
- tcp->pid, (char *)1, SIGTERM);
- droptcb(tcp);
- }
- continue;
- }
- if (tcp->flags & TCB_EXITING) {
- if (tcp->flags & TCB_ATTACHED) {
- if (detach(tcp, 0) < 0);
- } else {
- if (ptrace(PTRACE_CONT, pid, (char *)1, 0) < 0) {
- perror("trace: ptrace(PTRACE_CONT)");
- cleanup();
- return -1;
- }
- }
- continue;
- }
- if (tcp->flags & TCB_SUSPENDED) {
- (void)fprintf(stderr, "\nProcess %u suspended\n", pid);
- continue;
- }
- tracing:
- if (ptrace(PTRACE_SYSCALL, pid, (char *)1, 0) < 0) {
- perror("trace: ptrace(PTRACE_SYSCALL)");
- cleanup();
- return -1;
- }
- }
- return 0;
- }
-