I'm using gcc's -finstrument-functions
option. To minimize the overhead, I want to instrument only a few functions. However, gcc only lets you blacklist functions (with the no_instrument_function
attribute, or by providing a list of paths). It doesn't let you whitelist functions.
So I wrote a small gcc plugin adding an instrument_function
attribute. This lets me set the instrumentation "flag" for a specific function (or, rather, clear the no instrumentation flag):
tree handle_instrument_function_attribute(
tree * node,
tree name,
tree args,
int flags,
bool * no_add_attrs)
{
tree decl = *node;
DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(decl) = 0;
return NULL_TREE;
}
However, from my understanding, this does not work. Looking at the gcc source, for this flag to actually do anything, you need to also use -finstrument-functions
. See gcc/gimplify.c:14436
:
...
/* If we're instrumenting function entry/exit, then prepend the call to
the entry hook and wrap the whole function in a TRY_FINALLY_EXPR to
catch the exit hook. */
/* ??? Add some way to ignore exceptions for this TFE. */
if (flag_instrument_function_entry_exit
&& !DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl)
/* Do not instrument extern inline functions. */
&& !(DECL_DECLARED_INLINE_P (fndecl)
&& DECL_EXTERNAL (fndecl)
&& DECL_DISREGARD_INLINE_LIMITS (fndecl))
&& !flag_instrument_functions_exclude_p (fndecl))
...
It first checks that the global -finstrument-functions
flag is enabled. Then it checks a specific function's flag, which, from what I understand, is enabled by default. So all other functions that don't have my instrument_function
attribute would still be instrumented.
Is there a way to clear this flag for all functions first, then handle my instrument_function
attribute to set the flag for those functions only?
The trick was only defining the attribute, but not actually using any handling function, and do the processing elsewhere.
We still use -finstrument-functions
to enable instrumentation for all functions at first. We can register a callback for PLUGIN_FINISH_PARSE_FUNCTION
, which checks everything. For every function declaration, it checks its attributes. If it has the instrument_function
attribute, it sets the flag for the instrumentation to be added later as usual. If the function doesn't have the attribute, it clears the flag.
#include <stdio.h>
#include "gcc-plugin.h"
#include "plugin-version.h"
#include "tree.h"
int plugin_is_GPL_compatible;
static struct plugin_info info = {
"0.0.1",
"This plugin provides the instrument_function attribute.",
};
static struct attribute_spec instrument_function_attr =
{
"instrument_function",
0,
-1,
false,
false,
false,
NULL, // No need for a handling function
};
static void register_attributes(void * event_data, void * data)
{
register_attribute(&instrument_function_attr);
}
void handle(void * event_data, void * data)
{
tree fndecl = (tree) event_data;
// Make sure it's a function
if (TREE_CODE(fndecl) == FUNCTION_DECL)
{
// If the function has our attribute, enable instrumentation,
// otherwise explicitly disable it
if (lookup_attribute("instrument_function", DECL_ATTRIBUTES(fndecl)) != NULL_TREE)
{
printf("instrument_function: (%s:%d) %s\n",
DECL_SOURCE_FILE(fndecl),
DECL_SOURCE_LINE(fndecl),
get_name(fndecl));
DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(fndecl) = 0;
}
else
{
DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(fndecl) = 1;
}
}
}
int plugin_init(
struct plugin_name_args * plugin_info,
struct plugin_gcc_version * version)
{
register_callback(
plugin_info->base_name,
PLUGIN_INFO,
NULL,
&info);
register_callback(
plugin_info->base_name,
PLUGIN_FINISH_PARSE_FUNCTION,
handle,
NULL);
register_callback(
plugin_info->base_name,
PLUGIN_ATTRIBUTES,
register_attributes,
NULL);
return 0;
}