callgraph

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

commit efedcd4b641ea255f9d37ca0f34c27d259ac0bdd
parent 9860888f568e593da2f67c6db00c0d8c01e785de
Author: lostd <lostd@2f30.org>
Date:   Thu, 14 Nov 2013 21:49:40 +0200

Proper name for a library

Diffstat:
Alibcallgraph.c | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 198 insertions(+), 0 deletions(-)

diff --git a/libcallgraph.c b/libcallgraph.c @@ -0,0 +1,198 @@ +/* runtime callgraph generator by stateless */ + +#include <stdio.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <inttypes.h> +#include <assert.h> + +struct fnode_t { + uintptr_t addr; + struct fnode_t *next; +}; + +struct fpair_t { + uintptr_t l, r; /* l is the caller, r is the callee */ + struct fpair_t *next; +}; + +/* essentially a stack of function calls */ +static struct fnode_t *fnode_head = NULL; +static struct fnode_t *fnode_tail = NULL; + +/* a list of function pairs (caller -> callee) */ +static struct fpair_t *fpair_head = NULL; +static struct fpair_t *fpair_tail = NULL; + +static int fd = -1; +static char logbuf[BUFSIZ]; + +static void * +xmalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) + _Exit(EXIT_FAILURE); + return p; +} + +static ssize_t +writelog(int fildes, const void *buf, size_t nbyte) +{ + ssize_t w, r, tmp; + + w = 0; + r = nbyte; + if (r < 0) return 0; + do { +again: + tmp = write(fildes, (const char *)buf + w, r); + if (tmp <= 0) { + if (tmp) + if (errno == EINTR) + goto again; + return tmp; + } + w += tmp; + r -= tmp; + } while (r > 0); + return w; +} + +__attribute__ ((constructor)) static void +init_logging(void) +{ + char *f; + + f = getenv("CGRAPH_OUT"); + if (!f) { + fprintf(stderr, "cgraph: CGRAPH_OUT is not set!\n"); + _Exit(EXIT_FAILURE); + } + + fd = open(f, O_WRONLY | O_CREAT, 0644); + if (fd < 0 || ftruncate(fd, 0) < 0) { + perror("cgraph"); + _Exit(EXIT_FAILURE); + } +} + +__attribute__ ((destructor)) static void +deinit_logging(void) +{ + struct fpair_t *p, *tmppair; + struct fnode_t *n, *tmpnode; + + writelog(fd, "digraph G {\n", + strlen("digraph G {\n")); + p = fpair_head; + while (p) { + snprintf(logbuf, sizeof logbuf, + "\t \"%"PRIxPTR"\" -> \"%"PRIxPTR"\"\n", p->l, p->r); + writelog(fd, logbuf, strlen(logbuf)); + p = p->next; + } + writelog(fd, "}\n", 2); + + if (fd != -1) + if (close(fd) < 0) + perror("cgraph"); + + p = fpair_head; + while (p) { + tmppair = p->next; + free(p); + p = tmppair; + } + n = fnode_head; + while (n) { + tmpnode = n->next; + free(n); + n = tmpnode; + } +} + +static void +push_fnode(uintptr_t addr) +{ + if (!fnode_head) { + fnode_head = xmalloc(sizeof(struct fnode_t)); + fnode_head->addr = addr; + fnode_head->next = NULL; + fnode_tail = fnode_head; + } else { + fnode_tail->next = xmalloc(sizeof(struct fnode_t)); + fnode_tail = fnode_tail->next; + fnode_tail->next = NULL; + fnode_tail->addr = addr; + } +} + +static void +pop_fnode(void) +{ + struct fnode_t *i; + + assert(fnode_tail); + for (i = fnode_head; i; i = i->next) + if (i->next == fnode_tail) + break; + if (!i) return; + free(fnode_tail); + fnode_tail = i; + fnode_tail->next = NULL; +} + +static inline struct fnode_t * +get_top(void) { + return fnode_tail; +} + +static void +add_fpair(uintptr_t r) +{ + struct fnode_t *curfun; + + curfun = get_top(); + if (!fpair_head) { + fpair_head = xmalloc(sizeof(struct fpair_t)); + fpair_head->l = curfun->addr; + fpair_head->r = r; + fpair_head->next = NULL; + fpair_tail = fpair_head; + } else { + fpair_tail->next = xmalloc(sizeof(struct fpair_t)); + fpair_tail = fpair_tail->next; + fpair_tail->l = curfun->addr; + fpair_tail->r = r; + fpair_tail->next = NULL; + } +} + +void +__cyg_profile_func_enter(void *func_address, + __attribute__ ((unused)) void *call_site) +{ + struct fnode_t *n; + + n = get_top(); + if (n) + add_fpair((uintptr_t)func_address); + push_fnode((uintptr_t)func_address); +} + +void +__cyg_profile_func_exit ( + __attribute__ ((unused)) void *func_address, + __attribute__ ((unused)) void *call_site) +{ + pop_fnode(); +} +