From 6242251b39f531a2485b758edcb455e220267fdd Mon Sep 17 00:00:00 2001 From: Christian Babeux Date: Fri, 14 Sep 2012 15:00:30 -0400 Subject: [PATCH] New testpoint mechanism to instrument binaries for testing 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 Signed-off-by: David Goulet --- configure.ac | 1 + src/common/Makefile.am | 6 ++- src/common/testpoint/Makefile.am | 6 +++ src/common/testpoint/testpoint.c | 62 +++++++++++++++++++++++++++ src/common/testpoint/testpoint.h | 72 ++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 src/common/testpoint/Makefile.am create mode 100644 src/common/testpoint/testpoint.c create mode 100644 src/common/testpoint/testpoint.h diff --git a/configure.ac b/configure.ac index 0aede284d..f33f9e6bd 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/common/Makefile.am b/src/common/Makefile.am index ca48153c3..577dffe85 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -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 index 000000000..7d3df162f --- /dev/null +++ b/src/common/testpoint/Makefile.am @@ -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 index 000000000..6893a4112 --- /dev/null +++ b/src/common/testpoint/testpoint.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 - Christian Babeux + * + * 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 /* for dlsym */ +#include /* for getenv */ +#include /* 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 index 000000000..ba3af8a53 --- /dev/null +++ b/src/common/testpoint/testpoint.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 - Christian Babeux + * + * 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 /* 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 */ -- 2.34.1