home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / sun / volume3 / strace / part02 / strace.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-03-02  |  9.2 KB  |  461 lines

  1. /*
  2.  * @(#)strace.c    2.5 92/01/10
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <signal.h>
  8. #include <errno.h>
  9. #include <sys/types.h>
  10. #include <sys/varargs.h>
  11. #include <sys/param.h>
  12. #include <sys/file.h>
  13. #include <sys/ptrace.h>
  14. #include <sys/wait.h>
  15.  
  16. #include "defs.h"
  17.  
  18. int debug, verbose, followfork, followvfork, tflag, cflag;
  19. struct tcb tcbtab[MAXPROC];
  20. int Nproc;
  21.  
  22. static FILE *outf;
  23. static char *outfname;
  24.  
  25. static struct tcb *findtcb();
  26. static void cleanup();
  27.  
  28. extern void exit();
  29.  
  30. int
  31. main(argc, argv)
  32. int argc;
  33. char *argv[];
  34. {
  35.     extern int optind;
  36.     extern char *optarg;
  37.     struct tcb *tcp;
  38.     int c, pid = 0;
  39.  
  40.     outf = stderr; /* default output is a standard error */
  41.     while ((c = getopt(argc, argv, "dvo:p:s:fFtc")) != EOF) {
  42.         switch (c) {
  43.         case 'd':
  44.             debug++;
  45.             break;
  46.         case 'v':
  47.             verbose++;
  48.             break;
  49.         case 'F':
  50.             followvfork++;
  51.             /* `-F' implies `-f' */
  52.             /*FALLTHROUGH*/
  53.         case 'f':
  54.             followfork++;
  55. #ifdef f_IMPLIES_F
  56.             followvfork++;
  57. #endif
  58.             break;
  59.         case 't':
  60.             tflag++;
  61.             break;
  62.         case 's':
  63.             max_str_len = (unsigned int)atoi(optarg);
  64.             break;
  65.         case 'o':
  66.             if ((outf = fopen(outfname = strdup(optarg), "w")) == NULL)
  67.                 perror("strace: fopen"), exit(1);
  68.             break;
  69.         case 'p':
  70.             if ((pid = atoi(optarg)) == 0) {
  71.                 (void)fprintf(stderr, "Invalid process id\n");
  72.                 break;
  73.             }
  74.             if ((tcp = alloctcb(pid)) == NULLTCB) {
  75.                 (void)fprintf(stderr, "tcb table full\n");
  76.                 exit(1);
  77.             }
  78.             tcp->flags |= TCB_ATTACHED;
  79.             break;
  80.         case 'c':
  81.             cflag++;
  82.             break;
  83.         default:
  84.         usage:
  85.             (void)fprintf(stderr,
  86.     "Usage: %s [-d][-t][-f][-s strsize][-o file] -p pid | command\n", argv[0]);
  87.             exit(1);
  88.             break;
  89.         }
  90.     }
  91.  
  92.     (void)signal(SIGINT, cleanup);
  93.     (void)signal(SIGTERM, cleanup);
  94.     (void)signal(SIGTTOU, SIG_IGN);
  95.     (void)signal(SIGTTIN, SIG_IGN);
  96.  
  97.     for (c = 0, tcp = tcbtab; c < MAXPROC; c++, tcp++) {
  98.         if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))
  99.             continue;
  100.         if (ptrace(PTRACE_ATTACH, tcp->pid, (char *)1, 0) < 0) {
  101.             perror("attach: ptrace(PTRACE_ATTACH)");
  102.             droptcb(tcp);
  103.             continue;
  104.         }
  105.         (void)fprintf(stderr,
  106.             "Process %u attached - interrupt to quit\n", pid);
  107.     }
  108.  
  109.     if (optind < argc) {
  110.         switch (pid = fork()) {
  111.         case -1:
  112.             perror("strace: fork");
  113.             cleanup();
  114.             exit(1);
  115.             break;
  116.         case 0: {
  117.             if (ptrace(PTRACE_TRACEME, 0, (char *)1, 0) < 0) {
  118.                 perror("trace: ptrace(PTRACE_TRACEME)");
  119.                 return -1;
  120.             }
  121.             if (debug) (void)kill(getpid(), SIGSTOP);
  122.             (void)execvp(argv[optind], &argv[optind]);
  123.             if (errno == ENOENT)
  124.                 (void)fprintf(stderr, "%s: Command not found\n",
  125.                             argv[optind]);
  126.             else
  127.                 perror("strace: exec");
  128.             _exit(1);
  129.             break;
  130.         }
  131.         default:
  132.             if (alloctcb(pid) == NULLTCB) {
  133.                 (void)fprintf(stderr, "tcb table full\n");
  134.                 cleanup();
  135.                 exit(1);
  136.             }
  137.             break;
  138.         }
  139.     } else
  140.         if (Nproc == 0) /* No valid arguments */ goto usage;
  141.  
  142.     if (trace() < 0)
  143.         exit(1);
  144.     return 0;
  145. }
  146.  
  147. struct tcb *
  148. alloctcb(pid)
  149. int pid;
  150. {
  151.     int i;
  152.     struct tcb *tcp;
  153.  
  154.     for (i = 0, tcp = tcbtab; i < MAXPROC; i++, tcp++) {
  155.         if ((tcp->flags & TCB_INUSE) == 0) {
  156.             tcp->pid = pid;
  157.             tcp->parent = NULLTCB;
  158.             tcp->nchildren = 0;
  159.             tcp->flags = TCB_INUSE | TCB_STARTUP;
  160.             tcp->outf = outf; /* Initialise to current out file */
  161.             Nproc++;
  162.             return tcp;
  163.         }
  164.     }
  165.     return NULLTCB;
  166. }
  167.  
  168. static struct tcb *
  169. findtcb(pid)
  170. int pid;
  171. {
  172.     int i;
  173.     struct tcb *tcp;
  174.  
  175.     for (i = 0, tcp = tcbtab; i < MAXPROC; i++, tcp++) {
  176.         if ((tcp->pid == pid) && (tcp->flags & TCB_INUSE))
  177.             return tcp;
  178.     }
  179.     return NULLTCB;
  180. }
  181.  
  182. void
  183. droptcb(tcp)
  184. struct tcb *tcp;
  185. {
  186.     if (tcp->pid == 0)
  187.         return;
  188.     tcp->pid = 0;
  189.     tcp->flags = 0;
  190.     if (tcp->parent != NULLTCB) {
  191.         tcp->parent->nchildren--;
  192.         tcp->parent = NULLTCB;
  193.     }
  194. #if 0
  195.     if (tcp->outf != stderr) fclose(tcp->outf);
  196. #endif
  197.     tcp->outf = stderr;
  198.     Nproc--;
  199. }
  200.  
  201. static int
  202. resume(tcp)
  203. struct tcb *tcp;
  204. {
  205.     if (tcp == NULLTCB)
  206.         return -1;
  207.  
  208.     if (!(tcp->flags & TCB_SUSPENDED)) {
  209.         (void)fprintf(stderr, "PANIC: pid %u not suspended\n", tcp->pid);
  210.         return -1;
  211.     }
  212.     tcp->flags &= ~TCB_SUSPENDED;
  213.  
  214.     if (ptrace(PTRACE_SYSCALL, tcp->pid, (char *)1, 0) < 0) {
  215.         perror("resume: ptrace(PTRACE_SYSCALL)");
  216.         return -1;
  217.     }
  218.  
  219.     (void)fprintf(stderr, "Process %u resumed\n", tcp->pid);
  220.     return 0;
  221. }
  222.  
  223. static int
  224. detach(tcp, sig)
  225. struct tcb *tcp;
  226. {
  227.     /* detach traced process; continue with sig */
  228.     int error;
  229.  
  230.     if (tcp->flags & TCB_BPTSET)
  231.         sig = SIGKILL;
  232.  
  233.     /* PTRACE_DETACH won't respect `sig' argument, so we post it here */
  234.     if (sig)
  235.         if (kill(tcp->pid, sig) < 0)
  236.             perror("detach: kill");
  237.  
  238.     if ((error = ptrace(PTRACE_DETACH, tcp->pid, (char *)1, sig)) < 0) {
  239.         perror("detach: ptrace(PTRACE_DETACH)");
  240.     }
  241.  
  242.     (void)fprintf(stderr, "\nProcess %u detached\n", tcp->pid);
  243.  
  244.     if (waiting_parent(tcp))
  245.         error = resume(tcp->parent);
  246.     droptcb(tcp);
  247.     return error;
  248. }
  249.  
  250. void
  251. newoutf(tcp)
  252. struct tcb *tcp;
  253. {
  254.     char name[MAXPATHLEN];
  255.     FILE *fp;
  256.  
  257.     if (outfname) {
  258.         (void)sprintf(name, "%s.%u", outfname, tcp->pid);
  259.         if ((fp = fopen(name, "w")) == NULL) {
  260.             perror("fopen");
  261.             return;
  262.         }
  263.         tcp->outf = fp;
  264.     }
  265.     return;
  266. }
  267.  
  268. void
  269. tprintf(fmt, va_alist)
  270. char *fmt;
  271. va_dcl
  272. {
  273.     va_list args;
  274.  
  275.     va_start(args);
  276.     (void)vfprintf(outf, fmt, args);
  277.     va_end(args);
  278.     return;
  279. }
  280.  
  281. static void
  282. cleanup()
  283. {
  284.     int i;
  285.     struct tcb *tcp;
  286.  
  287.     for (i = 0, tcp = tcbtab; i < MAXPROC; i++, tcp++) {
  288.         if (!(tcp->flags & TCB_INUSE))
  289.             continue;
  290.         if (debug)
  291.             (void)fprintf(stderr,
  292.                 "cleanup: looking at pid %u\n", tcp->pid);
  293.         if (tcp->flags & TCB_ATTACHED) {
  294.             (void)detach(tcp, 0);
  295.         } else {
  296.             (void)kill(tcp->pid, SIGTERM);
  297.         }
  298.     }
  299. }
  300.  
  301. int
  302. trace()
  303. {
  304.     int pid;
  305.     int status;
  306.     struct tcb *tcp;
  307.  
  308.     while (1) {
  309.         (void)sigsetmask(0);
  310.         if ((pid = wait(&status)) == -1) {
  311.             if (Nproc == 0)
  312.                 break;
  313.             perror("trace: wait");
  314.             return -1;
  315.         }
  316.         (void)sigblock(sigmask(SIGINT)|sigmask(SIGTERM));
  317.         if (debug)
  318.             (void)fprintf(stderr, " [wait(%x) = %u]\n", status, pid);
  319.  
  320.         /* Lookup `pid' in our table */
  321.         if ((tcp = findtcb(pid)) == NULLTCB) {
  322.             (void)fprintf(stderr, "Unknown pid: %u\n", pid);
  323.             if (WIFSTOPPED(status))
  324.                 (void)ptrace(PTRACE_CONT, pid, (char *)1, 0);
  325.             continue;
  326.         }
  327.         /* set current output file */
  328.         outf = tcp->outf;
  329.  
  330.         if (tcp->flags & TCB_SUSPENDED) {
  331.             /*
  332.              * Apparently, doing any ptrace() call on a stopped
  333.              * process, provokes the kernel to report the process
  334.              * status again on a subsequent wait(), even if the
  335.              * process has not been actually restarted.
  336.              * Since we have inspected the arguments of suspended
  337.              * processes we end up here testing for this case.
  338.              */
  339.             continue;
  340.         }
  341.         if (WIFSIGNALED(status)) {
  342.             tprintf(
  343.                 " + [%s]\n", signals[WTERMSIG(status)]);
  344.             droptcb(tcp);
  345.             continue;
  346.         }
  347.         if(WIFEXITED(status)) {
  348.             if (debug) (void)fprintf(stderr,
  349.                     "pid %u exited\n", pid);
  350.             if (tcp->flags & TCB_ATTACHED)
  351.                 (void)fprintf(stderr, "PANIC: attached pid %u exited\n", pid);
  352.             droptcb(tcp);
  353.             continue;
  354.         }
  355.         if (! WIFSTOPPED(status)) {
  356.             (void)fprintf(stderr,
  357.                 "PANIC: pid %u not stopped\n", pid);
  358.             droptcb(tcp);
  359.             continue;
  360.         }
  361.         if (debug)
  362.             (void)fprintf(stderr, "pid %u stopped, [%s]\n",
  363.                     pid, signals[WSTOPSIG(status)]);
  364.  
  365.         if (tcp->flags & TCB_STARTUP) {
  366.             /*
  367.              * This flag is there to keep us in sync
  368.              * Next time this process stops it should
  369.              * really be entering a system call
  370.              */
  371.             tcp->flags &= ~TCB_STARTUP;
  372.             if (tcp->flags & TCB_ATTACHED) {
  373.                 if (WSTOPSIG(status) != SIGSTOP) {
  374.                     (void)fprintf(stderr,
  375.                         "pid %u not stopped\n", pid);
  376.                     (void)detach(tcp, (int)WSTOPSIG(status));
  377.                     continue;
  378.                 }
  379.             } else {
  380.                 /* A child of us stopped at exec */
  381.                 if (WSTOPSIG(status) == SIGTRAP && followvfork)
  382.                     (void)fixvfork(pid);
  383.             }
  384.             if (tcp->flags & TCB_BPTSET) {
  385.                 if (clearbpt(tcp) < 0) /* Pretty fatal */ {
  386.                     droptcb(tcp);
  387.                     cleanup();
  388.                     return -1;
  389.                 }
  390.             }
  391.             goto tracing;
  392.         }
  393.         if (WSTOPSIG(status) != SIGTRAP) {
  394.             if (WSTOPSIG(status) == SIGSTOP &&
  395.                     (tcp->flags & TCB_SIGTRAPPED)) {
  396.                 /*
  397.                  * Trapped attempt to block SIGTRAP
  398.                  * Hope we are back in control now.
  399.                  */
  400.                 tcp->flags &= ~(TCB_INSYSCALL | TCB_SIGTRAPPED);
  401.                 if (ptrace(PTRACE_SYSCALL,
  402.                         pid, (char *)1, 0) < 0) {
  403.                     perror("trace: ptrace(PTRACE_SYSCALL)");
  404.                     cleanup();
  405.                     return -1;
  406.                 }
  407.                 continue;
  408.             }
  409.             if (Nproc > 1)
  410.                 tprintf(" -> %u", pid);
  411.             tprintf(" - [%s]\n", signals[WSTOPSIG(status)]);
  412.             if ((tcp->flags & TCB_ATTACHED) &&
  413.                 !sigishandled(tcp->pid, WSTOPSIG(status))) {
  414.                 if (detach(tcp, (int)WSTOPSIG(status)) < 0);
  415.                 continue;
  416.             }
  417.             if (ptrace(PTRACE_SYSCALL, pid, (char *)1,
  418.                         (int)WSTOPSIG(status)) < 0) {
  419.                 perror("trace: ptrace(PTRACE_SYSCALL)");
  420.                 cleanup();
  421.                 return -1;
  422.             }
  423.             tcp->flags &= ~(TCB_INSYSCALL | TCB_SUSPENDED);
  424.             continue;
  425.         }
  426.         if (syscall(tcp) < 0) {
  427.             if (tcp->flags & TCB_ATTACHED) {
  428.                 if (detach(tcp, 0) < 0);
  429.             } else {
  430.                 (void)ptrace(PTRACE_KILL,
  431.                     tcp->pid, (char *)1, SIGTERM);
  432.                 droptcb(tcp);
  433.             }
  434.             continue;
  435.         }
  436.         if (tcp->flags & TCB_EXITING) {
  437.             if (tcp->flags & TCB_ATTACHED) {
  438.                 if (detach(tcp, 0) < 0);
  439.             } else {
  440.                 if (ptrace(PTRACE_CONT, pid, (char *)1, 0) < 0) {
  441.                     perror("trace: ptrace(PTRACE_CONT)");
  442.                     cleanup();
  443.                     return -1;
  444.                 }
  445.             }
  446.             continue;
  447.         }
  448.         if (tcp->flags & TCB_SUSPENDED) {
  449.             (void)fprintf(stderr, "\nProcess %u suspended\n", pid);
  450.             continue;
  451.         }
  452. tracing:
  453.         if (ptrace(PTRACE_SYSCALL, pid, (char *)1, 0) < 0) {
  454.             perror("trace: ptrace(PTRACE_SYSCALL)");
  455.             cleanup();
  456.             return -1;
  457.         }
  458.     }
  459.     return 0;
  460. }
  461.