New testpoint mechanism to instrument binaries for testing
authorChristian Babeux <christian.babeux@efficios.com>
Fri, 14 Sep 2012 19:00:30 +0000 (15:00 -0400)
committerDavid Goulet <dgoulet@efficios.com>
Tue, 2 Oct 2012 19:32:50 +0000 (15:32 -0400)
This commit introduce two new macros: TESTPOINT_DECL(name) and
testpoint(name).

Here a quick example that show how to use the testpoint mechanism:

file: main.c

/* Testpoint declaration */
TESTPOINT_DECL(interesting_function)

void interesting_function(void)
{
        testpoint(interesting_function);
        /* Some processing that can fail */
        ...
}

int main(int argc, char *argv[])
{
        interesting_function();
        ...
        printf("End");
}

file: testpoint.c
void __testpoint_interesting_function(void)
{
        printf("In testpoint of interesting function!");
}

Compile:
gcc -o test main.c
gcc -fPIC -shared -o testpoint.so testpoint.c

Run:
> ./test
  End
> export LTTNG_TESTPOINT_ENABLE=1
> LD_PRELOAD=testpoint.so ./test
  In testpoint of interesting function!
  End
> export LTTNG_TESTPOINT_ENABLE=0
> LD_PRELOAD=testpoint.so ./test
  End

The testpoint mechanism is triggered via the preloading of a shared
object containing the appropriate testpoint symbols and by setting the
LTTNG_TESTPOINT_ENABLE environment variable.

The check on this environment variable is done on the application
startup with the help of a constructor (lttng_testpoint_check) which
toggle a global state variable indicating whether or not the testpoints
should be activated.

When enabled, the testpoint() macro calls an underlying wrapper specific
to the testpoint and simply try to lookup the testpoint symbol via a
dlsym() call.

When disabled, the testpoint() call will only incur an additionnal test
per testpoint on a global variable. This performance 'hit' should be
acceptable for production use.

The testpoint mechanism should be *always on*. It can be explicitly
disabled via CFLAGS="-DNTESTPOINT" in a way similar to NDEBUG and
assert().

Please refer to 0005-testpoint-mechanism.txt for more information.

Signed-off-by: Christian Babeux <christian.babeux@efficios.com>
Signed-off-by: David Goulet <dgoulet@efficios.com>
configure.ac
src/common/Makefile.am
src/common/testpoint/Makefile.am [new file with mode: 0644]
src/common/testpoint/testpoint.c [new file with mode: 0644]
src/common/testpoint/testpoint.h [new file with mode: 0644]

index 0aede284dcee0db95d4e4ada0f0f0fede4a3b48e..f33f9e6bd20f34a44d7eb864eb6ac1efb2596887 100644 (file)
@@ -275,6 +275,7 @@ AC_CONFIG_FILES([
        src/common/sessiond-comm/Makefile
        src/common/compat/Makefile
        src/common/relayd/Makefile
+       src/common/testpoint/Makefile
        src/lib/Makefile
        src/lib/lttng-ctl/Makefile
        src/lib/lttng-ctl/filter/Makefile
index ca48153c36b8a97bd50959df1e103aa448cc9fae..577dffe856d96bed26f53d10c0f09869a33b335d 100644 (file)
@@ -1,6 +1,7 @@
 AM_CPPFLAGS =
 
-SUBDIRS = compat hashtable kernel-ctl sessiond-comm relayd kernel-consumer ust-consumer
+SUBDIRS = compat hashtable kernel-ctl sessiond-comm relayd \
+                 kernel-consumer ust-consumer testpoint
 
 AM_CFLAGS = -fno-strict-aliasing
 
@@ -9,7 +10,8 @@ noinst_HEADERS = lttng-kernel.h defaults.h macros.h error.h futex.h uri.h utils.
 # Common library
 noinst_LTLIBRARIES = libcommon.la
 
-libcommon_la_SOURCES = error.h error.c utils.c utils.h runas.c runas.h common.h futex.c futex.h uri.c uri.h
+libcommon_la_SOURCES = error.h error.c utils.c utils.h runas.c runas.h \
+                       common.h futex.c futex.h uri.c uri.h
 
 # Consumer library
 noinst_LTLIBRARIES += libconsumer.la
diff --git a/src/common/testpoint/Makefile.am b/src/common/testpoint/Makefile.am
new file mode 100644 (file)
index 0000000..7d3df16
--- /dev/null
@@ -0,0 +1,6 @@
+AM_CPPFLAGS =
+
+noinst_LTLIBRARIES = libtestpoint.la
+
+libtestpoint_la_SOURCES = testpoint.h testpoint.c
+libtestpoint_la_LIBADD = -ldl
diff --git a/src/common/testpoint/testpoint.c b/src/common/testpoint/testpoint.c
new file mode 100644 (file)
index 0000000..6893a41
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 - Christian Babeux <christian.babeux@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef NTESTPOINT
+
+#define _GNU_SOURCE /* for RTLD_DEFAULT GNU extension */
+#include <dlfcn.h>  /* for dlsym   */
+#include <stdlib.h> /* for getenv  */
+#include <string.h> /* for strncmp */
+
+#include "testpoint.h"
+
+/* Environment variable used to enable the testpoints facilities. */
+static const char *lttng_testpoint_env_var = "LTTNG_TESTPOINT_ENABLE";
+
+/* Testpoint toggle flag */
+int lttng_testpoint_activated;
+
+/*
+ * Toggle the support for testpoints on the application startup.
+ */
+static void __attribute__((constructor)) lttng_testpoint_check(void)
+{
+       char *testpoint_env_val = NULL;
+
+       testpoint_env_val = getenv(lttng_testpoint_env_var);
+       if (testpoint_env_val != NULL
+                       && (strncmp(testpoint_env_val, "1", 1) == 0)) {
+               lttng_testpoint_activated = 1;
+       }
+}
+
+/*
+ * Lookup a symbol by name.
+ *
+ * Return the address where the symbol is loaded or NULL if the symbol was not
+ * found.
+ */
+void *lttng_testpoint_lookup(const char *name)
+{
+       if (!name) {
+               return NULL;
+       }
+
+       return dlsym(RTLD_DEFAULT, name);
+}
+
+#endif /* NTESTPOINT */
diff --git a/src/common/testpoint/testpoint.h b/src/common/testpoint/testpoint.h
new file mode 100644 (file)
index 0000000..ba3af8a
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 - Christian Babeux <christian.babeux@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef NTESTPOINT
+
+#define testpoint(name)
+#define TESTPOINT_DECL(name)
+
+#else /* NTESTPOINT */
+
+#include <urcu.h> /* for caa_likely/unlikely */
+
+extern int lttng_testpoint_activated;
+
+void *lttng_testpoint_lookup(const char *name);
+
+/*
+ * Testpoint is only active if the global lttng_testpoint_activated flag is
+ * set.
+ */
+#define testpoint(name)                                        \
+       do {                                                    \
+               if (caa_unlikely(lttng_testpoint_activated)) {  \
+                       __testpoint_##name##_wrapper();         \
+               }                                               \
+       } while (0)
+
+/*
+ * One wrapper per testpoint is generated. This is to keep track of the symbol
+ * lookup status and the corresponding function pointer, if any.
+ */
+#define _TESTPOINT_DECL(_name)                                         \
+       static inline void __testpoint_##_name##_wrapper(void)          \
+       {                                                               \
+               static void (*tp)(void);                                \
+               static int found;                                       \
+               const char *tp_name = "__testpoint_" #_name;            \
+                                                                       \
+               if (tp) {                                               \
+                       tp();                                           \
+               } else {                                                \
+                       if (!found) {                                   \
+                               tp = lttng_testpoint_lookup(tp_name);   \
+                               if (tp) {                               \
+                                       found = 1;                      \
+                                       tp();                           \
+                               } else {                                \
+                                       found = -1;                     \
+                               }                                       \
+                       }                                               \
+               }                                                       \
+       }
+
+/* Testpoint declaration */
+#define TESTPOINT_DECL(name)   \
+       _TESTPOINT_DECL(name)
+
+#endif /* NTESTPOINT */
This page took 0.028467 seconds and 4 git commands to generate.