#include <libunwind.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdatomic.h>
#define MAX_EDGES 64
#define MAX_NAME 128
#define SAMPLE_US 200000
#define DRAW_US 300000
typedef struct {
char caller[MAX_NAME];
char callee[MAX_NAME];
unsigned long hits;
} Edge;
static Edge edges[MAX_EDGES];
static int cnt = 0;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static atomic_int run = 1;
// Capture stack and merge into shared graph
static void capture(void) {
unw_context_t ctx;
unw_cursor_t cur;
unw_getcontext(&ctx);
unw_init_local(&cur, &ctx);
char stk[16][MAX_NAME];
int n = 0;
while (unw_step(&cur) > 0 && n < 16) {
unw_word_t ip;
unw_get_reg(&cur, UNW_REG_IP, &ip);
stk[n][0] = '\0';
if (unw_get_proc_name(&cur, stk[n], MAX_NAME, NULL) != 0)
snprintf(stk[n], MAX_NAME, "0x%lx", (unsigned long)ip);
n++;
}
pthread_mutex_lock(&lock);
for (int i = 1; i < n; i++) {
int found = 0;
for (int j = 0; j < cnt; j++) {
if (strcmp(edges[j].caller, stk[i]) == 0 && strcmp(edges[j].callee, stk[i-1]) == 0) {
edges[j].hits++;
found = 1;
break;
}
}
if (!found && cnt < MAX_EDGES) {
strncpy(edges[cnt].caller, stk[i], MAX_NAME - 1);
strncpy(edges[cnt].callee, stk[i-1], MAX_NAME - 1);
edges[cnt].hits = 1;
cnt++;
}
}
pthread_mutex_unlock(&lock);
}
// Synthetic call tree
static void leaf(void) { usleep(50); }
static void mid(void) { leaf(); leaf(); }
static void root(void) { mid(); leaf(); }
// Worker: executes workload and triggers sampling
static void* worker(void* arg) {
(void)arg;
while (atomic_load(&run)) {
root();
capture();
usleep(1000);
}
return NULL;
}
// Renderer: clears terminal and prints live graph
static void* renderer(void* arg) {
(void)arg;
while (atomic_load(&run)) {
printf("\033[2J\033[H");
pthread_mutex_lock(&lock);
printf("LIVE CALL GRAPH\n---\n");
for (int i = 0; i < cnt; i++)
printf("%s -> %s [%lu]\n", edges[i].caller, edges[i].callee, edges[i].hits);
pthread_mutex_unlock(&lock);
fflush(stdout);
usleep(DRAW_US);
}
return NULL;
}
int main(void) {
pthread_t w, r;
pthread_create(&w, NULL, worker, NULL);
pthread_create(&r, NULL, renderer, NULL);
sleep(5);
atomic_store(&run, 0);
pthread_join(w, NULL);
pthread_join(r, NULL);
return 0;
}
// Compile: gcc -O2 -g -rdynamic main.c -o live_graph -lunwind -lunwind-x86_64 -lpthread
// Run: ./live_graph
// Note: Uses ANSI escape codes for terminal refresh. Requires x86_64 Linux.