commit efedcd4b641ea255f9d37ca0f34c27d259ac0bdd
parent 9860888f568e593da2f67c6db00c0d8c01e785de
Author: lostd <lostd@2f30.org>
Date: Thu, 14 Nov 2013 21:49:40 +0200
Proper name for a library
Diffstat:
A | libcallgraph.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();
+}
+