1.3.12. Instrumentation Options¶
1.3.12.1. Stack Smashing Detection Options¶
The compiler provides the capability to instrument protection for stack smashing attacks.
1.3.12.2. Function Entry/Exit Hook Options¶
The compiler provides the capability to instrument functions with entry and exit hook function calls using the -finstrument-functions option:
- -finstrument-functions¶
For each function being compiled, instruct the compiler to generate a call to the entry hook function, __cyg_profile_func_enter, just after entry to a given function, and a call to exit hook function, __cyg_profile_func_exit, just prior to exit from a given function.
The compiler also calls __cyg_profile_func_enter and __cyg_profile_func_exit on behalf of a function that is inlined into another function. This means that an addressable version of an inlined function must be available in the linked application to facilitate lookup of the inlined function symbol. If all uses of a function are inlined, the definition of the inlined function may incur some growth in code size for the linked application.
1.3.12.2.1. Enabling Use of Function Entry/Exit Hooks¶
To enable the use of function entry/exit hooks in your application, you need to provide definitions of:
__cyg_profile_func_entry
The signature of the __cyg_profile_func_enter function is as follows:
void __cyg_profile_func_entry(void *this_fcn, void *call_site);
An example definition of this function might look like this:
#include "func_timer.h"
extern "C" {
// Entry Hook Function
__attribute__((no_instrument_function))
void __cyg_profile_func_enter(void *this_fcn, void *call_site) {
// Non-NULL function address is required
if (!this_fcn) return;
// Find function address in function timer map;
// If this is the first call to the specified function,
// then create a timer record for it and insert record into map
auto func_iter = func_timer_map.find((unsigned long)this_fcn);
func_timer_record *func_timer;
if (func_iter == func_timer_map.end()) {
func_timer = new func_timer_record((unsigned long)this_fcn);
func_timer_map[(unsigned long)this_fcn] = func_timer;
}
else {
func_timer = func_iter->second;
}
// If function is not already on the call stack, start the clock
if (func_timer->recur_level == 0) {
func_timer->clock_start = clock();
}
else {
func_timer->recur_level++;
}
}
} /* extern "C" */
__cyg_profile_func_exit
The signature of the __cyg_profile_func_exit function is as follows:
void __cyg_profile_func_exit(void *this_fcn, void *call_site);
An example definition of this function might look like this:
#include "func_timer.h"
extern "C" {
// Function Exit Hook
__attribute__((no_instrument_function))
void __cyg_profile_func_exit(void *this_fcn, void *call_site) {
// Non-NULL function address is required
if (!this_fcn) return;
// Find function in function timer map; error if not found
auto func_iter = func_timer_map.find((unsigned long)this_fcn);
func_timer_record *func_timer;
if (func_iter == func_timer_map.end()) {
printf("ERROR: expected function in func_timer_map\n");
return;
}
func_timer = func_iter->second;
// If we're about to remove the function from the call stack,
// add elapsed time to total accumulated time for this function
if (func_timer->recur_level == 1) {
func_timer->acc_func_time += (long)(clock() - func_timer->clock_start);
}
func_timer->recur_level--;
}
} /* extern "C" */
For both of the above functions, the first argument, this_fcn, is the address of the start of the current function, which can be looked up in the symbol table, and the second argument, call_site, is the return address of the current function that can be used to determine where the current function was called from.
Note
Define __cyg_profile_func_enter and __cyg_profile_func_exit as “C” Symbols
When using the -finstrument-functions option with a C++ source file, the c29clang compiler instruments a given function with calls to __cyg_profile_func_enter and __cyg_profile_func_exit using the “C” names of those function symbols. Consequently, when you define the __cyg_profile_func_enter and __cyg_profile_func_exit functions for use in a C++ application, you must enclose the definitions of these functions in an extern “C” construct, as indicated in the examples above.
1.3.12.2.2. Disabling Instrumentation with no_instrument_function Attribute¶
While applying the -finstument-functions option to an application, there may be some functions that you may want to exclude from being instrumented, such as the definitions of __cyg_profile_func_enter and __cyg_profile_func_exit described above. In such cases, the no_instrument_function function attribute can be applied to prevent calls to the entry and exit hooks from being generated for a given function.
The above definition of __cyg_profile_func_enter contains an example of how to apply the no_instrument_function attribute to a function:
__attribute__((no_instrument_function))
void __cyg_profile_func_enter(void *this_fcn, void *call_site) {
...
}
1.3.12.2.3. Function Entry/Exit Hooks Example¶
One useful application of function entry and exit hook functions is to gather profile data for the functions in an application. The above definitions of __cyg_profile_func_enter and __cyg_profile_func_exit collect the accumulated time spent in each instrumented function in an application.
The profile data is collected and recorded in a map of function_timer_record objects as detailed in func_timer.h:
#include <stdio.h>
#include <time.h>
#include <map>
class func_timer_record {
public:
unsigned long func_address;
unsigned int recur_level;
clock_t clock_start;
long acc_func_time;
func_timer_record(unsigned long func_addr) :
func_address(func_addr),
recur_level(0),
clock_start(0),
acc_func_time(0) { }
~func_timer_record() { }
} func_timer_record;
extern std::map<unsigned long, func_timer_record *> func_timer_map;
__attribute__((no_instrument_function)) void report_function_times(void);
In this simplistic example, it is anticipated that the application being profiled will call report_function_times that writes out a comma-separated list of the function addresses and their corresponding recorded execution times:
#include "func_timer.h"
#include <list>
std::map<unsigned long, func_timer_record *> func_timer_map;
__attribute__((no_instrument_function)) void report_function_times(void) {
// Print CSV output of function addresses and corresponding times
std::list<function_timer_record *> curr_func_list;
for (auto it = func_timer_map.begin(); it != func_timer_map.end(); ++it) {
unsigned long curr_func_addr = it->first;
unsigned long curr_func_time = (it->second)->acc_func_time;
printf("func_address: 0x%08lx, cumulative time in function: %ld\n",
curr_func_addr, curr_func_time);
}
}
The application to be profiled can then be compiled with the -finstrument-functions option:
%> c29clang -mcpu=c29.c0 -finstrument-functions <app source files> \
func_timer.cpp func_enter.cpp func_exit.cpp -o app.out ...
While the functions defined in the application source files will be instrumented, the instrumentation functions themselves will not since they have been annotated with the no_instrument_function attribute.
When loaded and run, app.out produces the function time statistics that can then be analyzed and processed by a program that has access to the app.out file’s symbol table.