libqb/04-Med-add-extra-run-time-client-side-sanity-check-that.patch
Jan Pokorný b933a4c6ac
1.0.2-8 - Evolution of the previous (rhbz#1478089, rhbz#1487787)
- New test included in check phase (as per upsteam)

Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
2017-10-06 17:37:11 +02:00

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