From: Mathieu Desnoyers Date: Fri, 26 Aug 2011 00:22:37 +0000 (-0400) Subject: libust events: lazy probe binding X-Git-Tag: v1.9.1~249 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=8165c8da23fb8a3395d7e829f3d5734e18dd7db9;p=lttng-ust.git libust events: lazy probe binding Ability to cope with abritrary execution order of constructors, mainly that libust constructor will always be called before the constructor of the main programs (which will likely contain the probe callbacks and descriptions from TRACEPOINT_EVENT) requires us to support lazy binding between event and probes, "fixed" at probe registration time. Allowing this lazy binding natively supports instrumentation of .so (without changing the applications), and supports instrumenting shared objects with dlopen(). Signed-off-by: Mathieu Desnoyers --- diff --git a/include/ust/lttng-events.h b/include/ust/lttng-events.h index 31938327..303d0daf 100644 --- a/include/ust/lttng-events.h +++ b/include/ust/lttng-events.h @@ -176,6 +176,8 @@ struct lttng_probe_desc { struct cds_list_head head; /* chain registered probes */ }; +struct ust_pending_probe; + /* * ltt_event structure is referred to by the tracing fast path. It must be * kept small. @@ -191,6 +193,7 @@ struct ltt_event { union { } u; struct cds_list_head list; /* Event list */ + struct ust_pending_probe *pending_probe; int metadata_dumped:1; }; @@ -236,6 +239,7 @@ struct ltt_channel { struct ltt_session *session; int objd; /* Object associated to channel */ unsigned int free_event_id; /* Next event ID to allocate */ + unsigned int used_event_id; /* Max allocated event IDs */ struct cds_list_head list; /* Channel list */ struct ltt_channel_ops *ops; int header_type; /* 0: unset, 1: compact, 2: large */ @@ -297,6 +301,7 @@ void synchronize_trace(void); int ltt_probe_register(struct lttng_probe_desc *desc); void ltt_probe_unregister(struct lttng_probe_desc *desc); +int pending_probe_fix_events(const struct lttng_event_desc *desc); const struct lttng_event_desc *ltt_event_get(const char *name); void ltt_event_put(const struct lttng_event_desc *desc); int ltt_probes_init(void); diff --git a/libust/ltt-events.c b/libust/ltt-events.c index 94d57c33..39167d4b 100644 --- a/libust/ltt-events.c +++ b/libust/ltt-events.c @@ -11,6 +11,7 @@ #define _GNU_SOURCE #include #include +#include #include #include #include @@ -24,12 +25,46 @@ #include #include "ust/core.h" #include "ltt-tracer.h" +#include "ltt-tracer-core.h" #include "ust/wait.h" #include "../libringbuffer/shm.h" +typedef u32 uint32_t; +#include + +/* + * The sessions mutex is the centralized mutex across UST tracing + * control and probe registration. + */ +static DEFINE_MUTEX(sessions_mutex); + +void lock_ust(void) +{ + pthread_mutex_lock(&sessions_mutex); +} + +void unlock_ust(void) +{ + pthread_mutex_unlock(&sessions_mutex); +} + static CDS_LIST_HEAD(sessions); static CDS_LIST_HEAD(ltt_transport_list); -static DEFINE_MUTEX(sessions_mutex); + +/* + * Pending probes hash table, containing the registered ltt events for + * which tracepoint probes are still missing. Protected by the sessions + * mutex. + */ +#define PENDING_PROBE_HASH_BITS 6 +#define PENDING_PROBE_HASH_SIZE (1 << PENDING_PROBE_HASH_BITS) +static struct cds_hlist_head pending_probe_table[PENDING_PROBE_HASH_SIZE]; + +struct ust_pending_probe { + struct ltt_event *event; + struct cds_hlist_node node; + char name[]; +}; static void _ltt_event_destroy(struct ltt_event *event); static void _ltt_channel_destroy(struct ltt_channel *chan); @@ -41,6 +76,81 @@ int _ltt_event_metadata_statedump(struct ltt_session *session, static int _ltt_session_metadata_statedump(struct ltt_session *session); +/* + * called at event creation if probe is missing. + * called with session mutex held. + */ +static +int add_pending_probe(struct ltt_event *event, const char *name) +{ + struct cds_hlist_head *head; + struct cds_hlist_node *node; + struct ust_pending_probe *e; + size_t name_len = strlen(name) + 1; + u32 hash = jhash(name, name_len - 1, 0); + + head = &pending_probe_table[hash & (PENDING_PROBE_HASH_SIZE - 1)]; + e = zmalloc(sizeof(struct ust_pending_probe) + name_len); + if (!e) + return -ENOMEM; + memcpy(&e->name[0], name, name_len); + cds_hlist_add_head(&e->node, head); + e->event = event; + event->pending_probe = e; + return 0; +} + +/* + * remove a pending probe. called when at event teardown and when an + * event is fixed (probe is loaded). + * called with session mutex held. + */ +static +void remove_pending_probe(struct ust_pending_probe *e) +{ + if (!e) + return; + cds_hlist_del(&e->node); + free(e); +} + +/* + * Called at library load: connect the probe on the events pending on + * probe load. + * called with session mutex held. + */ +int pending_probe_fix_events(const struct lttng_event_desc *desc) +{ + struct cds_hlist_head *head; + struct cds_hlist_node *node, *p; + struct ust_pending_probe *e; + const char *name = desc->name; + size_t name_len = strlen(name) + 1; + u32 hash = jhash(name, name_len - 1, 0); + int ret = 0; + + head = &pending_probe_table[hash & (PENDING_PROBE_HASH_SIZE - 1)]; + cds_hlist_for_each_entry_safe(e, node, p, head, node) { + struct ltt_event *event; + struct ltt_channel *chan; + + if (strcmp(name, e->name)) + continue; + event = e->event; + chan = event->chan; + assert(!event->desc); + event->desc = desc; + event->pending_probe = NULL; + remove_pending_probe(e); + ret |= __tracepoint_probe_register(name, + event->desc->probe_callback, + event); + ret |= _ltt_event_metadata_statedump(chan->session, chan, + event); + } + return ret; +} + void synchronize_trace(void) { synchronize_rcu(); @@ -264,21 +374,25 @@ struct ltt_event *ltt_event_create(struct ltt_channel *chan, int ret; pthread_mutex_lock(&sessions_mutex); - if (chan->free_event_id == -1UL) + if (chan->used_event_id == -1UL) goto full; /* * This is O(n^2) (for each event, the loop is called at event * creation). Might require a hash if we have lots of events. */ cds_list_for_each_entry(event, &chan->session->events, list) - if (!strcmp(event->desc->name, event_param->name)) + if (event->desc && !strcmp(event->desc->name, event_param->name)) goto exist; event = zmalloc(sizeof(struct ltt_event)); if (!event) goto cache_error; event->chan = chan; event->filter = filter; - event->id = chan->free_event_id++; + /* + * used_event_id counts the maximum number of event IDs that can + * register if all probes register. + */ + chan->used_event_id++; event->enabled = 1; event->instrumentation = event_param->instrumentation; /* Populate ltt_event structure before tracepoint registration. */ @@ -286,29 +400,44 @@ struct ltt_event *ltt_event_create(struct ltt_channel *chan, switch (event_param->instrumentation) { case LTTNG_UST_TRACEPOINT: event->desc = ltt_event_get(event_param->name); - if (!event->desc) - goto register_error; - ret = __tracepoint_probe_register(event_param->name, - event->desc->probe_callback, - event); - if (ret) - goto register_error; + if (event->desc) { + ret = __tracepoint_probe_register(event_param->name, + event->desc->probe_callback, + event); + if (ret) + goto register_error; + event->id = chan->free_event_id++; + } else { + /* + * If the probe is not present, event->desc stays NULL, + * waiting for the probe to register, and the event->id + * stays unallocated. + */ + ret = add_pending_probe(event, event_param->name); + if (ret) + goto add_pending_error; + } break; default: WARN_ON_ONCE(1); } - ret = _ltt_event_metadata_statedump(chan->session, chan, event); - if (ret) - goto statedump_error; + if (event->desc) { + ret = _ltt_event_metadata_statedump(chan->session, chan, event); + if (ret) + goto statedump_error; + } cds_list_add(&event->list, &chan->session->events); pthread_mutex_unlock(&sessions_mutex); return event; statedump_error: - WARN_ON_ONCE(__tracepoint_probe_unregister(event_param->name, - event->desc->probe_callback, - event)); - ltt_event_put(event->desc); + if (event->desc) { + WARN_ON_ONCE(__tracepoint_probe_unregister(event_param->name, + event->desc->probe_callback, + event)); + ltt_event_put(event->desc); + } +add_pending_error: register_error: free(event); cache_error: @@ -327,11 +456,16 @@ int _ltt_event_unregister(struct ltt_event *event) switch (event->instrumentation) { case LTTNG_UST_TRACEPOINT: - ret = __tracepoint_probe_unregister(event->desc->name, - event->desc->probe_callback, - event); - if (ret) - return ret; + if (event->desc) { + ret = __tracepoint_probe_unregister(event->desc->name, + event->desc->probe_callback, + event); + if (ret) + return ret; + } else { + remove_pending_probe(event->pending_probe); + ret = 0; + } break; default: WARN_ON_ONCE(1); @@ -347,7 +481,9 @@ void _ltt_event_destroy(struct ltt_event *event) { switch (event->instrumentation) { case LTTNG_UST_TRACEPOINT: - ltt_event_put(event->desc); + if (event->desc) { + ltt_event_put(event->desc); + } break; default: WARN_ON_ONCE(1); @@ -598,6 +734,11 @@ int _ltt_event_metadata_statedump(struct ltt_session *session, return 0; if (chan == session->metadata) return 0; + /* + * Don't print events for which probe load is pending. + */ + if (!event->desc) + return 0; ret = lttng_metadata_printf(session, "event {\n" diff --git a/libust/ltt-probes.c b/libust/ltt-probes.c index 5b3a0cb2..76156f03 100644 --- a/libust/ltt-probes.c +++ b/libust/ltt-probes.c @@ -14,8 +14,12 @@ #include #include +#include "ltt-tracer-core.h" + +/* + * probe list is protected by lock_ust()/unlock_ust(). + */ static CDS_LIST_HEAD(probe_list); -static DEFINE_MUTEX(probe_mutex); static const struct lttng_event_desc *find_event(const char *name) @@ -37,7 +41,7 @@ int ltt_probe_register(struct lttng_probe_desc *desc) int ret = 0; int i; - pthread_mutex_lock(&probe_mutex); + lock_ust(); /* * TODO: This is O(N^2). Turn into a hash table when probe registration * overhead becomes an issue. @@ -49,25 +53,34 @@ int ltt_probe_register(struct lttng_probe_desc *desc) } } cds_list_add(&desc->head, &probe_list); + + /* + * fix the events awaiting probe load. + */ + for (i = 0; i < desc->nr_events; i++) { + ret = pending_probe_fix_events(&desc->event_desc[i]); + assert(!ret); + } end: - pthread_mutex_unlock(&probe_mutex); + unlock_ust(); return ret; } void ltt_probe_unregister(struct lttng_probe_desc *desc) { - pthread_mutex_lock(&probe_mutex); + lock_ust(); cds_list_del(&desc->head); - pthread_mutex_unlock(&probe_mutex); + unlock_ust(); } +/* + * called with UST lock held. + */ const struct lttng_event_desc *ltt_event_get(const char *name) { const struct lttng_event_desc *event; - pthread_mutex_lock(&probe_mutex); event = find_event(name); - pthread_mutex_unlock(&probe_mutex); if (!event) return NULL; return event; diff --git a/libust/ltt-tracer-core.h b/libust/ltt-tracer-core.h index c7936827..1a329780 100644 --- a/libust/ltt-tracer-core.h +++ b/libust/ltt-tracer-core.h @@ -34,4 +34,7 @@ struct ltt_session; struct ltt_channel; struct ltt_event; +void lock_ust(void); +void unlock_ust(void); + #endif /* _LTT_TRACER_CORE_H */