home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3999 / strace.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-09-09  |  7.5 KB  |  380 lines

  1. /*
  2.  * @(#)strace.c    1.5 91/09/05
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <signal.h>
  8. #include <sys/types.h>
  9. #include <sys/param.h>
  10. #include <sys/file.h>
  11. #include <sys/ptrace.h>
  12. #include <sys/wait.h>
  13.  
  14. #include "defs.h"
  15.  
  16. int debug, followfork, brutal, tflag;
  17. FILE *outf;
  18. struct tcb tcbtab[MAX_PROCS];
  19. int Nproc;
  20.  
  21. static char *outfname;
  22.  
  23. static struct tcb *findtcb();
  24. static void sighandler();
  25.  
  26. int
  27. main(argc, argv)
  28. int argc;
  29. char *argv[];
  30. {
  31.     extern int optind;
  32.     extern char *optarg;
  33.     extern int max_str_len;
  34.     int c, pid = 0;
  35.  
  36.     outf = stderr; /* default output is a standard error */
  37.     while ((c = getopt(argc, argv, "do:p:s:fbt")) != EOF) {
  38.         switch (c) {
  39.         case 'd':
  40.             debug++;
  41.             break;
  42.         case 'f':
  43.             followfork++;
  44.             break;
  45.         case 'b':
  46.             brutal++;
  47.             break;
  48.         case 't':
  49.             tflag++;
  50.             break;
  51.         case 's':
  52.             max_str_len = atoi(optarg);
  53.             break;
  54.         case 'o':
  55.             if ((outf = fopen(outfname = strdup(optarg), "w")) == NULL)
  56.                 perror("strace: fopen"), exit(1);
  57.             break;
  58.         case 'p':
  59.             pid = atoi(optarg);
  60.             break;
  61.         default:
  62.         usage:
  63.             fprintf(stderr,
  64.     "Usage: %s [-d][-t][-f][-s strsize][-o file] -p pid | command\n", argv[0]);
  65.             exit(1);
  66.             break;
  67.         }
  68.     }
  69.  
  70.     (void)signal(SIGINT, sighandler);
  71.     if (pid) {
  72.         struct tcb *tcp = alloctcb(pid);
  73.         if (tcp == NULLTCB) {
  74.             fprintf(stderr, "tcb table full\n");
  75.             exit(1);
  76.         }
  77.         if (ptrace(PTRACE_ATTACH, pid, (char *)1, 0) < 0) {
  78.             perror("attach: ptrace(PTRACE_ATTACH)");
  79.             return -1;
  80.         }
  81.         tcp->flags |= TCB_ATTACHED;
  82.         fprintf(stderr,
  83.             "Process %u attached - interrupt to quit\n", pid);
  84.         if (trace() < 0)
  85.             exit(1);
  86.     } else {
  87.         if (optind >= argc) /* No argument */ goto usage;
  88.         switch (pid = fork()) {
  89.         case -1:
  90.             perror("strace: fork");
  91.             exit(1);
  92.             break;
  93.         case 0: {
  94.             if (ptrace(PTRACE_TRACEME, 0, (char *)1, 0) < 0) {
  95.                 perror("trace: ptrace(PTRACE_TRACEME)");
  96.                 return -1;
  97.             }
  98.             if (debug) (void)kill(getpid(), SIGSTOP);
  99.             execvp(argv[optind], &argv[optind]);
  100.             perror("strace: execvp");
  101.             exit(1);
  102.             break;
  103.         }
  104.         default:
  105.             if (alloctcb(pid) == NULLTCB) {
  106.                 fprintf(stderr, "tcb table full\n");
  107.                 exit(1);
  108.             }
  109.             if (trace() < 0)
  110.                 exit(1);
  111.             break;
  112.         }
  113.     }
  114.     return 0;
  115. }
  116.  
  117.  
  118. struct tcb *
  119. alloctcb(pid)
  120. int pid;
  121. {
  122.     int i;
  123.     struct tcb *tcp;
  124.  
  125.     for (i = 0, tcp = tcbtab; i < MAX_PROCS; i++, tcp++) {
  126.         if ((tcp->flags & TCB_INUSE) == 0) {
  127.             tcp->pid = pid;
  128.             tcp->parent = NULLTCB;
  129.             tcp->nchildren = 0;
  130.             tcp->flags = TCB_INUSE | TCB_STARTUP;
  131.             tcp->outf = outf; /* Initialise to current out file */
  132.             Nproc++;
  133.             return tcp;
  134.         }
  135.     }
  136.     return NULLTCB;
  137. }
  138.  
  139. static struct tcb *
  140. findtcb(pid)
  141. int pid;
  142. {
  143.     int i;
  144.     struct tcb *tcp;
  145.  
  146.     for (i = 0, tcp = tcbtab; i < MAX_PROCS; i++, tcp++) {
  147.         if ((tcp->pid == pid) && (tcp->flags & TCB_INUSE))
  148.             return tcp;
  149.     }
  150.     return NULLTCB;
  151. }
  152.  
  153. void
  154. droptcb(tcp)
  155. struct tcb *tcp;
  156. {
  157.     if (tcp->pid == 0)
  158.         return;
  159.     tcp->pid = 0;
  160.     tcp->flags = 0;
  161.     if (tcp->parent != NULLTCB) {
  162.         tcp->parent->nchildren--;
  163.         tcp->parent = NULLTCB;
  164.     }
  165. #if 0
  166.     if (tcp->outf != stderr) fclose(tcp->outf);
  167. #endif
  168.     tcp->outf = stderr;
  169.     Nproc--;
  170. }
  171.  
  172. static int
  173. resume(tcp)
  174. struct tcb *tcp;
  175. {
  176.     if (tcp == NULLTCB)
  177.         return -1;
  178.  
  179.     if (!(tcp->flags & TCB_SUSPENDED)) {
  180.         fprintf(stderr, "PANIC: pid %u not suspended\n", tcp->pid);
  181.         return -1;
  182.     }
  183.     if (ptrace(PTRACE_SYSCALL, tcp->pid, (char *)1, 0) < 0) {
  184.         perror("trace: ptrace(PTRACE_SYSCALL)");
  185.         return -1;
  186.     }
  187.     tcp->flags &= ~TCB_SUSPENDED;
  188.     fprintf(stderr, "Process %u resumed\n", tcp->pid);
  189.     return 0;
  190. }
  191.  
  192. void
  193. newoutf(tcp)
  194. struct tcb *tcp;
  195. {
  196.     char name[MAXPATHLEN];
  197.     FILE *fp;
  198.  
  199.     if (outfname) {
  200.         (void)sprintf(name, "%s.%u", outfname, tcp->pid);
  201.         if ((fp = fopen(name, "w")) == NULL) {
  202.             perror("fopen");
  203.             return;
  204.         }
  205.         tcp->outf = fp;
  206.     }
  207.     return;
  208. }
  209.  
  210. static void
  211. sighandler(sig)
  212. {
  213.     int i;
  214.     struct tcb *tcp;
  215.  
  216.     switch (sig) {
  217.     case SIGINT:
  218.         for (i = 0, tcp = tcbtab; i < MAX_PROCS; i++, tcp++) {
  219.             if ((tcp->flags & TCB_INUSE)) {
  220.                 if (debug)
  221.                     fprintf(stderr,
  222.                     "cntl-C: killing/detaching pid %u\n",
  223.                     tcp->pid);
  224.  
  225.                 if (tcp->flags & TCB_ATTACHED) {
  226.                     if (ptrace(PTRACE_DETACH, tcp->pid,
  227.                             (char *)1, 0) < 0)
  228.                         perror("ptrace(PTRACE_DETACH,..");
  229.                     fprintf(stderr,
  230.                     "\nProcess %u detached\n", tcp->pid);
  231.                     droptcb(tcp);
  232.                 } else {
  233.                     if (ptrace(PTRACE_KILL, tcp->pid,
  234.                         (char *)1, SIGTERM) < 0)
  235.                         perror("ptrace(PTRACE_KILL,..");
  236.                 }
  237.             }
  238.         }
  239.         break;
  240.     default:
  241.         if (debug)
  242.             fprintf(stderr, " [[%s]]\n", signals[sig]);
  243.         break;
  244.     }
  245. }
  246.  
  247. int
  248. trace()
  249. {
  250.     int pid;
  251.     int status;
  252.     struct tcb *tcp;
  253.  
  254.     while (1) {
  255.         if ((pid = wait(&status)) == -1) {
  256.             if (Nproc == 0)
  257.                 break;
  258.             perror("trace: wait");
  259.             return -1;
  260.         }
  261.         if (debug)
  262.             fprintf(stderr, " [wait(%x) = %u]\n", status, pid);
  263.  
  264.         /* Lookup `pid' in our table */
  265.         if ((tcp = findtcb(pid)) == NULLTCB) {
  266.             fprintf(stderr, "Unknown pid: %u\n", pid);
  267.             if (WIFSTOPPED(status))
  268.                 (void)ptrace(PTRACE_CONT, pid, (char *)1, 0);
  269.             continue;
  270.         }
  271.         /* XXX, this is an afterthought.., set output file */
  272.         outf = tcp->outf;
  273.  
  274.         if (tcp->flags & TCB_SUSPENDED) {
  275.             /*
  276.              * Apparently, doing any ptrace() call on a stopped
  277.              * process, provokes the kernel to report the process
  278.              * status again on a subsequent wait(), even if the
  279.              * process has not been actually restarted.
  280.              * Since we have inspected the arguments of suspended
  281.              * processes we end up here testing for this case.
  282.              */
  283.             continue;
  284.         }
  285.         if (WIFSIGNALED(status)) {
  286.             fprintf(outf,
  287.                 " + [%s]\n", signals[WTERMSIG(status)]);
  288.             droptcb(tcp);
  289.             continue;
  290.         }
  291.         if(WIFEXITED(status)) {
  292.             if (debug) fprintf(stderr,
  293.                     "pid %u exited\n", pid);
  294.             droptcb(tcp);
  295.             continue;
  296.         }
  297.         if (! WIFSTOPPED(status)) {
  298.             fprintf(stderr,
  299.                 "PANIC: pid %u not stopped\n", pid);
  300.             droptcb(tcp);
  301.             continue;
  302.         }
  303.         if (debug)
  304.             fprintf(stderr, "pid %u stopped, [%s]\n",
  305.                     pid, signals[WSTOPSIG(status)]);
  306.  
  307.         if (tcp->flags & TCB_STARTUP) {
  308.             /*
  309.              * This flag is there to keep us in sync
  310.              * Next time this process stops it should
  311.              * really be entering a system call
  312.              */
  313.             tcp->flags &= ~TCB_STARTUP;
  314. #if 0
  315.             if ((tcp->flags & TCB_ATTACHED) &&
  316.                     WSTOPSIG(status) == SIGSTOP) {
  317.             }
  318. #endif
  319.             if (tcp->flags & TCB_BPTSET) {
  320.                 clearbpt(tcp);
  321.             }
  322.             goto tracing;
  323.         }
  324.         if (WSTOPSIG(status) != SIGTRAP) {
  325.             if (Nproc > 1)
  326.                 fprintf(outf, " -> %u", pid);
  327.             fprintf(outf, " - [%s]\n", signals[WSTOPSIG(status)]);
  328.             if (waiting_parent(tcp) &&
  329.                 WSTOPSIG(status) != SIGALRM &&
  330.                 WSTOPSIG(status) != SIGSTOP &&
  331.                 WSTOPSIG(status) != SIGCONT &&
  332.                 WSTOPSIG(status) != SIGCHLD) /* more ? */ {
  333.                 (void)ptrace(PTRACE_DETACH, pid, (char *)1,
  334.                             (int)WSTOPSIG(status));
  335.                 resume(tcp->parent);
  336.                 droptcb(tcp);
  337.             }
  338.             if (ptrace(PTRACE_SYSCALL, pid, (char *)1,
  339.                         (int)WSTOPSIG(status)) < 0) {
  340.                 perror("trace: ptrace(PTRACE_SYSCALL)");
  341.                 return -1;
  342.             }
  343.             /* XXX - must we do this for every syscall ? */
  344.             tcp->flags &= ~TCB_INSYSCALL;
  345.             continue;
  346.         }
  347.         if (syscall(tcp) < 0) {
  348.             droptcb(tcp);
  349.             continue;
  350.         }
  351.         if (tcp->flags & TCB_EXITING) {
  352.             if (tcp->flags & TCB_ATTACHED) {
  353.                 if (ptrace(PTRACE_DETACH, pid, (char *)1, 0) < 0) {
  354.                     perror("trace: ptrace(PTRACE_DETACH)");
  355.                     return -1;
  356.                 }
  357.                 if (waiting_parent(tcp))
  358.                     resume(tcp->parent);
  359.                 droptcb(tcp);
  360.             } else {
  361.                 if (ptrace(PTRACE_CONT, pid, (char *)1, 0) < 0) {
  362.                     perror("trace: ptrace(PTRACE_CONT)");
  363.                     return -1;
  364.                 }
  365.             }
  366.             continue;
  367.         }
  368.         if (tcp->flags & TCB_SUSPENDED) {
  369.             fprintf(stderr, "\nProcess %u suspended\n", pid);
  370.             continue;
  371.         }
  372. tracing:
  373.         if (ptrace(PTRACE_SYSCALL, pid, (char *)1, 0) < 0) {
  374.             perror("trace: ptrace(PTRACE_SYSCALL)");
  375.             return -1;
  376.         }
  377.     }
  378.     return 0;
  379. }
  380.