libust events: lazy probe binding
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 26 Aug 2011 00:22:37 +0000 (20:22 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 26 Aug 2011 00:22:37 +0000 (20:22 -0400)
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 <mathieu.desnoyers@efficios.com>
include/ust/lttng-events.h
libust/ltt-events.c
libust/ltt-probes.c
libust/ltt-tracer-core.h

index 31938327059c09ab218d92eed4c0cae0ba84fcf4..303d0dafc0a9d6393bb25ff8ff12b789c1e26455 100644 (file)
@@ -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);
index 94d57c3372b5c7b7a58a43899c0c658272c0c35f..39167d4b82ccdcc4f10668d0b8ab2ab09be1f732 100644 (file)
@@ -11,6 +11,7 @@
 #define _GNU_SOURCE
 #include <stdio.h>
 #include <urcu/list.h>
+#include <urcu/hlist.h>
 #include <pthread.h>
 #include <urcu-bp.h>
 #include <urcu/compiler.h>
 #include <ust/usterr-signal-safe.h>
 #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 <ust/kcompat/jhash.h>
+
+/*
+ * 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"
index 5b3a0cb217c208878d957f114aae2c7cf787b1bb..76156f03d5bb7490ba67e8468c3b499d9ee99700 100644 (file)
 #include <ust/core.h>
 #include <ust/lttng-events.h>
 
+#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;
index c793682770f7f3df1ffdc4a747c35ee20bb29d63..1a329780f7875e6105df70b3cf88733640849633 100644 (file)
@@ -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 */
This page took 0.028912 seconds and 4 git commands to generate.