Tag Archives: plugins

And now you can write nbdkit plugins in Lua

See here.

Previously Tcl …

Advertisements

Leave a comment

Filed under Uncategorized

nbdkit filters

nbdkit is our toolkit for creating Network Block Device (NBD) servers from “unusual” data sources. nbdkit was already configurable by writing simple plugins in several programming languages. Last week Eric Blake and I added a nice new feature: You can now modify existing plugins by placing “filters” in front of them.

A plugin might do something simple like serve a local file from disk or complicated like bridging to VMware servers. A filter can modify this by:

(You can also layer filters to arbitrary depth)

nbdkit 1.1.27 has three simple filters, and 1.1.28 will include two more, and you can write your own although (unlike plugins) filters do not yet have a stable ABI and we haven’t decided if we will offer a stable ABI in future.

1 Comment

Filed under Uncategorized

Playing with GCC plugins

Martin Kletzander’s devconf.cz talk about features in modern compilers that we probably don’t use inspired me to play with GCC plugins. I want to see if I can use a plugin to solve a horrible bug we hit at the end of last year.

We had a C struct defined in a header file like this:

struct {
  int foo;
#ifdef HAVE_LIBVIRT
  int bar;
#endif
  int baz;
};

But because of the way HAVE_LIBVIRT is defined, if you used the header file as:

#include <config.h>
#include "mystruct.h"

the struct would be defined with the bar element. But if you used the struct like this:

#include "mystruct.h"

you didn’t get the bar element. Ooops. This silently caused data corruption (when accessing the third member of the struct).

My idea is that we could use a GCC plugin to print out the size of every struct we define when compiling the code, and then you can post-process that to see if there are structs which unexpectedly change size.

The GCC plugin API is not exactly well documented. In fact, without reading a lot of GCC header files and internals, it’s virtually impossible to work out what is going on. Anyway, my plugin below works for me on GCC 6.

You have to compile the plugin using:

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

and then when you compile your source code you have to add:

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

The plugin writes simple text records to logfile:

struct 'random_data' has size 384 [bits]
struct 'drand48_data' has size 192 [bits]
...

which you can easily post-process with some shell script. Note you should compile your source with make -j1 so that parallel runs of the compiler don’t try to write to the log file at the same time, since my plugin doesn’t include any locking.

/* structsizes.cc plugin: public domain example code written by
 * Richard W.M. Jones
 */

#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;

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

    /* 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? */

    /* 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;
    }

    /* 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]\n", name, size);
    }
    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 < plugin_info->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=<logfile>\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;
}

5 Comments

Filed under Uncategorized