Skip to content

Commit a0a6784

Browse files
profiling to kcachegrind format, with callers and call counts
1 parent 1f74f4d commit a0a6784

4 files changed

Lines changed: 90 additions & 23 deletions

File tree

include/kernel.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,16 @@ void validate_limine_page_tables_and_gdt(void);
8181
uint64_t calls;
8282
} profile_entry;
8383

84-
#define PROFILE_MAX_FUNCS 8192
84+
typedef struct profile_edge {
85+
void *parent;
86+
void *child;
87+
uint64_t calls;
88+
uint64_t total_cycles;
89+
} profile_edge;
90+
91+
#define PROFILE_MAX_FUNCS 8192
92+
#define PROFILE_MAX_EDGES 32768
8593

8694
void profile_dump(void);
87-
void profile_init(uint8_t* pre_allocated);
95+
void profile_init(uint8_t* pre_allocated_funcs, uint8_t* pre_allocated_edges);
8896
#endif

src/init.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void init()
3838
kprintf("THIS IS A PROFILING BUILD - Expect things to run slower!\n");
3939
setforeground(current_console, COLOUR_WHITE);
4040
serial_init(COM1);
41-
profile_init(kmalloc(sizeof(profile_entry) * PROFILE_MAX_FUNCS));
41+
profile_init(kmalloc(sizeof(profile_entry) * PROFILE_MAX_FUNCS), kmalloc(sizeof(profile_edge) * PROFILE_MAX_EDGES));
4242
dprintf("Initialising profiler done!\n");
4343
#endif
4444

src/keyboard.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,9 @@ bool caps_lock_on()
270270
static void push_to_buffer(char x) {
271271
#ifdef PROFILE_KERNEL
272272
if (ctrl_held() && alt_held() && shift_held() && x == 'P') {
273-
dprintf("Dumping callgrind.out to serial port...\n");
273+
kprintf("\nDumping callgrind.out to serial port...\n");
274274
profile_dump();
275+
kprintf("Profile written.\n");
275276
return;
276277
}
277278
#endif

src/profiler.c

Lines changed: 77 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,24 @@
66

77
const char* findsymbol(uint64_t address, uint64_t* offset) __attribute__((no_instrument_function));
88

9-
static struct profile_entry* profile_table = NULL;
9+
static profile_entry* profile_table = NULL;
1010
static size_t profile_entry_count = 0;
1111

12+
static profile_edge* edge_table = NULL;
13+
static size_t edge_count = 0;
14+
1215
struct call_frame {
1316
void *fn;
1417
uint64_t enter_time;
1518
};
1619
static struct call_frame call_stack[PROFILE_STACK_DEPTH];
1720
static int call_sp = 0;
1821

19-
static struct profile_entry *profile_find_or_add(void *fn) __attribute__((no_instrument_function));
22+
static profile_entry *profile_find_or_add(void *fn) __attribute__((no_instrument_function));
23+
static profile_edge *edge_find_or_add(void *parent, void *child) __attribute__((no_instrument_function));
2024

21-
/* Find existing entry or allocate a new one */
22-
static struct profile_entry *profile_find_or_add(void *fn) {
25+
/* Find or add a function entry */
26+
static profile_entry *profile_find_or_add(void *fn) {
2327
for (size_t i = 0; i < profile_entry_count; i++) {
2428
if (profile_table[i].fn == fn) {
2529
return &profile_table[i];
@@ -35,15 +39,41 @@ static struct profile_entry *profile_find_or_add(void *fn) {
3539
return NULL; // out of slots
3640
}
3741

38-
/* Called at kernel startup */
39-
__attribute__((no_instrument_function)) void profile_init(uint8_t* pre_allocated) {
40-
profile_table = (profile_entry*)pre_allocated;
42+
/* Find or add an edge */
43+
static profile_edge *edge_find_or_add(void *parent, void *child) {
44+
for (size_t i = 0; i < edge_count; i++) {
45+
if (edge_table[i].parent == parent && edge_table[i].child == child) {
46+
return &edge_table[i];
47+
}
48+
}
49+
if (edge_count < PROFILE_MAX_EDGES) {
50+
profile_edge *e = &edge_table[edge_count++];
51+
e->parent = parent;
52+
e->child = child;
53+
e->calls = 0;
54+
e->total_cycles = 0;
55+
return e;
56+
}
57+
return NULL;
58+
}
59+
60+
/* Called at kernel startup: caller allocates memory for profile + edge tables */
61+
__attribute__((no_instrument_function)) void profile_init(uint8_t* pre_allocated_funcs, uint8_t* pre_allocated_edges) {
62+
profile_table = (profile_entry*)pre_allocated_funcs;
63+
edge_table = (profile_edge*)pre_allocated_edges;
64+
/* Intentionally NOT using memcpy here, as it is profiled */
4165
if (profile_table) {
4266
for (uint64_t n = 0; n < sizeof(profile_entry) * PROFILE_MAX_FUNCS; ++n) {
43-
pre_allocated[n] = 0;
67+
pre_allocated_funcs[n] = 0;
4468
}
4569
profile_entry_count = 0;
4670
}
71+
if (edge_table) {
72+
for (uint64_t n = 0; n < sizeof(profile_edge) * PROFILE_MAX_EDGES; ++n) {
73+
pre_allocated_edges[n] = 0;
74+
}
75+
edge_count = 0;
76+
}
4777
}
4878

4979
__attribute__((no_instrument_function)) void __cyg_profile_func_enter(void *this_fn, void *call_site) {
@@ -52,6 +82,14 @@ __attribute__((no_instrument_function)) void __cyg_profile_func_enter(void *this
5282
}
5383
uint64_t t = rdtsc();
5484
if (call_sp < PROFILE_STACK_DEPTH) {
85+
/* record edge from parent -> this_fn */
86+
if (call_sp > 0) {
87+
void *parent = call_stack[call_sp - 1].fn;
88+
profile_edge *edge = edge_find_or_add(parent, this_fn);
89+
if (edge) {
90+
edge->calls++;
91+
}
92+
}
5593
call_stack[call_sp].fn = this_fn;
5694
call_stack[call_sp].enter_time = t;
5795
call_sp++;
@@ -69,8 +107,17 @@ __attribute__((no_instrument_function)) void __cyg_profile_func_exit(void *this_
69107
uint64_t enter = call_stack[call_sp].enter_time;
70108
profile_entry *e = profile_find_or_add(fn);
71109
if (e) {
72-
e->total_cycles += (t - enter);
110+
uint64_t delta = (t - enter);
111+
e->total_cycles += delta;
73112
e->calls++;
113+
/* attribute to parent edge as well */
114+
if (call_sp > 0) {
115+
void *parent = call_stack[call_sp - 1].fn;
116+
profile_edge *edge = edge_find_or_add(parent, fn);
117+
if (edge) {
118+
edge->total_cycles += delta;
119+
}
120+
}
74121
}
75122
}
76123
}
@@ -82,21 +129,32 @@ __attribute__((no_instrument_function)) void profile_dump(void) {
82129
}
83130
serial_printf(COM1, "version: 1\n");
84131
serial_printf(COM1, "creator: RetroRocketProfiler\n");
85-
serial_printf(COM1, "events: Cycles\n");
132+
serial_printf(COM1, "events: Cycles\n\n");
86133

87134
for (size_t i = 0; i < profile_entry_count; i++) {
88135
profile_entry *e = &profile_table[i];
89-
if (!e->fn) {
90-
continue;
91-
}
136+
if (!e->fn) continue;
137+
92138
uint64_t offset;
93-
const char *name = findsymbol((uint64_t) e->fn, &offset);
94-
if (!name) {
95-
serial_printf(COM1, "fn=0x%p\n", e->fn);
96-
} else {
97-
serial_printf(COM1, "fn=%s\n", name);
139+
const char *name = findsymbol((uint64_t)e->fn, &offset);
140+
serial_printf(COM1, "fn=%s\n", name ? name : "unknown");
141+
142+
// function self-cost
143+
serial_printf(COM1, "1 %llu\n", (unsigned long long)e->total_cycles);
144+
145+
// child edges
146+
for (size_t j = 0; j < edge_count; j++) {
147+
profile_edge *edge = &edge_table[j];
148+
if (edge->parent == e->fn) {
149+
const char *cname = findsymbol((uint64_t)edge->child, &offset);
150+
serial_printf(COM1, "cfn=%s\n", cname ? cname : "unknown");
151+
serial_printf(COM1, "calls=%llu 1\n", (unsigned long long)edge->calls);
152+
serial_printf(COM1, "1 %llu\n", (unsigned long long)edge->total_cycles);
153+
}
98154
}
99-
serial_printf(COM1, "1 %lu\n", (uint64_t) (e->total_cycles));
155+
156+
// terminate block
157+
serial_printf(COM1, "\n");
100158
}
101159
}
102160

0 commit comments

Comments
 (0)