X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=liblttng-ust%2Ftracepoint.c;h=a93f286364550e1cf29a3388fa2f5b4a09d5adbf;hb=8c82e2da6b17493d879523b80aed11d0d250eef1;hp=526ee00e58d130cd2093ab59e31078b7cdbf52b4;hpb=1af3d49e997e5585079ff917d2435e9380a071b7;p=lttng-ust.git diff --git a/liblttng-ust/tracepoint.c b/liblttng-ust/tracepoint.c index 526ee00e..a93f2863 100644 --- a/liblttng-ust/tracepoint.c +++ b/liblttng-ust/tracepoint.c @@ -21,40 +21,62 @@ #define _LGPL_SOURCE #include -#include -#include #include #include +#include + #include #include #include #include #include -#include +#include +#include /* for LTTNG_UST_SYM_NAME_LEN */ + +#include +#include + #include "tracepoint-internal.h" #include "ltt-tracer-core.h" #include "jhash.h" +#include "error.h" /* Set to 1 to enable tracepoint debug output */ static const int tracepoint_debug; static int initialized; static void (*new_tracepoint_cb)(struct tracepoint *); -/* libraries that contain tracepoints (struct tracepoint_lib) */ +/* + * tracepoint_mutex nests inside UST mutex. + * + * Note about interaction with fork/clone: UST does not hold the + * tracepoint mutex across fork/clone because it is either: + * - nested within UST mutex, in which case holding the UST mutex across + * fork/clone suffice, + * - taken by a library constructor, which should never race with a + * fork/clone if the application is expected to continue running with + * the same memory layout (no following exec()). + */ +static pthread_mutex_t tracepoint_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* + * libraries that contain tracepoints (struct tracepoint_lib). + * Protected by tracepoint mutex. + */ static CDS_LIST_HEAD(libs); /* - * The UST lock protects the library tracepoints, the hash table, and + * The tracepoint mutex protects the library tracepoints, the hash table, and * the library list. - * All calls to the tracepoint API must be protected by the UST lock, + * All calls to the tracepoint API must be protected by the tracepoint mutex, * excepts calls to tracepoint_register_lib and - * tracepoint_unregister_lib, which take the UST lock themselves. + * tracepoint_unregister_lib, which take the tracepoint mutex themselves. */ /* * Tracepoint hash table, containing the active tracepoints. - * Protected by tracepoints_mutex. + * Protected by tracepoint mutex. */ #define TRACEPOINT_HASH_BITS 6 #define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS) @@ -67,30 +89,33 @@ static int need_update; * Note about RCU : * It is used to to delay the free of multiple probes array until a quiescent * state is reached. - * Tracepoint entries modifications are protected by the tracepoints_mutex. + * Tracepoint entries modifications are protected by the tracepoint mutex. */ struct tracepoint_entry { struct cds_hlist_node hlist; struct tracepoint_probe *probes; int refcount; /* Number of times armed. 0 if disarmed. */ + const char *signature; char name[0]; }; struct tp_probes { union { struct cds_list_head list; + /* Field below only used for call_rcu scheme */ + /* struct rcu_head head; */ } u; struct tracepoint_probe probes[0]; }; -static inline void *allocate_probes(int count) +static void *allocate_probes(int count) { struct tp_probes *p = zmalloc(count * sizeof(struct tracepoint_probe) + sizeof(struct tp_probes)); return p == NULL ? NULL : p->probes; } -static inline void release_probes(void *old) +static void release_probes(void *old) { if (old) { struct tp_probes *tp_probes = caa_container_of(old, @@ -192,7 +217,7 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe, /* * Get tracepoint if the tracepoint is present in the tracepoint hash table. - * Must be called with tracepoints_mutex held. + * Must be called with tracepoint mutex held. * Returns NULL if not present. */ static struct tracepoint_entry *get_tracepoint(const char *name) @@ -200,11 +225,17 @@ static struct tracepoint_entry *get_tracepoint(const char *name) struct cds_hlist_head *head; struct cds_hlist_node *node; struct tracepoint_entry *e; - uint32_t hash = jhash(name, strlen(name), 0); + size_t name_len = strlen(name); + uint32_t hash; + if (name_len > LTTNG_UST_SYM_NAME_LEN - 1) { + WARN("Truncating tracepoint name %s which exceeds size limits of %u chars", name, LTTNG_UST_SYM_NAME_LEN - 1); + name_len = LTTNG_UST_SYM_NAME_LEN - 1; + } + hash = jhash(name, name_len, 0); head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)]; cds_hlist_for_each_entry(e, node, head, hlist) { - if (!strcmp(name, e->name)) + if (!strncmp(name, e->name, LTTNG_UST_SYM_NAME_LEN - 1)) return e; } return NULL; @@ -212,19 +243,25 @@ static struct tracepoint_entry *get_tracepoint(const char *name) /* * Add the tracepoint to the tracepoint hash table. Must be called with - * tracepoints_mutex held. + * tracepoint mutex held. */ -static struct tracepoint_entry *add_tracepoint(const char *name) +static struct tracepoint_entry *add_tracepoint(const char *name, + const char *signature) { struct cds_hlist_head *head; struct cds_hlist_node *node; struct tracepoint_entry *e; - size_t name_len = strlen(name) + 1; - uint32_t hash = jhash(name, name_len-1, 0); + size_t name_len = strlen(name); + uint32_t hash; + if (name_len > LTTNG_UST_SYM_NAME_LEN - 1) { + WARN("Truncating tracepoint name %s which exceeds size limits of %u chars", name, LTTNG_UST_SYM_NAME_LEN - 1); + name_len = LTTNG_UST_SYM_NAME_LEN - 1; + } + hash = jhash(name, name_len, 0); head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)]; cds_hlist_for_each_entry(e, node, head, hlist) { - if (!strcmp(name, e->name)) { + if (!strncmp(name, e->name, LTTNG_UST_SYM_NAME_LEN - 1)) { DBG("tracepoint %s busy", name); return ERR_PTR(-EEXIST); /* Already there */ } @@ -233,21 +270,23 @@ static struct tracepoint_entry *add_tracepoint(const char *name) * Using zmalloc here to allocate a variable length element. Could * cause some memory fragmentation if overused. */ - e = zmalloc(sizeof(struct tracepoint_entry) + name_len); + e = zmalloc(sizeof(struct tracepoint_entry) + name_len + 1); if (!e) return ERR_PTR(-ENOMEM); - memcpy(&e->name[0], name, name_len); + memcpy(&e->name[0], name, name_len + 1); + e->name[name_len] = '\0'; e->probes = NULL; e->refcount = 0; + e->signature = signature; cds_hlist_add_head(&e->hlist, head); return e; } /* * Remove the tracepoint from the tracepoint hash table. Must be called with - * ust_lock held. + * tracepoint mutex held. */ -static inline void remove_tracepoint(struct tracepoint_entry *e) +static void remove_tracepoint(struct tracepoint_entry *e) { cds_hlist_del(&e->hlist); free(e); @@ -259,7 +298,24 @@ static inline void remove_tracepoint(struct tracepoint_entry *e) static void set_tracepoint(struct tracepoint_entry **entry, struct tracepoint *elem, int active) { - WARN_ON(strcmp((*entry)->name, elem->name) != 0); + WARN_ON(strncmp((*entry)->name, elem->name, LTTNG_UST_SYM_NAME_LEN - 1) != 0); + /* + * Check that signatures match before connecting a probe to a + * tracepoint. Warn the user if they don't. + */ + if (strcmp(elem->signature, (*entry)->signature) != 0) { + static int warned = 0; + + /* Only print once, don't flood console. */ + if (!warned) { + WARN("Tracepoint signature mismatch, not enabling one or more tracepoints. Ensure that the tracepoint probes prototypes match the application."); + WARN("Tracepoint \"%s\" signatures: call: \"%s\" vs probe: \"%s\".", + elem->name, elem->signature, (*entry)->signature); + warned = 1; + } + /* Don't accept connecting non-matching signatures. */ + return; + } /* * rcu_assign_pointer has a cmm_smp_wmb() which makes sure that the new @@ -335,14 +391,15 @@ static void tracepoint_update_probes(void) } static struct tracepoint_probe * -tracepoint_add_probe(const char *name, void *probe, void *data) +tracepoint_add_probe(const char *name, void *probe, void *data, + const char *signature) { struct tracepoint_entry *entry; struct tracepoint_probe *old; entry = get_tracepoint(name); if (!entry) { - entry = add_tracepoint(name); + entry = add_tracepoint(name, signature); if (IS_ERR(entry)) return (struct tracepoint_probe *)entry; } @@ -359,19 +416,28 @@ tracepoint_add_probe(const char *name, void *probe, void *data) * * Returns 0 if ok, error value on error. * The probe address must at least be aligned on the architecture pointer size. - * Called with the UST lock held. + * Called with the tracepoint mutex held. */ -int __tracepoint_probe_register(const char *name, void *probe, void *data) +int __tracepoint_probe_register(const char *name, void *probe, void *data, + const char *signature) { void *old; + int ret = 0; - old = tracepoint_add_probe(name, probe, data); - if (IS_ERR(old)) - return PTR_ERR(old); + DBG("Registering probe to tracepoint %s", name); + + pthread_mutex_lock(&tracepoint_mutex); + old = tracepoint_add_probe(name, probe, data, signature); + if (IS_ERR(old)) { + ret = PTR_ERR(old); + goto end; + } tracepoint_update_probes(); /* may update entry */ release_probes(old); - return 0; +end: + pthread_mutex_unlock(&tracepoint_mutex); + return ret; } static void *tracepoint_remove_probe(const char *name, void *probe, void *data) @@ -395,20 +461,25 @@ static void *tracepoint_remove_probe(const char *name, void *probe, void *data) * @name: tracepoint name * @probe: probe function pointer * @probe: probe data pointer - * - * Called with the UST lock held. */ int __tracepoint_probe_unregister(const char *name, void *probe, void *data) { void *old; + int ret = 0; - old = tracepoint_remove_probe(name, probe, data); - if (IS_ERR(old)) - return PTR_ERR(old); + DBG("Un-registering probe from tracepoint %s", name); + pthread_mutex_lock(&tracepoint_mutex); + old = tracepoint_remove_probe(name, probe, data); + if (IS_ERR(old)) { + ret = PTR_ERR(old); + goto end; + } tracepoint_update_probes(); /* may update entry */ release_probes(old); - return 0; +end: + pthread_mutex_unlock(&tracepoint_mutex); + return ret; } static void tracepoint_add_old_probes(void *old) @@ -427,19 +498,23 @@ static void tracepoint_add_old_probes(void *old) * @probe: probe handler * * caller must call tracepoint_probe_update_all() - * Called with the UST lock held. */ int tracepoint_probe_register_noupdate(const char *name, void *probe, - void *data) + void *data, const char *signature) { void *old; + int ret = 0; - old = tracepoint_add_probe(name, probe, data); + pthread_mutex_lock(&tracepoint_mutex); + old = tracepoint_add_probe(name, probe, data, signature); if (IS_ERR(old)) { - return PTR_ERR(old); + ret = PTR_ERR(old); + goto end; } tracepoint_add_old_probes(old); - return 0; +end: + pthread_mutex_unlock(&tracepoint_mutex); + return ret; } /** @@ -448,32 +523,39 @@ int tracepoint_probe_register_noupdate(const char *name, void *probe, * @probe: probe function pointer * * caller must call tracepoint_probe_update_all() - * Called with the UST lock held. + * Called with the tracepoint mutex held. */ int tracepoint_probe_unregister_noupdate(const char *name, void *probe, void *data) { void *old; + int ret = 0; + + DBG("Un-registering probe from tracepoint %s", name); + pthread_mutex_lock(&tracepoint_mutex); old = tracepoint_remove_probe(name, probe, data); if (IS_ERR(old)) { - return PTR_ERR(old); + ret = PTR_ERR(old); + goto end; } tracepoint_add_old_probes(old); - return 0; +end: + pthread_mutex_unlock(&tracepoint_mutex); + return ret; } /** * tracepoint_probe_update_all - update tracepoints - * Called with the UST lock held. */ void tracepoint_probe_update_all(void) { CDS_LIST_HEAD(release_probes); struct tp_probes *pos, *next; + pthread_mutex_lock(&tracepoint_mutex); if (!need_update) { - return; + goto end; } if (!cds_list_empty(&old_probes)) cds_list_replace_init(&old_probes, &release_probes); @@ -485,105 +567,8 @@ void tracepoint_probe_update_all(void) synchronize_rcu(); free(pos); } -} - -/* - * Returns 0 if current not found. - * Returns 1 if current found. - * - * Called with tracepoint mutex held - */ -int lib_get_iter_tracepoints(struct tracepoint_iter *iter) -{ - struct tracepoint_lib *iter_lib; - int found = 0; - - cds_list_for_each_entry(iter_lib, &libs, list) { - if (iter_lib < iter->lib) - continue; - else if (iter_lib > iter->lib) - iter->tracepoint = NULL; - found = tracepoint_get_iter_range(&iter->tracepoint, - iter_lib->tracepoints_start, - iter_lib->tracepoints_start + iter_lib->tracepoints_count); - if (found) { - iter->lib = iter_lib; - break; - } - } - return found; -} - -/** - * tracepoint_get_iter_range - Get a next tracepoint iterator given a range. - * @tracepoint: current tracepoints (in), next tracepoint (out) - * @begin: beginning of the range - * @end: end of the range - * - * Returns whether a next tracepoint has been found (1) or not (0). - * Will return the first tracepoint in the range if the input tracepoint is - * NULL. - * Called with tracepoint mutex held. - */ -int tracepoint_get_iter_range(struct tracepoint * const **tracepoint, - struct tracepoint * const *begin, struct tracepoint * const *end) -{ - if (!*tracepoint && begin != end) - *tracepoint = begin; - while (*tracepoint >= begin && *tracepoint < end) { - if (!**tracepoint) - (*tracepoint)++; /* skip dummy */ - else - return 1; - } - return 0; -} - -/* - * Called with tracepoint mutex held. - */ -static void tracepoint_get_iter(struct tracepoint_iter *iter) -{ - int found = 0; - - /* tracepoints in libs. */ - found = lib_get_iter_tracepoints(iter); - if (!found) - tracepoint_iter_reset(iter); -} - -/* - * Called with UST lock held. - */ -void tracepoint_iter_start(struct tracepoint_iter *iter) -{ - tracepoint_get_iter(iter); -} - -/* - * Called with UST lock held. - */ -void tracepoint_iter_next(struct tracepoint_iter *iter) -{ - iter->tracepoint++; - /* - * iter->tracepoint may be invalid because we blindly incremented it. - * Make sure it is valid by marshalling on the tracepoints, getting the - * tracepoints from following modules if necessary. - */ - tracepoint_get_iter(iter); -} - -/* - * Called with UST lock held. - */ -void tracepoint_iter_stop(struct tracepoint_iter *iter) -{ -} - -void tracepoint_iter_reset(struct tracepoint_iter *iter) -{ - iter->tracepoint = NULL; +end: + pthread_mutex_unlock(&tracepoint_mutex); } void tracepoint_set_new_tracepoint_cb(void (*cb)(struct tracepoint *)) @@ -603,6 +588,20 @@ static void new_tracepoints(struct tracepoint * const *start, struct tracepoint } } +static +void lib_disable_tracepoints(struct tracepoint * const *begin, + struct tracepoint * const *end) +{ + struct tracepoint * const *iter; + + for (iter = begin; iter < end; iter++) { + if (!*iter) + continue; /* skip dummy */ + disable_tracepoint(*iter); + } + +} + int tracepoint_register_lib(struct tracepoint * const *tracepoints_start, int tracepoints_count) { @@ -615,7 +614,7 @@ int tracepoint_register_lib(struct tracepoint * const *tracepoints_start, pl->tracepoints_start = tracepoints_start; pl->tracepoints_count = tracepoints_count; - ust_lock(); + pthread_mutex_lock(&tracepoint_mutex); /* * We sort the libs by struct lib pointer address. */ @@ -634,10 +633,17 @@ lib_added: /* TODO: update just the loaded lib */ lib_update_tracepoints(); - ust_unlock(); + pthread_mutex_unlock(&tracepoint_mutex); DBG("just registered a tracepoints section from %p and having %d tracepoints", tracepoints_start, tracepoints_count); + if (ust_debug()) { + int i; + + for (i = 0; i < tracepoints_count; i++) { + DBG("registered tracepoint: %s", tracepoints_start[i]->name); + } + } return 0; } @@ -645,18 +651,34 @@ lib_added: int tracepoint_unregister_lib(struct tracepoint * const *tracepoints_start) { struct tracepoint_lib *lib; + int tracepoints_count; - ust_lock(); + pthread_mutex_lock(&tracepoint_mutex); cds_list_for_each_entry(lib, &libs, list) { if (lib->tracepoints_start == tracepoints_start) { struct tracepoint_lib *lib2free = lib; + cds_list_del(&lib->list); + tracepoints_count = lib->tracepoints_count; free(lib2free); - break; + goto found; } } - ust_unlock(); - + goto end; +found: + /* + * Force tracepoint disarm for all tracepoints of this lib. + * This takes care of destructor of library that would leave a + * LD_PRELOAD wrapper override function enabled for tracing, but + * the session teardown would not be able to reach the + * tracepoint anymore to disable it. + */ + lib_disable_tracepoints(tracepoints_start, + tracepoints_start + tracepoints_count); + DBG("just unregistered a tracepoints section from %p", + tracepoints_start); +end: + pthread_mutex_unlock(&tracepoint_mutex); return 0; } @@ -671,3 +693,25 @@ void exit_tracepoint(void) { initialized = 0; } + +/* + * Create the wrapper symbols. + */ +#undef tp_rcu_read_lock_bp +#undef tp_rcu_read_unlock_bp +#undef tp_rcu_dereference_bp + +void tp_rcu_read_lock_bp(void) +{ + rcu_read_lock_bp(); +} + +void tp_rcu_read_unlock_bp(void) +{ + rcu_read_unlock_bp(); +} + +void *tp_rcu_dereference_sym_bp(void *p) +{ + return rcu_dereference_bp(p); +}