b933a4c6ac
- New test included in check phase (as per upsteam) Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
258 lines
12 KiB
Diff
258 lines
12 KiB
Diff
From 82835b453ac212937592c5a47e992d70b6f2d82b Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= <jpokorny@redhat.com>
|
|
Date: Fri, 6 Oct 2017 17:17:26 +0200
|
|
Subject: [PATCH 4/6] Med: add extra run-time client-side sanity check that
|
|
logging will work
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
The table at the bottom concludes how the test matrix overview
|
|
introduced with a message for the preceding commit (also introducing
|
|
log_test_mock.sh runner which got reused here) looks as of this
|
|
refreshed sanity check, once it (QB_LOG_INIT_DATA macro) gets applied
|
|
(meaning "for non-libqb logging participants" so as not complicate
|
|
the matrix further).
|
|
|
|
Note that for libqb users, this implies a new link dependency on libdl,
|
|
because they may opt-in for refreshed QB_LOG_INIT_DATA sanity check that
|
|
calls out to dlopen/dlsym/dladdr directly in case of "attribute section"
|
|
being available for the particular platform, and hence immediately needs
|
|
those symbols resolved in link time. Hence, add this conditional link
|
|
dependency to libqb.pc pkg-config file under Libs variable -- we
|
|
actually restore the occurrence of "-ldl" there as it used to be present
|
|
until commit 56754d0. While doing so, also move immediate link
|
|
dependencies of libqb (if any, currently not but that may be
|
|
a regression arising from the cleanup related to the mentioned commit)
|
|
represented with the LIBS autoconf variable under Libs.private variable
|
|
in libqb.pc, where it belongs per pkg-config documentation.
|
|
|
|
The promised table follows, but first as a recap, "X(Y)" denotes
|
|
"X linked with linker Y":
|
|
X(a) .. ld.bfd < 2.29
|
|
X(b) .. ld.bfd = 2.29 (and likely on)
|
|
|
|
goes like this (values in <angle brackets> denote non-trivial change
|
|
[not mere rewording] introduced as of this commit, in comparison to
|
|
the table stated in the preceding commit):
|
|
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
#client(x)# libqb(a) usage # libqb(b) usage #
|
|
# vvv #---------+---------+---------+---------+---------+---------+
|
|
# V # direct | libX(a) : libX(b) # direct | libX(a) : libX(b) #
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
# x = a # OK | OK : BAD[*2] #<BAD[*E]>|<BAD[*F]>:<BAD[*G]>#
|
|
# x = b # BAD[*A] | BAD[*B] : BAD[*C] #<BAD[*E]>|<BAD[*F]>:<BAD[*G]>#
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
|
|
[*1] client logging not working
|
|
[*2] interlib logging not working
|
|
[*3] both client and interlib logging not working
|
|
|
|
[*A] boils down to [*1], unless QB_LOG_INIT_DATA used on client side,
|
|
which then fails on
|
|
"non-empty implicit callsite section, otherwise target's linkage at
|
|
fault and logging would not work reliably"
|
|
assertion
|
|
[*B] boils down to [*1], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which then fails on
|
|
"non-empty implicit callsite section, otherwise target's linkage at
|
|
fault and logging would not work reliably"
|
|
assertion
|
|
[*C] boils down to [*3], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which then fails on
|
|
"non-empty implicit callsite section, otherwise target's linkage at
|
|
fault and logging would not work reliably"
|
|
assertion
|
|
[*E] boils down to [*1], unless QB_LOG_INIT_DATA used on client side,
|
|
which then fails on
|
|
"target's callsite section self-observable, otherwise target's
|
|
linkage at fault and logging would not work reliably"
|
|
assertion
|
|
[*F] boils down to [*3], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which then fails on
|
|
"libqb's callsite section non-empty, otherwise libqb's linkage at
|
|
fault and logging would not work reliably"
|
|
assertion
|
|
[*G] boils down to [*3], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which then fails on
|
|
"target's callsite section self-observable, otherwise target's linkage
|
|
at fault and logging would not work reliably"
|
|
assertion
|
|
|
|
Note: the only problematic (i.e. not captured automatically by the
|
|
QB_LOG_INIT_DATA macro presumably utilized at every non-libqb logging
|
|
system participant in the form of a discrete compilation unit)
|
|
combination is the one intersecting at "BAD[*2]" pertaining "everything
|
|
but interlib compiled with ld.bfd < 2.29". It would, of course, be
|
|
solvable as well, but presumably not in an easy way, and that use case
|
|
should not be as frequent.
|
|
|
|
Takeway: whenever your target (library or client program) actively
|
|
utilizes logging (meaning it emits at least a single log message),
|
|
YOU ARE strongly ENCOURAGED TO USE QB_LOG_INIT_DATA macro function
|
|
at (exactly) one of the source code files (presumably the main one)
|
|
per respective target's compilation unit. It will alleviate the
|
|
hassles possibly caused by downgrading libqb to the linker-vs-libqb
|
|
incompatibly compiled one or in similar circumstances arising merely
|
|
from the linker behaviour change, which the current build system/code
|
|
quake is all about.
|
|
|
|
Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
|
|
---
|
|
configure.ac | 1 +
|
|
include/qb/qblog.h | 67 ++++++++++++++++++++++++++++---
|
|
lib/libqb.pc.in | 3 +-
|
|
tests/functional/log.am | 2 +-
|
|
tests/functional/log_external/Makefile.am | 8 +++-
|
|
5 files changed, 72 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/configure.ac b/configure.ac
|
|
index db7bc68..5601302 100644
|
|
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -640,6 +640,7 @@ if test "x${GCC}" = xyes; then
|
|
AC_DEFINE([QB_HAVE_ATTRIBUTE_SECTION], 1,
|
|
[Enabling code using __attribute__((section))])
|
|
PACKAGE_FEATURES="$PACKAGE_FEATURES attribute-section"
|
|
+ AC_SUBST([client_dlopen_LIBS],[$dlopen_LIBS])
|
|
fi
|
|
fi
|
|
|
|
diff --git a/include/qb/qblog.h b/include/qb/qblog.h
|
|
index d38449d..e49c86c 100644
|
|
--- a/include/qb/qblog.h
|
|
+++ b/include/qb/qblog.h
|
|
@@ -42,6 +42,11 @@ extern "C" {
|
|
#undef QB_HAVE_ATTRIBUTE_SECTION
|
|
#endif /* S_SPLINT_S */
|
|
|
|
+#ifdef QB_HAVE_ATTRIBUTE_SECTION
|
|
+#include <assert.h>
|
|
+#include <dlfcn.h>
|
|
+#endif
|
|
+
|
|
/**
|
|
* @file qblog.h
|
|
* The logging API provides four main parts (basics, filtering, threading & blackbox).
|
|
@@ -268,18 +273,70 @@ typedef void (*qb_log_filter_fn)(struct qb_log_callsite * cs);
|
|
extern struct qb_log_callsite QB_ATTR_SECTION_START[];
|
|
extern struct qb_log_callsite QB_ATTR_SECTION_STOP[];
|
|
|
|
-/* optional on-demand self-check of (1) toolchain sanity and (2) non-void
|
|
+/* Related to the next macro, which is -- contrary to this -- public API */
|
|
+#ifndef _GNU_SOURCE
|
|
+#warning "without _GNU_SOURCE defined (directly or not), QB_LOG_INIT_DATA cannot check libqb's own sanity"
|
|
+#define QB_NONAPI_LOG_INIT_DATA_EXTRA_
|
|
+#else
|
|
+#define QB_NONAPI_LOG_INIT_DATA_EXTRA_ \
|
|
+ /* libqb sanity (locating libqb by it's relatively unique \
|
|
+ -- and currently only such per-linkage global one -- \
|
|
+ non-functional symbol, due to possible confusion otherwise) */ \
|
|
+ if (dladdr(dlsym(RTLD_DEFAULT, "facilitynames"), &work_dli) \
|
|
+ && (work_handle = dlopen(work_dli.dli_fname, \
|
|
+ RTLD_LOCAL|RTLD_LAZY)) != NULL) { \
|
|
+ work_s1 = (struct qb_log_callsite *) \
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_START_STR); \
|
|
+ assert(("libqb's callsite section not 1st in global resolution, \
|
|
+otherwise target's linkage at fault and logging would not work reliably",\
|
|
+ work_s2 != work_s1)); \
|
|
+ work_s2 = (struct qb_log_callsite *) \
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_STOP_STR); \
|
|
+ assert(("libqb's callsite section non-empty, otherwise libqb's \
|
|
+linkage at fault and logging would not work reliably", \
|
|
+ work_s1 != work_s2)); \
|
|
+ dlclose(work_handle); }
|
|
+#endif
|
|
+
|
|
+/**
|
|
+ * Optional on-demand self-check of (1) toolchain sanity and (2) non-void
|
|
* (implied by the justifying existence of at least a single logging callsite
|
|
* ~ qb_logt invocation) employment of logging subsystem in the target binary,
|
|
* which is presumably assured by it's author as of relying on this macro;
|
|
* only effective when link-time ("run-time amortizing") callsite collection
|
|
- * is available and may be extended in future for more in-depth self-validation
|
|
+ * is.
|
|
+ *
|
|
+ * Applying this macro in the target program/library is strongly recommended
|
|
+ * whenever the logging as framed by this header file is in use. Moreover,
|
|
+ * it's important to state that using this check while not ensuring _GNU_SOURCE
|
|
+ * macro definition is present at compile-time means only half of the available
|
|
+ * sanity checking will be performed, possibly resulting in libqb's own
|
|
+ * internally logged messages being lost.
|
|
*/
|
|
#define QB_LOG_INIT_DATA(name) \
|
|
void name(void); \
|
|
- void name(void) \
|
|
- { assert(("non-empty callsite section", \
|
|
- QB_ATTR_SECTION_START != QB_ATTR_SECTION_STOP)); } \
|
|
+ void name(void) { \
|
|
+ void *work_handle; struct qb_log_callsite *work_s1, *work_s2; \
|
|
+ Dl_info work_dli; \
|
|
+ /* our own (target's) sanity */ \
|
|
+ if ((work_handle = dlopen(NULL, RTLD_LOCAL|RTLD_LAZY)) != NULL) { \
|
|
+ work_s1 = (struct qb_log_callsite *) \
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_START_STR); \
|
|
+ work_s2 = (struct qb_log_callsite *) \
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_STOP_STR); \
|
|
+ assert(("target's callsite section self-observable, otherwise \
|
|
+target's linkage at fault and logging would not work reliably", \
|
|
+ work_s1 != NULL && work_s2 != NULL)); \
|
|
+ work_s2 = (struct qb_log_callsite *) \
|
|
+ dlsym(RTLD_DEFAULT, QB_ATTR_SECTION_START_STR); \
|
|
+ assert(("target's callsite section 1st in global resolution, \
|
|
+otherwise target's linkage at fault and logging would not work reliably",\
|
|
+ work_s1 == work_s2)); } \
|
|
+ QB_NONAPI_LOG_INIT_DATA_EXTRA_; \
|
|
+ /* original "cherry on the cake" check, possibly expendable */ \
|
|
+ assert(("non-empty implicit callsite section, otherwise target's \
|
|
+linkage at fault and logging would not work reliably", \
|
|
+ QB_ATTR_SECTION_START != QB_ATTR_SECTION_STOP)); } \
|
|
void __attribute__ ((constructor)) name(void);
|
|
#else
|
|
#define QB_LOG_INIT_DATA(name)
|
|
diff --git a/lib/libqb.pc.in b/lib/libqb.pc.in
|
|
index 8a8d0ba..37d27b7 100644
|
|
--- a/lib/libqb.pc.in
|
|
+++ b/lib/libqb.pc.in
|
|
@@ -7,5 +7,6 @@ Name: libqb
|
|
Version: @PACKAGE_VERSION@
|
|
Description: libqb
|
|
Requires:
|
|
-Libs: -L${libdir} -lqb @LIBS@
|
|
+Libs: -L${libdir} -lqb @client_dlopen_LIBS@
|
|
+Libs.private: @LIBS@
|
|
Cflags: -I${includedir}
|
|
diff --git a/tests/functional/log.am b/tests/functional/log.am
|
|
index 5fe1ae7..0753fa5 100644
|
|
--- a/tests/functional/log.am
|
|
+++ b/tests/functional/log.am
|
|
@@ -20,7 +20,7 @@
|
|
MAINTAINERCLEANFILES = Makefile.in
|
|
CLEANFILES = log_client.err.real log_interlib_client.err.real
|
|
|
|
-AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
|
|
+AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_builddir)/include -I$(top_srcdir)/include
|
|
|
|
noinst_PROGRAMS = log_client log_interlib_client
|
|
# cannot use {check,noinst}_LTLIBRARIES because it leads to solely static lib
|
|
diff --git a/tests/functional/log_external/Makefile.am b/tests/functional/log_external/Makefile.am
|
|
index 36aa0af..ca1c8a5 100644
|
|
--- a/tests/functional/log_external/Makefile.am
|
|
+++ b/tests/functional/log_external/Makefile.am
|
|
@@ -19,5 +19,9 @@
|
|
|
|
include ../log.am
|
|
|
|
-log_client_LDFLAGS = -lqb
|
|
-liblog_inter_la_LIBADD = -lqb
|
|
+# while linking with system-wide version of libqb, we are still pursuing
|
|
+# local in-tree header file, hence we need to link with dynamic linking
|
|
+# library (which is a prerequisite for using QB_LOG_INIT_DATA defined
|
|
+# in qblog.h) explicitly
|
|
+log_client_LDFLAGS = -lqb @client_dlopen_LIBS@
|
|
+liblog_inter_la_LIBADD = -lqb @client_dlopen_LIBS@
|
|
--
|
|
2.14.2
|
|
|