Add callsite support
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 2 Oct 2012 19:00:54 +0000 (15:00 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 2 Oct 2012 19:00:54 +0000 (15:00 -0400)
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
include/lttng/tracepoint-types.h
include/lttng/tracepoint.h
include/lttng/ust-events.h
liblttng-ust/ltt-events.c
liblttng-ust/tracepoint-internal.h
liblttng-ust/tracepoint.c

index 0c0c723729363a6cfd5a69813d37f76e7dca099a..27500d40451535ada1e63c227e80c6a7bc291a0f 100644 (file)
@@ -30,4 +30,13 @@ struct tracepoint {
        char padding[TRACEPOINT_PADDING];
 };
 
+#define TRACEPOINT_CALLSITE_PADDING    16
+struct tracepoint_callsite {
+       const struct tracepoint *tp;
+       const char *func;
+       const char *file;
+       unsigned int lineno;
+       char padding[TRACEPOINT_CALLSITE_PADDING];
+};
+
 #endif /* _LTTNG_TRACEPOINT_TYPES_H */
index 8b08914418f0c98b047c46d53ed77637fb838073..8086b648a2a4be8d9b7ed04cbe21977a3f039929 100644 (file)
@@ -37,6 +37,7 @@ extern "C" {
 
 #define tracepoint(provider, name, ...)                                            \
        do {                                                                \
+               TRACEPOINT_CALLSITE(provider, name);                        \
                STAP_PROBEV(provider, name, ## __VA_ARGS__);                \
                if (caa_unlikely(__tracepoint_##provider##___##name.state)) \
                        __tracepoint_cb_##provider##___##name(__VA_ARGS__); \
@@ -205,6 +206,103 @@ struct tracepoint_dlopen {
 
 extern struct tracepoint_dlopen tracepoint_dlopen;
 
+struct tracepoint_callsite_dlopen {
+       void *liblttngust_handle;
+
+       int (*tracepoint_register_lib_callsite)(struct tracepoint_callsite * const *tp_start,
+               int tp_count);
+       int (*tracepoint_unregister_lib_callsite)(struct tracepoint_callsite * const *tp_start,
+               int tp_count);
+};
+
+struct tracepoint_callsite_dlopen tracepoint_callsite_dlopen
+       __attribute__((weak, visibility("hidden")));
+int __tracepoint_callsite_registered
+       __attribute__((weak, visibility("hidden")));
+
+/*
+ * Note: to allow PIC code, we need to allow the linker to update the pointers
+ * in the __tracepoints_callsite_ptrs section.
+ * Therefore, this section is _not_ const (read-only).
+ */
+#define TRACEPOINT_CALLSITE(_provider, _name)                                  \
+       static struct tracepoint_callsite                                       \
+               __tracepoint_callsite_##_provider##___##_name                   \
+               __attribute__((section("__tracepoint_callsite"))) =             \
+               {                                                               \
+                       .tp = &__tracepoint_##_provider##___##_name,            \
+                       .func = __func__,                                       \
+                       .file = __FILE__,                                       \
+                       .lineno = __LINE__,                                     \
+               };                                                              \
+       static struct tracepoint_callsite *                                     \
+               __tracepoint_callsite_ptr_##_provider##___##_name               \
+               __attribute__((used, section("__tracepoint_callsite_ptrs"))) =  \
+                       &__tracepoint_callsite_##_provider##___##_name
+
+/*
+ * These weak symbols, the constructor, and destructor take care of
+ * registering only _one_ instance of the tracepoint callsite per
+ * shared-ojbect (or for the whole main program).
+ */
+extern struct tracepoint_callsite * const __start___tracepoint_callsite_ptrs[]
+       __attribute__((weak, visibility("hidden")));
+extern struct tracepoint_callsite * const __stop___tracepoint_callsite_ptrs[]
+       __attribute__((weak, visibility("hidden")));
+
+/*
+ * We need at least one pointer in the section.
+ */
+static struct tracepoint_callsite *
+       __tracepoint_callsite_ptr_dummy
+       __attribute__((used, section("__tracepoint_callsite_ptrs"))) =
+               NULL;
+
+static void lttng_ust_notrace __attribute__((constructor))
+__tracepoint_callsite__init(void);
+static void
+__tracepoint_callsite__init(void)
+{
+       if (__tracepoint_callsite_registered++)
+               return;
+
+       tracepoint_callsite_dlopen.liblttngust_handle =
+               dlopen("liblttng-ust-tracepoint.so.0", RTLD_NOW | RTLD_GLOBAL);
+       if (!tracepoint_callsite_dlopen.liblttngust_handle)
+               return;
+       tracepoint_callsite_dlopen.tracepoint_register_lib_callsite =
+               URCU_FORCE_CAST(int (*)(struct tracepoint_callsite * const *, int),
+                               dlsym(tracepoint_callsite_dlopen.liblttngust_handle,
+                                       "tracepoint_register_lib_callsite"));
+       tracepoint_callsite_dlopen.tracepoint_unregister_lib_callsite =
+               URCU_FORCE_CAST(int (*)(struct tracepoint_callsite * const *, int),
+                               dlsym(tracepoint_callsite_dlopen.liblttngust_handle,
+                                       "tracepoint_unregister_lib_callsite"));
+       tracepoint_callsite_dlopen.tracepoint_register_lib_callsite(__start___tracepoint_callsite_ptrs,
+                               __stop___tracepoint_callsite_ptrs -
+                               __start___tracepoint_callsite_ptrs);
+}
+
+static void lttng_ust_notrace __attribute__((destructor))
+__tracepoint_callsite__destroy(void);
+static void
+__tracepoint_callsite__destroy(void)
+{
+       int ret;
+
+       if (--__tracepoint_callsite_registered)
+               return;
+       if (tracepoint_callsite_dlopen.tracepoint_unregister_lib_callsite)
+               tracepoint_callsite_dlopen.tracepoint_unregister_lib_callsite(__start___tracepoint_callsite_ptrs,
+                               __stop___tracepoint_callsite_ptrs -
+                               __start___tracepoint_callsite_ptrs);
+       if (tracepoint_callsite_dlopen.liblttngust_handle) {
+               ret = dlclose(tracepoint_callsite_dlopen.liblttngust_handle);
+               assert(!ret);
+               memset(&tracepoint_callsite_dlopen, 0, sizeof(tracepoint_callsite_dlopen));
+       }
+}
+
 #ifdef TRACEPOINT_DEFINE
 
 /*
index 0e19ed2229ac9c7b417aee01be5113e0701351fe..b076835794126345eb4f7f1d3b2e0444818a5bca 100644 (file)
@@ -299,6 +299,11 @@ struct ust_pending_probe;
 struct ltt_event;
 struct lttng_ust_filter_bytecode;
 
+struct lttng_callsite {
+       const struct tracepoint_callsite *tp_cs;
+       struct cds_hlist_node node;     /* Callsite hash table node */
+};
+
 /*
  * ltt_event structure is referred to by the tracing fast path. It must be
  * kept small.
index 32135c85c03e92e2e79557116abbc265157e3a67..0b6e61c41a44f855f26a3724d174b101477b1fcb 100644 (file)
@@ -98,6 +98,14 @@ struct ust_pending_probe {
        char name[];
 };
 
+/*
+ * Callsite hash table, containing the registered callsites.
+ * Protected by the sessions mutex.
+ */
+#define CALLSITE_HASH_BITS             6
+#define CALLSITE_HASH_SIZE             (1 << CALLSITE_HASH_BITS)
+static struct cds_hlist_head callsite_table[CALLSITE_HASH_SIZE];
+
 static void _ltt_event_destroy(struct ltt_event *event);
 static void _ltt_wildcard_destroy(struct session_wildcard *sw);
 static void _ltt_channel_destroy(struct ltt_channel *chan);
@@ -107,6 +115,10 @@ int _ltt_event_metadata_statedump(struct ltt_session *session,
                                  struct ltt_channel *chan,
                                  struct ltt_event *event);
 static
+int _ltt_callsite_metadata_statedump(struct ltt_session *session,
+                                 struct lttng_callsite *callsite);
+
+static
 int _ltt_session_metadata_statedump(struct ltt_session *session);
 
 int ltt_loglevel_match(const struct lttng_event_desc *desc,
@@ -302,6 +314,104 @@ int pending_probe_fix_events(const struct lttng_event_desc *desc)
        return ret;
 }
 
+/*
+ * Called at library load: add callsite information.
+ * Takes the session mutex.
+ * Should _not_ be called with tracepoint mutex held (would cause
+ * deadlock with session mutex).
+ */
+int lttng_callsite_add(const struct tracepoint_callsite *tp_cs)
+{
+       struct cds_hlist_head *head;
+       struct lttng_callsite *cs_node;
+       struct ltt_session *session;
+       uint32_t hash;
+       int ret;
+
+       ust_lock();
+       /* hash by pointer value */
+       hash = jhash(&tp_cs, sizeof(tp_cs), 0);
+       head = &callsite_table[hash & (CALLSITE_HASH_SIZE - 1)];
+       cs_node = zmalloc(sizeof(struct lttng_callsite));
+       if (!cs_node)
+               goto error_mem;
+       cds_hlist_add_head(&cs_node->node, head);
+       cs_node->tp_cs = tp_cs;
+
+       /* print metadata for each session */
+       cds_list_for_each_entry(session, &sessions, list) {
+               ret = _ltt_callsite_metadata_statedump(session, cs_node);
+               assert(!ret);
+       }
+       ust_unlock();
+       return 0;
+
+error_mem:
+       ust_unlock();
+       return -ENOMEM;
+}
+
+/*
+ * Called at library unload: remove callsite information.
+ * Takes the session mutex.
+ * Should _not_ be called with tracepoint mutex held (would cause
+ * deadlock with session mutex).
+ */
+int lttng_callsite_remove(const struct tracepoint_callsite *tp_cs)
+{
+       struct cds_hlist_head *head;
+       struct cds_hlist_node *node;
+       struct lttng_callsite *cs_node;
+       uint32_t hash;
+       int found = 0;
+
+       ust_lock();
+       /* hash by pointer value */
+       hash = jhash(&tp_cs, sizeof(tp_cs), 0);
+       head = &callsite_table[hash & (CALLSITE_HASH_SIZE - 1)];
+       cds_hlist_for_each_entry(cs_node, node, head, node) {
+               if (cs_node->tp_cs == tp_cs) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (!found)
+               goto error;
+       cds_hlist_del(&cs_node->node);
+       free(cs_node);
+       ust_unlock();
+       return 0;
+
+error:
+       ust_unlock();
+       return -ENOENT;
+}
+
+/*
+ * Called with ust mutex held.
+ */
+static
+int _callsite_session_metadata_statedump(struct ltt_session *session)
+{
+       int ret = 0;
+       unsigned int i;
+
+       for (i = 0; i < CALLSITE_HASH_SIZE; i++) {
+               struct cds_hlist_head *head;
+               struct cds_hlist_node *node;
+               struct lttng_callsite *cs_node;
+
+               head = &callsite_table[i];
+               cds_hlist_for_each_entry(cs_node, node, head, node) {
+                       ret = _ltt_callsite_metadata_statedump(session,
+                                                       cs_node);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return ret;
+}
+
 void synchronize_trace(void)
 {
        synchronize_rcu();
@@ -892,6 +1002,32 @@ int _ltt_fields_metadata_statedump(struct ltt_session *session,
        return ret;
 }
 
+static
+int _ltt_callsite_metadata_statedump(struct ltt_session *session,
+                                 struct lttng_callsite *callsite)
+{
+       int ret = 0;
+
+       if (!CMM_ACCESS_ONCE(session->active))
+               return 0;
+
+       ret = lttng_metadata_printf(session,
+               "callsite {\n"
+               "       name = \"%s\";\n"
+               "       func = \"%s\";\n"
+               "       file = \"%s\";\n"
+               "       line = %u;\n"
+               "};\n\n",
+               callsite->tp_cs->tp->name,
+               callsite->tp_cs->func,
+               callsite->tp_cs->file,
+               callsite->tp_cs->lineno);
+       if (ret)
+               goto end;
+end:
+       return ret;
+}
+
 static
 int _ltt_event_metadata_statedump(struct ltt_session *session,
                                  struct ltt_channel *chan,
@@ -1264,6 +1400,9 @@ int _ltt_session_metadata_statedump(struct ltt_session *session)
        ret = _ltt_event_header_declare(session);
        if (ret)
                goto end;
+       ret = _callsite_session_metadata_statedump(session);
+       if (ret)
+               goto end;
 
 skip_session:
        cds_list_for_each_entry(chan, &session->chan, list) {
index 72eafec1ab57c81acc3cb6e12c545a9a4992d7bc..571627602924d8c6b8daa3eeaf3b7b843a52cf3b 100644 (file)
@@ -31,6 +31,12 @@ struct tracepoint_lib {
        int tracepoints_count;
 };
 
+struct tracepoint_callsite_lib {
+       struct cds_list_head list;
+       struct tracepoint_callsite * const *tp_start;
+       int tp_count;
+};
+
 extern int tracepoint_probe_register_noupdate(const char *name,
                void (*callback)(void), void *priv,
                const char *signature);
@@ -50,4 +56,7 @@ static inline void tracepoint_synchronize_unregister(void)
 extern void init_tracepoint(void);
 extern void exit_tracepoint(void);
 
+int lttng_callsite_add(const struct tracepoint_callsite *tp_cs);
+int lttng_callsite_remove(const struct tracepoint_callsite *tp_cs);
+
 #endif /* _LTTNG_TRACEPOINT_INTERNAL_H */
index 85b14d2c176af0cbccaf9ad517cb0afc42ab6056..3abed871be7c8bc6ef68cd018dc49feceaf52075 100644 (file)
@@ -381,6 +381,43 @@ static void lib_update_tracepoints(void)
        }
 }
 
+/**
+ * tracepoint_update_callsite_range - Update a callsite range
+ * @begin: beginning of the range
+ * @end: end of the range
+ *
+ * Updates the range of tracepoint call sites.
+ */
+static
+void tracepoint_enable_callsite_range(struct tracepoint_callsite * const *begin,
+                                  struct tracepoint_callsite * const *end)
+{
+       struct tracepoint_callsite * const *iter;
+       int ret;
+
+       for (iter = begin; iter < end; iter++) {
+               if (!*iter)
+                       continue;       /* skip dummy */
+               ret = lttng_callsite_add(*iter);
+               assert(!ret);
+       }
+}
+
+static
+void tracepoint_disable_callsite_range(struct tracepoint_callsite * const *begin,
+                       struct tracepoint_callsite * const *end)
+{
+       struct tracepoint_callsite * const *iter;
+       int ret;
+
+       for (iter = begin; iter < end; iter++) {
+               if (!*iter)
+                       continue;       /* skip dummy */
+               ret = lttng_callsite_remove(*iter);
+               assert(!ret);
+       }
+}
+
 /*
  * Update probes, removing the faulty probes.
  */
@@ -684,6 +721,43 @@ end:
        return 0;
 }
 
+int tracepoint_register_lib_callsite(struct tracepoint_callsite * const *tp_start,
+                           int tp_count)
+{
+       int real_count = 0;     /* without dummy */
+
+       init_tracepoint();
+
+       tracepoint_enable_callsite_range(tp_start, tp_start + tp_count);
+
+       if (ust_debug()) {
+               int i;
+
+               for (i = 0; i < tp_count; i++) {
+                       if (!tp_start[i])       /* Check for dummy */
+                               continue;
+                       DBG("registered callsite for tracepoint \"%s\" at %s@%s:%u",
+                               tp_start[i]->tp->name,
+                               tp_start[i]->func,
+                               tp_start[i]->file,
+                               tp_start[i]->lineno);
+                       real_count++;
+               }
+       }
+       DBG("just registered a tracepoint callsite section from %p and having %d call sites",
+               tp_start, real_count);
+       return 0;
+}
+
+int tracepoint_unregister_lib_callsite(struct tracepoint_callsite * const *tp_start,
+                           int tp_count)
+{
+       tracepoint_disable_callsite_range(tp_start, tp_start + tp_count);
+       DBG("just unregistered a tracepoints callsite section from %p",
+               tp_start);
+       return 0;
+}
+
 void init_tracepoint(void)
 {
        if (uatomic_xchg(&initialized, 1) == 1)
This page took 0.030884 seconds and 4 git commands to generate.