callgraph

runtime callgraph generator
git clone git://git.2f30.org/callgraph
Log | Files | Refs | README | LICENSE

libcallgraph.c (4125B)


      1 #include <stdio.h>
      2 #include <sys/stat.h>
      3 #include <fcntl.h>
      4 #include <stdlib.h>
      5 #include <unistd.h>
      6 #include <errno.h>
      7 #include <string.h>
      8 #include <stdint.h>
      9 #include <inttypes.h>
     10 #include <assert.h>
     11 
     12 struct fnode_t {
     13      uintptr_t addr;
     14      struct fnode_t *next;
     15 };
     16 
     17 struct fpair_t {
     18      uintptr_t l, r; /* l is the caller, r is the callee */
     19      struct fpair_t *next;
     20 };
     21 
     22 /* essentially a stack of function calls */
     23 static struct fnode_t *fnode_head = NULL;
     24 static struct fnode_t *fnode_tail = NULL;
     25 
     26 /* a list of function pairs (caller -> callee) */
     27 static struct fpair_t *fpair_head = NULL;
     28 static struct fpair_t *fpair_tail = NULL;
     29 
     30 static int fd = -1;
     31 static char logbuf[BUFSIZ];
     32 
     33 static void *
     34 xmalloc(size_t size)
     35 {
     36      void *p;
     37 
     38      p = malloc(size);
     39      if (!p)
     40           _Exit(EXIT_FAILURE);
     41      return p;
     42 }
     43 
     44 static ssize_t
     45 writelog(int fildes, const void *buf, size_t nbyte)
     46 {
     47      ssize_t w, r, tmp;
     48 
     49      w = 0;
     50      r = nbyte;
     51      do {
     52           tmp = write(fildes, (const char *)buf + w, r);
     53           if (tmp <= 0) {
     54                if (tmp)
     55                     if (errno == EINTR)
     56                          continue;
     57                return tmp;
     58           }
     59           w += tmp;
     60           r -= tmp;
     61      } while (r > 0);
     62      return w;
     63 }
     64 
     65 __attribute__ ((constructor)) static void
     66 init_logging(void)
     67 {
     68      char *f;
     69 
     70      f = getenv("CGRAPH_OUT");
     71      if (f == NULL)
     72           f = "callgraph.dot";
     73      fd = open(f, O_WRONLY | O_CREAT | O_EXCL, 0644);
     74      if (fd == -1) {
     75           fprintf(stderr, "cgraph %s: %s\n", f, strerror(errno));
     76           _Exit(EXIT_FAILURE);
     77      }
     78 }
     79 
     80 __attribute__ ((destructor)) static void
     81 deinit_logging(void)
     82 {
     83      struct fpair_t *p, *tmppair;
     84      struct fnode_t *n, *tmpnode;
     85 
     86      writelog(fd, "digraph G {\n",
     87               strlen("digraph G {\n"));
     88      p = fpair_head;
     89      while (p) {
     90           snprintf(logbuf, sizeof logbuf,
     91                    "\t \"%"PRIxPTR"\" -> \"%"PRIxPTR"\"\n", p->l, p->r);
     92           writelog(fd, logbuf, strlen(logbuf));
     93           p = p->next;
     94      }
     95      writelog(fd, "}\n", 2);
     96 
     97      if (fd != -1)
     98           if (close(fd) < 0)
     99                perror("cgraph");
    100 
    101      p = fpair_head;
    102      while (p) {
    103           tmppair = p->next;
    104           free(p);
    105           p = tmppair;
    106      }
    107      n = fnode_head;
    108      while (n) {
    109           tmpnode = n->next;
    110           free(n);
    111           n = tmpnode;
    112      }
    113 }
    114 
    115 static void
    116 push_fnode(uintptr_t addr)
    117 {
    118      if (!fnode_head) {
    119           fnode_head = xmalloc(sizeof(struct fnode_t));
    120           fnode_head->addr = addr;
    121           fnode_head->next = NULL;
    122           fnode_tail = fnode_head;
    123      } else {
    124           fnode_tail->next = xmalloc(sizeof(struct fnode_t));
    125           fnode_tail = fnode_tail->next;
    126           fnode_tail->next = NULL;
    127           fnode_tail->addr = addr;
    128      }
    129 }
    130 
    131 static void
    132 pop_fnode(void)
    133 {
    134      struct fnode_t *i;
    135 
    136      assert(fnode_tail);
    137      for (i = fnode_head; i; i = i->next)
    138           if (i->next == fnode_tail)
    139                break;
    140      if (!i) return;
    141      free(fnode_tail);
    142      fnode_tail = i;
    143      fnode_tail->next = NULL;
    144 }
    145 
    146 static inline struct fnode_t *
    147 get_top(void) {
    148      return fnode_tail;
    149 }
    150 
    151 static void
    152 add_fpair(uintptr_t r)
    153 {
    154      struct fnode_t *curfun;
    155 
    156      curfun = get_top();
    157      if (!fpair_head) {
    158           fpair_head = xmalloc(sizeof(struct fpair_t));
    159           fpair_head->l = curfun->addr;
    160           fpair_head->r = r;
    161           fpair_head->next = NULL;
    162           fpair_tail = fpair_head;
    163      } else {
    164           fpair_tail->next = xmalloc(sizeof(struct fpair_t));
    165           fpair_tail = fpair_tail->next;
    166           fpair_tail->l = curfun->addr;
    167           fpair_tail->r = r;
    168           fpair_tail->next = NULL;
    169      }
    170 }
    171 
    172 void
    173 __cyg_profile_func_enter(void *func_address,
    174                          __attribute__ ((unused)) void *call_site)
    175 {
    176      struct fnode_t *n;
    177 
    178      n = get_top();
    179      if (n)
    180           add_fpair((uintptr_t)func_address);
    181      push_fnode((uintptr_t)func_address);
    182 }
    183 
    184 void
    185 __cyg_profile_func_exit (
    186      __attribute__ ((unused)) void *func_address,
    187      __attribute__ ((unused)) void *call_site)
    188 {
    189      pop_fnode();
    190 }