home *** CD-ROM | disk | FTP | other *** search
- /*
- * @(#)strace.c 1.5 91/09/05
- */
-
- #include <stdio.h>
- #include <string.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/file.h>
- #include <sys/ptrace.h>
- #include <sys/wait.h>
-
- #include "defs.h"
-
- int debug, followfork, brutal, tflag;
- FILE *outf;
- struct tcb tcbtab[MAX_PROCS];
- int Nproc;
-
- static char *outfname;
-
- static struct tcb *findtcb();
- static void sighandler();
-
- int
- main(argc, argv)
- int argc;
- char *argv[];
- {
- extern int optind;
- extern char *optarg;
- extern int max_str_len;
- int c, pid = 0;
-
- outf = stderr; /* default output is a standard error */
- while ((c = getopt(argc, argv, "do:p:s:fbt")) != EOF) {
- switch (c) {
- case 'd':
- debug++;
- break;
- case 'f':
- followfork++;
- break;
- case 'b':
- brutal++;
- break;
- case 't':
- tflag++;
- break;
- case 's':
- max_str_len = atoi(optarg);
- break;
- case 'o':
- if ((outf = fopen(outfname = strdup(optarg), "w")) == NULL)
- perror("strace: fopen"), exit(1);
- break;
- case 'p':
- pid = atoi(optarg);
- break;
- default:
- usage:
- fprintf(stderr,
- "Usage: %s [-d][-t][-f][-s strsize][-o file] -p pid | command\n", argv[0]);
- exit(1);
- break;
- }
- }
-
- (void)signal(SIGINT, sighandler);
- if (pid) {
- struct tcb *tcp = alloctcb(pid);
- if (tcp == NULLTCB) {
- fprintf(stderr, "tcb table full\n");
- exit(1);
- }
- if (ptrace(PTRACE_ATTACH, pid, (char *)1, 0) < 0) {
- perror("attach: ptrace(PTRACE_ATTACH)");
- return -1;
- }
- tcp->flags |= TCB_ATTACHED;
- fprintf(stderr,
- "Process %u attached - interrupt to quit\n", pid);
- if (trace() < 0)
- exit(1);
- } else {
- if (optind >= argc) /* No argument */ goto usage;
- switch (pid = fork()) {
- case -1:
- perror("strace: fork");
- 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);
- execvp(argv[optind], &argv[optind]);
- perror("strace: execvp");
- exit(1);
- break;
- }
- default:
- if (alloctcb(pid) == NULLTCB) {
- fprintf(stderr, "tcb table full\n");
- exit(1);
- }
- if (trace() < 0)
- exit(1);
- break;
- }
- }
- return 0;
- }
-
-
- struct tcb *
- alloctcb(pid)
- int pid;
- {
- int i;
- struct tcb *tcp;
-
- for (i = 0, tcp = tcbtab; i < MAX_PROCS; 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 < MAX_PROCS; 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)) {
- fprintf(stderr, "PANIC: pid %u not suspended\n", tcp->pid);
- return -1;
- }
- if (ptrace(PTRACE_SYSCALL, tcp->pid, (char *)1, 0) < 0) {
- perror("trace: ptrace(PTRACE_SYSCALL)");
- return -1;
- }
- tcp->flags &= ~TCB_SUSPENDED;
- fprintf(stderr, "Process %u resumed\n", tcp->pid);
- return 0;
- }
-
- 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;
- }
-
- static void
- sighandler(sig)
- {
- int i;
- struct tcb *tcp;
-
- switch (sig) {
- case SIGINT:
- for (i = 0, tcp = tcbtab; i < MAX_PROCS; i++, tcp++) {
- if ((tcp->flags & TCB_INUSE)) {
- if (debug)
- fprintf(stderr,
- "cntl-C: killing/detaching pid %u\n",
- tcp->pid);
-
- if (tcp->flags & TCB_ATTACHED) {
- if (ptrace(PTRACE_DETACH, tcp->pid,
- (char *)1, 0) < 0)
- perror("ptrace(PTRACE_DETACH,..");
- fprintf(stderr,
- "\nProcess %u detached\n", tcp->pid);
- droptcb(tcp);
- } else {
- if (ptrace(PTRACE_KILL, tcp->pid,
- (char *)1, SIGTERM) < 0)
- perror("ptrace(PTRACE_KILL,..");
- }
- }
- }
- break;
- default:
- if (debug)
- fprintf(stderr, " [[%s]]\n", signals[sig]);
- break;
- }
- }
-
- int
- trace()
- {
- int pid;
- int status;
- struct tcb *tcp;
-
- while (1) {
- if ((pid = wait(&status)) == -1) {
- if (Nproc == 0)
- break;
- perror("trace: wait");
- return -1;
- }
- if (debug)
- fprintf(stderr, " [wait(%x) = %u]\n", status, pid);
-
- /* Lookup `pid' in our table */
- if ((tcp = findtcb(pid)) == NULLTCB) {
- fprintf(stderr, "Unknown pid: %u\n", pid);
- if (WIFSTOPPED(status))
- (void)ptrace(PTRACE_CONT, pid, (char *)1, 0);
- continue;
- }
- /* XXX, this is an afterthought.., set 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)) {
- fprintf(outf,
- " + [%s]\n", signals[WTERMSIG(status)]);
- droptcb(tcp);
- continue;
- }
- if(WIFEXITED(status)) {
- if (debug) fprintf(stderr,
- "pid %u exited\n", pid);
- droptcb(tcp);
- continue;
- }
- if (! WIFSTOPPED(status)) {
- fprintf(stderr,
- "PANIC: pid %u not stopped\n", pid);
- droptcb(tcp);
- continue;
- }
- if (debug)
- 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 0
- if ((tcp->flags & TCB_ATTACHED) &&
- WSTOPSIG(status) == SIGSTOP) {
- }
- #endif
- if (tcp->flags & TCB_BPTSET) {
- clearbpt(tcp);
- }
- goto tracing;
- }
- if (WSTOPSIG(status) != SIGTRAP) {
- if (Nproc > 1)
- fprintf(outf, " -> %u", pid);
- fprintf(outf, " - [%s]\n", signals[WSTOPSIG(status)]);
- if (waiting_parent(tcp) &&
- WSTOPSIG(status) != SIGALRM &&
- WSTOPSIG(status) != SIGSTOP &&
- WSTOPSIG(status) != SIGCONT &&
- WSTOPSIG(status) != SIGCHLD) /* more ? */ {
- (void)ptrace(PTRACE_DETACH, pid, (char *)1,
- (int)WSTOPSIG(status));
- resume(tcp->parent);
- droptcb(tcp);
- }
- if (ptrace(PTRACE_SYSCALL, pid, (char *)1,
- (int)WSTOPSIG(status)) < 0) {
- perror("trace: ptrace(PTRACE_SYSCALL)");
- return -1;
- }
- /* XXX - must we do this for every syscall ? */
- tcp->flags &= ~TCB_INSYSCALL;
- continue;
- }
- if (syscall(tcp) < 0) {
- droptcb(tcp);
- continue;
- }
- if (tcp->flags & TCB_EXITING) {
- if (tcp->flags & TCB_ATTACHED) {
- if (ptrace(PTRACE_DETACH, pid, (char *)1, 0) < 0) {
- perror("trace: ptrace(PTRACE_DETACH)");
- return -1;
- }
- if (waiting_parent(tcp))
- resume(tcp->parent);
- droptcb(tcp);
- } else {
- if (ptrace(PTRACE_CONT, pid, (char *)1, 0) < 0) {
- perror("trace: ptrace(PTRACE_CONT)");
- return -1;
- }
- }
- continue;
- }
- if (tcp->flags & TCB_SUSPENDED) {
- fprintf(stderr, "\nProcess %u suspended\n", pid);
- continue;
- }
- tracing:
- if (ptrace(PTRACE_SYSCALL, pid, (char *)1, 0) < 0) {
- perror("trace: ptrace(PTRACE_SYSCALL)");
- return -1;
- }
- }
- return 0;
- }
-