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 */
#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__); \
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
/*
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.
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);
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,
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();
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,
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) {
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);
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 */
}
}
+/**
+ * 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.
*/
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)