[lttng-dev] [PATCH lttng-ust v2] Fix: destructors do not run on probe provider dlclose
Mathieu Desnoyers
mathieu.desnoyers at efficios.com
Mon Feb 26 10:16:10 EST 2018
----- On Feb 22, 2018, at 3:41 PM, Francis Deslauriers francis.deslauriers at efficios.com wrote:
> Calling dlclose on the probe provider library that first loaded
> __tracepoints__disable_destructors in the symbol table does not
> unregister the probes from the callsites as the destructors are not
> executed.
>
> The __tracepoints__disable_destructors weak symbol is exposed by probe
> providers, liblttng-ust.so and liblttng-ust-tracepoint.so libraries. If
> a probe provider is loaded first into the address space, its definition
> is binded to the symbol. All the subsequent loaded libraries using the
binded -> bound
> symbol will use the existing definition of the symbol, thus creating a
> situation where liblttng-ust.so or liblttng-ust-tracepoint.so depend on
> the probe provider library.
>
> This prevents the dynamic loader from unloading the library as it is
> still in use by other libraries. Because of this, the execution of its
> destructors and the unregistration of the probes is postponed.
>
> To overcome this issue, we no longer expose this symbol in the
> tracepoint.h file to remove the explicit dependency of the probe
> provider on the symbol. We instead use the existing dlopen handle on
> liblttng-ust-tracepoint.so to get handles on getter and setter functions
> for this value.
>
> Signed-off-by: Francis Deslauriers <francis.deslauriers at efficios.com>
> ---
> include/lttng/tracepoint.h | 82 +++++++++++++++++++++++++++++++++++-----------
> liblttng-ust/tracepoint.c | 35 ++++++++++++++++++++
> 2 files changed, 98 insertions(+), 19 deletions(-)
>
> diff --git a/include/lttng/tracepoint.h b/include/lttng/tracepoint.h
> index 39f2c4d..d285347 100644
> --- a/include/lttng/tracepoint.h
> +++ b/include/lttng/tracepoint.h
> @@ -226,21 +226,6 @@ struct lttng_ust_tracepoint_dlopen {
> extern struct lttng_ust_tracepoint_dlopen tracepoint_dlopen;
> extern struct lttng_ust_tracepoint_dlopen *tracepoint_dlopen_ptr;
>
> -/* Disable tracepoint destructors. */
> -int __tracepoints__disable_destructors __attribute__((weak));
I think we may want to keep that symbol around, but move it into
tracepoint.c perhaps ? This would be for backward compatibility with
applications previously built against older lttng-ust.
We also need to read both the old symbol and new refcount in the
state getter.
> -
> -/*
> - * Programs that have threads that survive after they exit, and
> - * therefore call library destructors, should disable the tracepoint
> - * destructors by calling tracepoint_disable_destructors(). This will
> - * leak the tracepoint instrumentation library shared object, leaving
> - * its teardown to the operating system process teardown.
> - */
> -static inline void tracepoint_disable_destructors(void)
> -{
> - __tracepoints__disable_destructors = 1;
> -}
> -
> /*
> * These weak symbols, the constructor, and destructor take care of
> * registering only _one_ instance of the tracepoints per shared-ojbect
> @@ -265,6 +250,47 @@ struct lttng_ust_tracepoint_dlopen tracepoint_dlopen
> struct lttng_ust_tracepoint_dlopen *tracepoint_dlopen_ptr
> __attribute__((weak, visibility("hidden")));
>
> +/*
> + * Tracepoint dynamic linkage handling (callbacks). Hidden visibility: shared
> + * across objects in a module/main executable. The callbacks are used to
> + * control and check if the destructors should be executed.
> + */
> +struct lttng_ust_tracepoint_destructors_syms {
> + void (*tracepoint_enable_destructors)(void);
> + void (*tracepoint_disable_destructors)(void);
> + int (*tracepoint_get_destructors_state)(void);
> +};
> +
> +extern struct lttng_ust_tracepoint_destructors_syms
> tracepoint_destructors_syms;
> +extern struct lttng_ust_tracepoint_destructors_syms
> *tracepoint_destructors_syms_ptr;
> +
> +struct lttng_ust_tracepoint_destructors_syms tracepoint_destructors_syms
> + __attribute__((weak, visibility("hidden")));
> +struct lttng_ust_tracepoint_destructors_syms *tracepoint_destructors_syms_ptr
> + __attribute__((weak, visibility("hidden")));
> +
> +static inline void tracepoint_disable_destructors(void)
> +{
> + if (!tracepoint_dlopen_ptr)
> + tracepoint_dlopen_ptr = &tracepoint_dlopen;
> + if (!tracepoint_destructors_syms_ptr)
> + tracepoint_destructors_syms_ptr = &tracepoint_destructors_syms;
> + if (tracepoint_dlopen_ptr->liblttngust_handle
> + && tracepoint_destructors_syms_ptr->tracepoint_disable_destructors)
> + tracepoint_destructors_syms_ptr->tracepoint_disable_destructors();
> +}
> +
> +static inline void tracepoint_enable_destructors(void)
> +{
> + if (!tracepoint_dlopen_ptr)
> + tracepoint_dlopen_ptr = &tracepoint_dlopen;
> + if (!tracepoint_destructors_syms_ptr)
> + tracepoint_destructors_syms_ptr = &tracepoint_destructors_syms;
> + if (tracepoint_dlopen_ptr->liblttngust_handle
> + && tracepoint_destructors_syms_ptr->tracepoint_enable_destructors)
> + tracepoint_destructors_syms_ptr->tracepoint_enable_destructors();
> +}
> +
> #ifndef _LGPL_SOURCE
> static inline void lttng_ust_notrace
> __tracepoint__init_urcu_sym(void);
> @@ -335,8 +361,11 @@ __tracepoints__destroy(void)
> return;
> if (!tracepoint_dlopen_ptr)
> tracepoint_dlopen_ptr = &tracepoint_dlopen;
> - if (!__tracepoints__disable_destructors
> - && tracepoint_dlopen_ptr->liblttngust_handle
> + if (!tracepoint_destructors_syms_ptr)
> + tracepoint_destructors_syms_ptr = &tracepoint_destructors_syms;
> + if (tracepoint_dlopen_ptr->liblttngust_handle
> + && tracepoint_destructors_syms_ptr->tracepoint_get_destructors_state
> + && tracepoint_destructors_syms_ptr->tracepoint_get_destructors_state()
> && !__tracepoint_ptrs_registered) {
> ret = dlclose(tracepoint_dlopen_ptr->liblttngust_handle);
> if (ret) {
> @@ -415,6 +444,8 @@ __tracepoints__ptrs_init(void)
> dlopen("liblttng-ust-tracepoint.so.0", RTLD_NOW | RTLD_GLOBAL);
> if (!tracepoint_dlopen_ptr->liblttngust_handle)
> return;
> + if (!tracepoint_destructors_syms_ptr)
> + tracepoint_destructors_syms_ptr = &tracepoint_destructors_syms;
> tracepoint_dlopen_ptr->tracepoint_register_lib =
> URCU_FORCE_CAST(int (*)(struct lttng_ust_tracepoint * const *, int),
> dlsym(tracepoint_dlopen_ptr->liblttngust_handle,
> @@ -423,6 +454,18 @@ __tracepoints__ptrs_init(void)
> URCU_FORCE_CAST(int (*)(struct lttng_ust_tracepoint * const *),
> dlsym(tracepoint_dlopen_ptr->liblttngust_handle,
> "tracepoint_unregister_lib"));
> + tracepoint_destructors_syms_ptr->tracepoint_enable_destructors =
> + URCU_FORCE_CAST(void (*)(void),
> + dlsym(tracepoint_dlopen_ptr->liblttngust_handle,
> + "tp_enable_destructors"));
> + tracepoint_destructors_syms_ptr->tracepoint_disable_destructors =
> + URCU_FORCE_CAST(void (*)(void),
> + dlsym(tracepoint_dlopen_ptr->liblttngust_handle,
> + "tp_disable_destructors"));
> + tracepoint_destructors_syms_ptr->tracepoint_get_destructors_state =
> + URCU_FORCE_CAST(int (*)(void),
> + dlsym(tracepoint_dlopen_ptr->liblttngust_handle,
> + "tp_get_destructors_state"));
> __tracepoint__init_urcu_sym();
> if (tracepoint_dlopen_ptr->tracepoint_register_lib) {
> tracepoint_dlopen_ptr->tracepoint_register_lib(__start___tracepoints_ptrs,
> @@ -444,8 +487,9 @@ __tracepoints__ptrs_destroy(void)
> tracepoint_dlopen_ptr = &tracepoint_dlopen;
> if (tracepoint_dlopen_ptr->tracepoint_unregister_lib)
> tracepoint_dlopen_ptr->tracepoint_unregister_lib(__start___tracepoints_ptrs);
> - if (!__tracepoints__disable_destructors
> - && tracepoint_dlopen_ptr->liblttngust_handle
> + if (tracepoint_dlopen_ptr->liblttngust_handle
> + && tracepoint_destructors_syms_ptr->tracepoint_get_destructors_state
> + && tracepoint_destructors_syms_ptr->tracepoint_get_destructors_state()
> && !__tracepoint_ptrs_registered) {
> ret = dlclose(tracepoint_dlopen_ptr->liblttngust_handle);
> if (ret) {
> diff --git a/liblttng-ust/tracepoint.c b/liblttng-ust/tracepoint.c
> index 2c25d46..2e03801 100644
> --- a/liblttng-ust/tracepoint.c
> +++ b/liblttng-ust/tracepoint.c
> @@ -53,6 +53,11 @@ struct {
> /* Set to 1 to enable tracepoint debug output */
> static const int tracepoint_debug;
> static int initialized;
> +
> +/*
> + * If tracepoint_destructors_refcount > 0, tracepoint destructors are enabled.
> + */
> +static int tracepoint_destructors_refcount = 1;
> static void (*new_tracepoint_cb)(struct lttng_ust_tracepoint *);
>
> /*
> @@ -983,3 +988,33 @@ void *tp_rcu_dereference_sym_bp(void *p)
> {
> return rcu_dereference_bp(p);
> }
> +
> +/*
> + * Programs that have threads that survive after they exit, and therefore call
> + * library destructors, should disable the tracepoint destructors by calling
> + * tp_disable_destructors(). This will leak the tracepoint
> + * instrumentation library shared object, leaving its teardown to the operating
> + * system process teardown.
> + *
> + * To access and/or modify this value, users need to use a combination of
> + * dlopen(3) and dlsym(3) to get an handle on the
> + * tp_{enable,disable}_destructors and tp_get_destructors_state symbols below.
> + */
> +void tp_disable_destructors(void)
> +{
> + uatomic_dec(&tracepoint_destructors_refcount);
> +}
> +
> +void tp_enable_destructors(void)
> +{
> + uatomic_inc(&tracepoint_destructors_refcount);
> +}
> +
> +/*
> + * Returns 1 if the destructors are enabled and should be executed.
> + * Returns 0 if the destructors are disabled.
> + */
> +int tp_get_destructors_state(void)
> +{
> + return !!uatomic_read(&tracepoint_destructors_refcount);
return uatomic_read(&tracepoint_destructors_refcount) > 0);
because it can be negative.
We should also sample the state of the __tracepoints__disable_destructors symbol here,
e.g.
return !__tracepoints__disable_destructors && uatomic_read(&tracepoint_destructors_refcount) > 0);
Thanks,
Mathieu
> +}
> --
> 2.7.4
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
More information about the lttng-dev
mailing list