It's possible to build babeltrace with AddressSanitizer by adding
-fsanitize=address to CFLAGS/LDFLAGS, but it causes some issues. These
issues are related to the fact that if libasan.so is to be loaded, it
must be the first library to be loaded. When a Python interpreter loads
the bt2 module, that pulls in libbabeltrace2.so, which pulls libasan.so.
libasan.so then complains about not being the first loaded libraries.
The only way I know to fix this is to LD_PRELOAD libasan.so in the
Python interpreter.
The first case where this happens is when building the Python bindings.
This is because the Sphinx tool (so, the Python interpreter) loads the
bt2 Python module in order to obtain the documentation.
The other case is when running the Python bindings tests, the test
runner is in Python, and the .py tests imports the bt2 module.
In both cases, libasan unfortunately finds some leaks in the Python
interpreter. We must therefore disable leak reporting with
ASAN_OPTIONS=detect_leaks=0. This is particularly unfortunate for the
Python bindings tests, because it would be nice to know about leaks that
are libbabeltrace2's or the tests' fault. But I don't see any way
around it. For running Sphinx, we don't care about leaks.
Since we need to set some special environment variables when building
and checking if AddressSanitizer is used, it's much easier if the build
system knows that we want to use AddressSanitizer. This patch therefore
adds a new `--enable-asan` option that developers should use in order to
use AddressSanitizer, instead of manually adding -fsanitize=address in
CFLAGS/LDFLAGS.
If `--enable-asan` is used, the following command will be invoked :
$(CC) -print-file-name=libasan.so
... to find the path of the libasan.so library associated to the
compiler being used (it's important that we pre-load the right version
of libasan.so).
For the Python bindings documentation generation, the command is used in
the Makefile where Sphinx is invoked.
For tests, a boolean is written in a generated tests/utils/env.sh file,
which is sourced by tests/utils/utils.sh and the command is then used
when running the tests.
Change-Id: I5e0ca10532605cfdfda08b3ee4fcc39922480797
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/5860
Tested-by: jenkins <jenkins@lttng.org>
Reviewed-by: Michael Jeanson <mjeanson@efficios.com>
15 files changed:
AE_FEATURE_DEFAULT_DISABLE
AE_FEATURE([Werror],[Treat compiler warnings as errors.])
AE_FEATURE_DEFAULT_DISABLE
AE_FEATURE([Werror],[Treat compiler warnings as errors.])
+# When given, build with AddressSanitizer.
+AE_FEATURE_DEFAULT_DISABLE
+AE_FEATURE([asan],[Build with AddressSanitizer.])
## ##
## Check for conflicting features selection ##
## ##
## Check for conflicting features selection ##
AM_CONDITIONAL([ENABLE_BUILT_IN_PYTHON_PLUGIN_SUPPORT], AE_IS_FEATURE_ENABLED([built-in-python-plugin-support]))
AM_CONDITIONAL([ENABLE_MAN_PAGES], AE_IS_FEATURE_ENABLED([man-pages]))
AM_CONDITIONAL([ENABLE_PYTHON_COMMON_DEPS], AE_IS_FEATURE_ENABLED([python-bindings]) || AE_IS_FEATURE_ENABLED([python-plugins]))
AM_CONDITIONAL([ENABLE_BUILT_IN_PYTHON_PLUGIN_SUPPORT], AE_IS_FEATURE_ENABLED([built-in-python-plugin-support]))
AM_CONDITIONAL([ENABLE_MAN_PAGES], AE_IS_FEATURE_ENABLED([man-pages]))
AM_CONDITIONAL([ENABLE_PYTHON_COMMON_DEPS], AE_IS_FEATURE_ENABLED([python-bindings]) || AE_IS_FEATURE_ENABLED([python-plugins]))
+AM_CONDITIONAL([ENABLE_ASAN], AE_IS_FEATURE_ENABLED([asan]))
AE_IF_FEATURE_ENABLED([debug-info], [ENABLE_DEBUG_INFO_VAL=1], [ENABLE_DEBUG_INFO_VAL=0])
AC_SUBST([ENABLE_DEBUG_INFO_VAL])
AE_IF_FEATURE_ENABLED([debug-info], [ENABLE_DEBUG_INFO_VAL=1], [ENABLE_DEBUG_INFO_VAL=0])
AC_SUBST([ENABLE_DEBUG_INFO_VAL])
+AE_IF_FEATURE_ENABLED([asan], [ENABLE_ASAN=1], [ENABLE_ASAN=0])
+AC_SUBST([ENABLE_ASAN])
## ##
## Check for optionnal features dependencies ##
## ##
## Check for optionnal features dependencies ##
+# If --enable-asan is used...
+AE_IF_FEATURE_ENABLED([asan], [
+ # ... add -fsanitize=address to the *FLAGS variables.
+ ASAN_CFLAGS="-fsanitize=address"
+ ASAN_CXXFLAGS="-fsanitize=address"
+ ASAN_LDFLAGS="-fsanitize=address"
+])
# CFLAGS from libraries (the glib ones are needed for the following sizeof
# test).
# CFLAGS from libraries (the glib ones are needed for the following sizeof
# test).
-AM_CFLAGS="${PTHREAD_CFLAGS} ${GLIB_CFLAGS}"
-AM_CXXFLAGS="${PTHREAD_CFLAGS} ${GLIB_CFLAGS}"
+AM_CFLAGS="${PTHREAD_CFLAGS} ${GLIB_CFLAGS} ${ASAN_CFLAGS}"
+AM_CXXFLAGS="${PTHREAD_CFLAGS} ${GLIB_CFLAGS} ${ASAN_CXXFLAGS}"
+AM_LDFLAGS="${ASAN_LDFLAGS}"
# Check that the current size_t matches the size that glib thinks it should
# be. This catches problems on multi-arch where people try to do a 32-bit
# Check that the current size_t matches the size that glib thinks it should
# be. This catches problems on multi-arch where people try to do a 32-bit
AM_CXXFLAGS="${AM_CXXFLAGS} ${WARN_CXXFLAGS}"
AM_CFLAGS="${AM_CFLAGS} ${WARN_CFLAGS}"
AM_CXXFLAGS="${AM_CXXFLAGS} ${WARN_CXXFLAGS}"
AM_CFLAGS="${AM_CFLAGS} ${WARN_CFLAGS}"
-# Done for AM_CXXFLAGS and AM_CFLAGS.
+# Done for AM_CXXFLAGS, AM_CFLAGS and AM_LDFLAGS.
AC_SUBST(AM_CXXFLAGS)
AC_SUBST(AM_CFLAGS)
AC_SUBST(AM_CXXFLAGS)
AC_SUBST(AM_CFLAGS)
# Set global CPPFLAGS in AM_CPPFLAGS
AM_CPPFLAGS="-I\$(top_srcdir)/include -I\$(top_builddir)/src -I\$(top_srcdir)/src -include common/config.h"
# Set global CPPFLAGS in AM_CPPFLAGS
AM_CPPFLAGS="-I\$(top_srcdir)/include -I\$(top_builddir)/src -I\$(top_srcdir)/src -include common/config.h"
PP = $(PYTHON_BT2_BUILD_LIB_DIR)
LLP = $(abs_top_builddir)/src/lib/.libs
PP = $(PYTHON_BT2_BUILD_LIB_DIR)
LLP = $(abs_top_builddir)/src/lib/.libs
+# Sphinx loads the bt2 Python module and thus libbabeltrace2.so. If
+# AddressSanitizer is used, we must preload libasan.so so that libasan doesn't
+# complain about not being the first loaded library.
+#
+# Python produces some leaks, so disable leak detection (we don't care about
+# leaks here anyway).
+if ENABLE_ASAN
+MAYBE_LD_PRELOAD = LD_PRELOAD="$$($(CC) -print-file-name=libasan.so):$(LD_PRELOAD)"
+MAYBE_ASAN_OPTIONS = ASAN_OPTIONS="$(ASAN_OPTIONS),detect_leaks=0"
+endif
+
# `PATH` is used as a replacement for `LD_LIBRARY_PATH` on Windows
# builds (Cygwin, MinGW).
#
# `DYLD_LIBRARY_PATH` is used a replacement for `LD_LIBRARY_PATH` on
# macOS builds.
# `PATH` is used as a replacement for `LD_LIBRARY_PATH` on Windows
# builds (Cygwin, MinGW).
#
# `DYLD_LIBRARY_PATH` is used a replacement for `LD_LIBRARY_PATH` on
# macOS builds.
-SPHINXBUILD = PATH="$(LLP):$$PATH" PYTHONPATH="$(PP):$(SPHINX_EXT_DIR)" LD_LIBRARY_PATH="$(LLP)" DYLD_LIBRARY_PATH="$(LLP)" $(PYTHON) -m sphinx
+SPHINXBUILD = \
+ PATH="$(LLP):$$PATH" \
+ PYTHONPATH="$(PP):$(SPHINX_EXT_DIR)" \
+ LD_LIBRARY_PATH="$(LLP)" \
+ DYLD_LIBRARY_PATH="$(LLP)" \
+ $(MAYBE_LD_PRELOAD) \
+ $(MAYBE_ASAN_OPTIONS) \
+ $(PYTHON) -m sphinx
SPHINX_SRC = \
$(SPHINX_SOURCE_DIR)/common.rst \
$(SPHINX_SOURCE_DIR)/index.rst \
SPHINX_SRC = \
$(SPHINX_SOURCE_DIR)/common.rst \
$(SPHINX_SOURCE_DIR)/index.rst \
# Since the shared object used by the python bindings is not built with
# libtool, we need to add the directory containing libbabeltrace2 to the
# linker path.
# Since the shared object used by the python bindings is not built with
# libtool, we need to add the directory containing libbabeltrace2 to the
# linker path.
-AM_LDFLAGS=-L$(top_builddir)/src/lib/.libs
+AM_LDFLAGS += -L$(top_builddir)/src/lib/.libs
INSTALLED_FILES=$(builddir)/installed_files.txt
INSTALLED_FILES=$(builddir)/installed_files.txt
# -Wl,--no-as-needed is needed for recent gold linker who seems to think
# it knows better and considers libraries with constructors having
# side-effects as dead code.
# -Wl,--no-as-needed is needed for recent gold linker who seems to think
# it knows better and considers libraries with constructors having
# side-effects as dead code.
-babeltrace2_bin_LDFLAGS = $(LD_NO_AS_NEEDED)
+babeltrace2_bin_LDFLAGS = $(AM_LDFLAGS) $(LD_NO_AS_NEEDED)
# Add all the convenience libraries used by Babeltrace plugins and the
# library. They will be used when embedding plugins (--enable-built-in-plugins),
# Add all the convenience libraries used by Babeltrace plugins and the
# library. They will be used when embedding plugins (--enable-built-in-plugins),
mman.h
libcompat_la_LDFLAGS = \
mman.h
libcompat_la_LDFLAGS = \
$(LD_NO_AS_NEEDED)
noinst_HEADERS = \
$(LD_NO_AS_NEEDED)
noinst_HEADERS = \
-libbabeltrace2_ctf_writer_la_LDFLAGS = $(LT_NO_UNDEFINED) \
- -version-info $(BABELTRACE_LIBRARY_VERSION)
+libbabeltrace2_ctf_writer_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ $(LT_NO_UNDEFINED) \
+ -version-info $(BABELTRACE_LIBRARY_VERSION)
libbabeltrace2_ctf_writer_la_LIBADD = \
$(top_builddir)/src/logging/libbabeltrace2-logging.la \
libbabeltrace2_ctf_writer_la_LIBADD = \
$(top_builddir)/src/logging/libbabeltrace2-logging.la \
-libbabeltrace2_la_LDFLAGS = $(LT_NO_UNDEFINED) \
- -version-info $(BABELTRACE_LIBRARY_VERSION)
+libbabeltrace2_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ $(LT_NO_UNDEFINED) \
+ -version-info $(BABELTRACE_LIBRARY_VERSION)
libbabeltrace2_la_LIBADD = \
prio-heap/libprio-heap.la \
libbabeltrace2_la_LIBADD = \
prio-heap/libprio-heap.la \
babeltrace_plugin_ctf_la_SOURCES = plugin.c
babeltrace_plugin_ctf_la_LDFLAGS = \
babeltrace_plugin_ctf_la_SOURCES = plugin.c
babeltrace_plugin_ctf_la_LDFLAGS = \
$(LT_NO_UNDEFINED) \
-avoid-version -module $(LD_NOTEXT)
$(LT_NO_UNDEFINED) \
-avoid-version -module $(LD_NOTEXT)
plugin.c
babeltrace_plugin_lttng_utils_la_LDFLAGS = \
plugin.c
babeltrace_plugin_lttng_utils_la_LDFLAGS = \
$(LT_NO_UNDEFINED) \
-avoid-version -module $(LD_NOTEXT) \
$(ELFUTILS_LIBS)
$(LT_NO_UNDEFINED) \
-avoid-version -module $(LD_NOTEXT) \
$(ELFUTILS_LIBS)
babeltrace_plugin_text_la_SOURCES = plugin.c
babeltrace_plugin_text_la_LDFLAGS = \
babeltrace_plugin_text_la_SOURCES = plugin.c
babeltrace_plugin_text_la_LDFLAGS = \
$(LT_NO_UNDEFINED) \
-avoid-version -module $(LD_NOTEXT)
$(LT_NO_UNDEFINED) \
-avoid-version -module $(LD_NOTEXT)
babeltrace_plugin_utils_la_SOURCES = plugin.c
babeltrace_plugin_utils_la_LDFLAGS = \
babeltrace_plugin_utils_la_SOURCES = plugin.c
babeltrace_plugin_utils_la_LDFLAGS = \
$(LT_NO_UNDEFINED) \
-avoid-version -module $(LD_NOTEXT)
babeltrace_plugin_utils_la_LIBADD = \
$(LT_NO_UNDEFINED) \
-avoid-version -module $(LD_NOTEXT)
babeltrace_plugin_utils_la_LIBADD = \
python-plugin-provider.h
babeltrace2_python_plugin_provider_la_LDFLAGS = \
python-plugin-provider.h
babeltrace2_python_plugin_provider_la_LDFLAGS = \
$(LT_NO_UNDEFINED) \
-avoid-version -module \
$(PYTHON_LDFLAGS)
$(LT_NO_UNDEFINED) \
-avoid-version -module \
$(PYTHON_LDFLAGS)
# the minimal plugin
plugin_minimal_la_SOURCES = minimal.c
plugin_minimal_la_LDFLAGS = \
# the minimal plugin
plugin_minimal_la_SOURCES = minimal.c
plugin_minimal_la_LDFLAGS = \
$(LT_NO_UNDEFINED) \
-rpath / -avoid-version -module $(LD_NOTEXT)
plugin_minimal_la_LIBADD = \
$(LT_NO_UNDEFINED) \
-rpath / -avoid-version -module $(LD_NOTEXT)
plugin_minimal_la_LIBADD = \
# source/filter/sink plugin
plugin_sfs_la_SOURCES = sfs.c
plugin_sfs_la_LDFLAGS = \
# source/filter/sink plugin
plugin_sfs_la_SOURCES = sfs.c
plugin_sfs_la_LDFLAGS = \
$(LT_NO_UNDEFINED) \
-rpath / -avoid-version -module $(LD_NOTEXT)
plugin_sfs_la_LIBADD = \
$(LT_NO_UNDEFINED) \
-rpath / -avoid-version -module $(LD_NOTEXT)
plugin_sfs_la_LIBADD = \
BT_TESTS_SED_BIN="@SED@"
fi
export BT_TESTS_SED_BIN
BT_TESTS_SED_BIN="@SED@"
fi
export BT_TESTS_SED_BIN
+
+if [ "x${BT_TESTS_CC_BIN:-}" = "x" ]; then
+ BT_TESTS_CC_BIN="@CC@"
+fi
+export BT_TESTS_CC_BIN
+
+
+### Optional features ###
+
+if [ "x${BT_TESTS_ENABLE_ASAN:-}" = "x" ]; then
+ BT_TESTS_ENABLE_ASAN="@ENABLE_ASAN@"
+fi
+export BT_TESTS_ENABLE_ASAN
fi
export BT_TESTS_SED_BIN
fi
export BT_TESTS_SED_BIN
+if [ "x${BT_TESTS_CC_BIN:-}" = "x" ]; then
+ BT_TESTS_CC_BIN="cc"
+fi
+export BT_TESTS_CC_BIN
+
+
+### Optional features ###
+
+if [ "x${BT_TESTS_ENABLE_ASAN:-}" = "x" ]; then
+ BT_TESTS_ENABLE_ASAN="0"
+fi
+export BT_TESTS_ENABLE_ASAN
+
# Data files path
BT_TESTS_DATADIR="${BT_TESTS_SRCDIR}/data"
# Data files path
BT_TESTS_DATADIR="${BT_TESTS_SRCDIR}/data"
# bt2 Python bindings.
run_python_bt2() {
local env_args
# bt2 Python bindings.
run_python_bt2() {
local env_args
env_args=(
"BABELTRACE_PYTHON_BT2_NO_TRACEBACK=1" \
env_args=(
"BABELTRACE_PYTHON_BT2_NO_TRACEBACK=1" \
env_args+=("PYTHONHOME=$($BT_TESTS_PYTHON_CONFIG_BIN --prefix)")
fi
env_args+=("PYTHONHOME=$($BT_TESTS_PYTHON_CONFIG_BIN --prefix)")
fi
+ # If AddressSanitizer is used, we must preload libasan.so so that
+ # libasan doesn't complain about not being the first loaded library.
+ #
+ # Python and sed (executed as part of the libtool wrapper) produce some
+ # leaks, so we must unfortunately disable leak detection. Append it to
+ # existing ASAN_OPTIONS, such that we override the user's value if it
+ # contains detect_leaks=1.
+ if [ "x${BT_TESTS_ENABLE_ASAN:-}" = "x1" ]; then
+ lib_asan=$(${BT_TESTS_CC_BIN} -print-file-name=libasan.so)
+
+ env_args+=("LD_PRELOAD=${lib_asan}:${LD_PRELOAD:-}")
+ env_args+=("ASAN_OPTIONS=${ASAN_OPTIONS:-},detect_leaks=0")
+ fi
+
env "${env_args[@]}" "$@"
}
env "${env_args[@]}" "$@"
}