The Gluster Blog

Gluster blog stories provide high-level spotlights on our users all over the world

Compiling GlusterFS with a gcc plug-in – an experiment

Gluster
2016-10-07

Back in February of this year Martin Kletzander gave a talk at devconf.cz on GCC plug-ins. It would seem that gcc plug-ins are a feature that has gone largely overlooked for many years.

I came back from DevConf inspired to try it out. A quick search showed me I was not alone – a colleague here at Red Hat had also seen Martin’s talk, and he wrote about his experiment here source

I had had something similar in mind to what Richard had done. I wanted to check all the struct definitions, i.e. all instances of all the variables of any particular type, and make sure the defined sizes were consistent throughout the GlusterFS sources.

Using Richard’s plug-in I found that while things were generally good, there were a couple structs that appeared to have mismatched sizes. The only thing was, Richard’s plug-in didn’t tell me where those structs were defined. And unfortunately GlusterFS has a lot of cut-and-pasted code, so it wasn’t a matter of a simple grep to find them.

As both Martin and Richard note, the GCC plug-in framework is not well documented. It was not obvious how to do something that seems – on the surface – like it should be trivial. But, with a bit of detective work, I was able to solve it. (And after the fact the change was, in fact, quite simple; finding it however took some time.)

/* structsizes.cc plugin: public domain example code written by
 * Richard W.M. Jones, with modifications by Kaleb S. KEITHLEY
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gcc-plugin.h>
#include <tree.h>
#include <print-tree.h>

int plugin_is_GPL_compatible;

static FILE *log_fp;

static void
plugin_finish_type (void *event_data, void *user_data)
{
  tree type = (tree) event_data;

  if (user_data) {
    char *c = (char *) user_data;
    fprintf (log_fp, "user_data %x %x %x %x\n", c[0], c[1], c[2], c[3]);
  }

  /* We only care about structs, not any other type definition. */
  if (TREE_CODE (type) == RECORD_TYPE) {
    /* This is useful for working out how to navigate the tree below. */
    /* debug_tree (type); */

    /* If the type is not complete, we can't do anything. */
    if (!COMPLETE_TYPE_P (type)) {
      /* fprintf (log_fp, "struct '%s' has incomplete type\n", name); */
      return;
    }

    /* Struct name? */
    tree name_tree = TYPE_NAME (type);

    /* Ignore unnamed structs. */
    if (!name_tree) {
      /* fprintf (log_fp, "ignoring unnamed struct\n"); */
      return;
    }

    const char *name;
    if (TREE_CODE (name_tree) == IDENTIFIER_NODE)
      name = IDENTIFIER_POINTER (name_tree);
    else if (TREE_CODE (name_tree) == TYPE_DECL && DECL_NAME (name_tree))
      name = IDENTIFIER_POINTER (DECL_NAME (name_tree));
    else
      name = "unknown struct name"; /* should never happen? */

    tree decl = type;
    for (; decl; decl = TREE_CHAIN (decl)) {
      if (DECL_P (decl))
        break;
    }

    const char *filename = DECL_SOURCE_FILE (decl);
    int lineno = DECL_SOURCE_LINE (decl);

    /* Get the size of the struct that has been defined. */
    tree size_tree = TYPE_SIZE (type);
    if (TREE_CODE (size_tree) == INTEGER_CST &&
        !TYPE_P (size_tree) && TREE_CONSTANT (size_tree)) {
      size_t size = TREE_INT_CST_LOW (size_tree);
      fprintf (log_fp, "struct '%s' has size %zu [bits] in %s, line %d\n", name, size, filename, lineno);
    }
    else
      fprintf (log_fp, "struct '%s' has non-constant size\n", name);
  }

  fflush (log_fp);
}

int
plugin_init (struct plugin_name_args *plugin_info,
             struct plugin_gcc_version *version)
{
  const char *logfile = NULL;
  size_t i;

  /* Open the log file. */
  for (i = 0; i argc; ++i) {
    if (strcmp (plugin_info->argv[i].key, "log") == 0) {
      logfile = plugin_info->argv[i].value;
    }
  }

  if (!logfile) {
    fprintf (stderr, "structsizes plugin: missing parameter: -fplugin-arg-structsizes-log=\n");
    exit (EXIT_FAILURE);
  }

  log_fp = fopen (logfile, "a");
  if (log_fp == NULL) {
    perror (logfile);
    exit (EXIT_FAILURE);
  }

  fprintf (log_fp, "Loaded structsizes plugin (GCC %s.%s.%s)\n",
           version->basever, version->devphase, version->revision);

  register_callback (plugin_info->base_name, PLUGIN_FINISH_TYPE,
                     plugin_finish_type, NULL);

  return 0;
}

Compile the plug-in using

    gcc -g -I`gcc -print-file-name=plugin`/include \
        -fpic -shared -o structsizes.so structsizes.cc

and when you compile your source, to use the plug-in you must add

    ... -fplugin=./structsizes.so \
        -fplugin-arg-structsizes-log=<logfile> ...

to the compiler command line options.

For the purposes of compiling GlusterFS with this plug-in, I changed GlusterFS’s configure.ac file like this:

    ...
    CFLAGS="${CFLAGS} -g -O2 -fplugin=/path/to/structsizes.so \
        -fplugin-arg-structsizes-log=/tmp/ss2.out"
    ...

and then ran

    autogen.sh && ./configure && make

to build GlusterFS.

Afterwards you can

    sort -u < /tmp/ss2.out > outfile

to reduce the output to something digestible.

And in the end, the seemingly mismatched struct sizes weren’t really a problem. They were two different types – that happened to have the same type name – in two different translators.

BLOG

  • 06 Dec 2020
    Looking back at 2020 – with g...

    2020 has not been a year we would have been able to predict. With a worldwide pandemic and lives thrown out of gear, as we head into 2021, we are thankful that our community and project continued to receive new developers, users and make small gains. For that and a...

    Read more
  • 27 Apr 2020
    Update from the team

    It has been a while since we provided an update to the Gluster community. Across the world various nations, states and localities have put together sets of guidelines around shelter-in-place and quarantine. We request our community members to stay safe, to care for their loved ones, to continue to be...

    Read more
  • 03 Feb 2020
    Building a longer term focus for Gl...

    The initial rounds of conversation around the planning of content for release 8 has helped the project identify one key thing – the need to stagger out features and enhancements over multiple releases. Thus, while release 8 is unlikely to be feature heavy as previous releases, it will be the...

    Read more