#include "flame.h"
static void flame_disable(int sig);
static void flame_collect(int ncpus);
static void flame_doentry(int cpuid, uint32_t idx,
struct flame_graph_entry *fge);
static uint32_t *savewindex;
static struct save_ctx symctx;
void
flame_collect_loop(void)
{
int ncpus;
int enable;
int sniff = 1;
int n;
size_t ncpus_size = sizeof(ncpus);
if (sysctlbyname("hw.ncpu", &ncpus, &ncpus_size, NULL, 0) < 0) {
perror("hw.ncpu");
exit(1);
}
savewindex = calloc(ncpus, sizeof(*savewindex));
read_symbols("/boot/kernel/kernel");
enable = 1;
signal(SIGINT, flame_disable);
if (sysctlbyname("debug.flame_graph_enable",
NULL, NULL, &enable, sizeof(enable)) < 0) {
perror("debug.flame_graph_enable");
exit(1);
}
n = 0;
for (;;) {
sysctlbyname("debug.flame_graph_sniff", NULL, NULL,
&sniff, sizeof(sniff));
usleep(10000);
++n;
if ((n & 127) == 0)
flame_collect(ncpus);
}
flame_disable(0);
}
static void
flame_collect(int ncpus)
{
struct flame_graph_pcpu *fg;
struct flame_graph_entry *fge;
struct flame_graph_entry *scan;
void *buf;
void *ptr;
size_t bytes;
uint32_t rindex;
uint32_t windex;
uint32_t delta;
int n;
bytes = sizeof(size_t) +
sizeof(struct flame_graph_pcpu) +
sizeof(struct flame_graph_entry) * FLAME_GRAPH_NENTRIES;
bytes *= ncpus;
buf = malloc(bytes);
if (sysctlbyname("debug.flame_graph_data", buf, &bytes, NULL, 0) < 0) {
perror("debug.flame_graph_data");
exit(1);
}
ptr = buf;
for (n = 0; n < ncpus; ++n) {
if (bytes < sizeof(size_t) + sizeof(*fg))
break;
fg = (void *)((char *)ptr + sizeof(size_t));
bytes -= sizeof(size_t) + sizeof(*fg);
ptr = (char *)ptr + sizeof(size_t) + sizeof(*fg);
if (bytes < sizeof(*fge) * fg->nentries)
break;
fge = (void *)(fg + 1);
ptr = (char *)ptr + sizeof(*fge) * fg->nentries;
bytes -= sizeof(*fge) * fg->nentries;
rindex = savewindex[n];
windex = fg->windex;
delta = windex - rindex;
if (delta >= fg->nentries)
rindex = windex - fg->nentries + 1;
while (rindex != windex) {
scan = &fge[rindex % fg->nentries];
if (scan->rips[0])
flame_doentry(n, rindex, scan);
++rindex;
}
savewindex[n] = windex;
}
free(buf);
}
static void
flame_doentry(int cpuid, uint32_t idx, struct flame_graph_entry *fge)
{
int i;
for (i = 0; i < FLAME_GRAPH_FRAMES; ++i) {
if (fge->rips[i] == 0)
break;
}
printf("%03d/%03d ", cpuid, idx % 1000);
while (--i >= 0) {
printf(" ");
printf("%s()",
address_to_symbol((void *)fge->rips[i], &symctx));
}
printf("\n");
}
static void
flame_disable(int sig __unused)
{
int enable;
enable = 0;
if (sysctlbyname("debug.flame_graph_enable",
NULL, NULL, &enable, sizeof(enable)) < 0) {
perror("debug.flame_graph_enable");
}
exit(0);
}