Загрузка данных


// 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;
}