4

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?

1 Answers1

4

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;
}
  • Hello, thank you very much for sharing this code. I just had a very quick question on how you are compiling this program. I am using the following command (`$GCCDIR` is where GCC 8.3 is installed): `$GCCDIR/bin/g++ -I$GCCDIR/lib/gcc/x86_64-pc-linux-gnu/8.3.0/plugin/include -std=gnu++11 -fno-rtti -fPIC -shared -O2 -o plugin.so plugin.c` and I end up with error of `error: ‘lookup_attribute’ was not declared in this scope`. Is there anything that I'm missing in terms of compiling this code? Thank you in advance. – Jay Mar 17 '23 at 01:07
  • Not sure, but I do remember that you need to install `gcc-N-plugin-dev` (where N is the major version number). The code that resulted from my question/answer above is here, along with some instructions, if you want to use that: https://github.com/christophebedard/instrument-attribute-gcc-plugin – christophebedard Mar 18 '23 at 16:39
  • Thank you very much for your swift response! Thanks to your suggestion, I was able to get it to work. Thank you again! – Jay Mar 19 '23 at 19:03