Hi all,
in Languages like C, C++, Fortran, … with the right compilers it is possible to do function instrumentation. If you don’t know what it is, let me explain:
Consider a C-function like
int foo() {
// do some work
return 0;
}
Function instrumentation would insert so called function hooks during compilation, such that the function that gets compiles looks more like this:
int foo() {
function_hook_enter();
// do some work
function_hook_exit();
return 0;
}
The source code remains unchanged. The function hook definition can be supplied via a library during linking, or be part of the same source file.
In gcc, clang, icc, and other compilers there exists the -finstrument-funcitons
flag to trigger this behavior.
As this goes against one of Zig’s most fundamental principles (no hidden control flow) I would like to explain why such an option might be useful.
Imagine you have a complex code and you would like to profile the functions. This is often done via sampling. A sampling-profiler checks, the current call-tree from time to time and gives information about the calling statistics in the end.
This can be flawed as not everything is covered, calls might be missed. timings might be off, …
With function instrumentation, one could keep track of the exact calling order, count how often something was called, and so on. By allowing a flag like -finstrument-functions
one would also be independent from some weird compiler wrapper, that instruments with predefined hooks. One could supply ones own hooks.
Some compiler vendors have more or less standardized the hook definition to look like this:
void __cyg_profile_func_enter (void *this_fn, void *call_site)
void __cyg_profile_func_exit (void *this_fn, void *call_site)
Here is a small C-example:
#include <stdio.h>
#include <time.h>
int level = -1;
__attribute__ ((no_instrument_function))
void __cyg_profile_func_enter (void *this_fn, void *call_site) {
level++;
for (int i=0; i<level; i++) {printf(" ");}
printf("Entering function %p at time %d\n", this_fn, (int)time(NULL));
}
__attribute__ ((no_instrument_function))
void __cyg_profile_func_exit (void *this_fn, void *call_site) {
for (int i=0; i<level; i++) {printf(" ");}
printf("Leaving function %p at time %d\n", this_fn, (int)time(NULL));
level--;
}
int bar() {
sleep(1);
return 1;
}
int foo() {
sleep(2);
return bar();
}
int main() {
sleep(1);
foo();
bar();
sleep(2);
return 0;
}
When compiling with gcc and running the code one gets:
gcc -c -finstrument-functions instr.c
gcc -o instr.x -finstrument-functions instr.o
$ ./instr.x
Entering function 0x55c86168132c at time 1745233450
Entering function 0x55c8616812d0 at time 1745233451
Entering function 0x55c86168127b at time 1745233453
Leaving function 0x55c86168127b at time 1745233454
Leaving function 0x55c8616812d0 at time 1745233454
Entering function 0x55c86168127b at time 1745233454
Leaving function 0x55c86168127b at time 1745233455
Leaving function 0x55c86168132c at time 1745233457
This would give a developer a tool to figure out, if their code needs tweaking at a specific routine, that takes a lot of time, or if some routine is called so often, that the calling overhead slows down program execution. Upon release one would discard the flag from the build process (or one could introduce a DebugProfile, or ReleaseProfile mode) and the code behaves perfectly normal.
I would like to hear your thoughts about this instrumentation and would like to propose to incorporate the -finstrument-function
flag into the compiler, as well as propose that the __cyg_profile_func
definitions are used, as they are basically standardized among different compiler vendors.
Cheers.