// watch -n 0.5 'dot -Tpng graph.dot > graph.png && display graph.png'
#include <libunwind.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdatomic.h>
#define MAX_FRAMES 32
#define MAX_EDGES 2048
#define SAMPLE_INTERVAL_US 100000
typedef struct {
char caller[256];
char callee[256];
unsigned long hits;
} Edge;
static Edge edges[MAX_EDGES];
static int edge_count = 0;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static atomic_int stop = 0;
// Merge edge or create new with hit count
static void add_edge(const char *a, const char *b) {
pthread_mutex_lock(&mtx);
for (int i = 0; i < edge_count; i++) {
if (!strcmp(edges[i].caller, a) && !strcmp(edges[i].callee, b)) {
edges[i].hits++;
pthread_mutex_unlock(&mtx);
return;
}
}
if (edge_count < MAX_EDGES) {
strncpy(edges[edge_count].caller, a, 255);
strncpy(edges[edge_count].callee, b, 255);
edges[edge_count].hits = 1;
edge_count++;
}
pthread_mutex_unlock(&mtx);
}
// Background thread: periodically captures stack
static void *sampler(void *arg) {
(void)arg;
while (!atomic_load(&stop)) {
unw_context_t ctx;
unw_cursor_t cur;
unw_getcontext(&ctx);
unw_init_local(&cur, &ctx);
char stack[MAX_FRAMES][256];
int n = 0;
while (unw_step(&cur) > 0 && n < MAX_FRAMES) {
unw_word_t ip;
unw_get_reg(&cur, UNW_REG_IP, &ip);
stack[n][0] = '\0';
if (unw_get_proc_name(&cur, stack[n], 256, NULL) != 0)
snprintf(stack[n], 256, "0x%lx", (unsigned long)ip);
n++;
}
// stack[0] = sampler, stack[1] = caller, ...
for (int i = 1; i < n; i++)
add_edge(stack[i], stack[i-1]);
usleep(SAMPLE_INTERVAL_US);
}
return NULL;
}
static void handler(int sig) {
(void)sig;
atomic_store(&stop, 1);
}
void print_graph(void) {
pthread_mutex_lock(&mtx);
printf("digraph call_graph {\n");
for (int i = 0; i < edge_count; i++)
printf(" \"%s\" -> \"%s\" [label=\"%lu\"];\n",
edges[i].caller, edges[i].callee, edges[i].hits);
printf("}\n");
pthread_mutex_unlock(&mtx);
}
void workload(void) {
volatile unsigned long x = 0;
for (;;) { x += 1; if (x % 1000000 == 0) usleep(1000); }
}
int main(void) {
struct sigaction sa = {.sa_handler = handler};
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
pthread_t tid;
pthread_create(&tid, NULL, sampler, NULL);
workload();
pthread_join(tid, NULL);
print_graph();
return 0;
}