From: Mathieu Desnoyers Date: Tue, 2 Oct 2012 19:00:54 +0000 (-0400) Subject: Add callsite support X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=4e776b26f63c6ecd5c4622ae80d1970827a6b8b3;p=lttng-ust.git Add callsite support Signed-off-by: Mathieu Desnoyers --- diff --git a/include/lttng/tracepoint-types.h b/include/lttng/tracepoint-types.h index 0c0c7237..27500d40 100644 --- a/include/lttng/tracepoint-types.h +++ b/include/lttng/tracepoint-types.h @@ -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 */ diff --git a/include/lttng/tracepoint.h b/include/lttng/tracepoint.h index 8b089144..8086b648 100644 --- a/include/lttng/tracepoint.h +++ b/include/lttng/tracepoint.h @@ -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 /* diff --git a/include/lttng/ust-events.h b/include/lttng/ust-events.h index 0e19ed22..b0768357 100644 --- a/include/lttng/ust-events.h +++ b/include/lttng/ust-events.h @@ -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. diff --git a/liblttng-ust/ltt-events.c b/liblttng-ust/ltt-events.c index 32135c85..0b6e61c4 100644 --- a/liblttng-ust/ltt-events.c +++ b/liblttng-ust/ltt-events.c @@ -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) { diff --git a/liblttng-ust/tracepoint-internal.h b/liblttng-ust/tracepoint-internal.h index 72eafec1..57162760 100644 --- a/liblttng-ust/tracepoint-internal.h +++ b/liblttng-ust/tracepoint-internal.h @@ -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 */ diff --git a/liblttng-ust/tracepoint.c b/liblttng-ust/tracepoint.c index 85b14d2c..3abed871 100644 --- a/liblttng-ust/tracepoint.c +++ b/liblttng-ust/tracepoint.c @@ -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)