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 }