66
77const 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 ;
1010static size_t profile_entry_count = 0 ;
1111
12+ static profile_edge * edge_table = NULL ;
13+ static size_t edge_count = 0 ;
14+
1215struct call_frame {
1316 void * fn ;
1417 uint64_t enter_time ;
1518};
1619static struct call_frame call_stack [PROFILE_STACK_DEPTH ];
1720static 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