X-Git-Url: http://git.liburcu.org/?a=blobdiff_plain;f=liblttng-ust%2Ftracepoint.c;h=e68ca58be07ff59703e7fa4ead0f2627587ca9b9;hb=4a8f9fe6a3fb242ce5cdb905d7f9f24a8fb5df6b;hp=7144fab40003c586a726fff5a6b4544f14d4c36c;hpb=05780d81d89c19ff3f9356796bb636a255f11077;p=lttng-ust.git diff --git a/liblttng-ust/tracepoint.c b/liblttng-ust/tracepoint.c index 7144fab4..e68ca58b 100644 --- a/liblttng-ust/tracepoint.c +++ b/liblttng-ust/tracepoint.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,7 @@ #include #include "tracepoint-internal.h" -#include "ltt-tracer-core.h" +#include "lttng-tracer-core.h" #include "jhash.h" #include "error.h" @@ -77,7 +78,7 @@ static CDS_LIST_HEAD(libs); * Tracepoint hash table, containing the active tracepoints. * Protected by tracepoint mutex. */ -#define TRACEPOINT_HASH_BITS 6 +#define TRACEPOINT_HASH_BITS 12 #define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS) static struct cds_hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; @@ -94,6 +95,7 @@ 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]; }; @@ -106,6 +108,21 @@ struct tp_probes { struct tracepoint_probe probes[0]; }; +/* + * Callsite hash table, containing the tracepoint call sites. + * Protected by tracepoint mutex. + */ +#define CALLSITE_HASH_BITS 12 +#define CALLSITE_TABLE_SIZE (1 << CALLSITE_HASH_BITS) +static struct cds_hlist_head callsite_table[CALLSITE_TABLE_SIZE]; + +struct callsite_entry { + struct cds_hlist_node hlist; /* hash table node */ + struct cds_list_head node; /* lib list of callsites node */ + struct tracepoint *tp; +}; + +/* coverity[+alloc] */ static void *allocate_probes(int count) { struct tp_probes *p = zmalloc(count * sizeof(struct tracepoint_probe) @@ -113,6 +130,7 @@ static void *allocate_probes(int count) return p == NULL ? NULL : p->probes; } +/* coverity[+free : arg-0] */ static void release_probes(void *old) { if (old) { @@ -136,13 +154,15 @@ static void debug_print_probes(struct tracepoint_entry *entry) static void * tracepoint_entry_add_probe(struct tracepoint_entry *entry, - void *probe, void *data) + void (*probe)(void), void *data) { int nr_probes = 0; struct tracepoint_probe *old, *new; - WARN_ON(!probe); - + if (!probe) { + WARN_ON(1); + return ERR_PTR(-EINVAL); + } debug_print_probes(entry); old = entry->probes; if (old) { @@ -168,8 +188,8 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, } static void * -tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe, - void *data) +tracepoint_entry_remove_probe(struct tracepoint_entry *entry, + void (*probe)(void), void *data) { int nr_probes = 0, nr_del = 0, i; struct tracepoint_probe *old, *new; @@ -181,11 +201,12 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe, debug_print_probes(entry); /* (N -> M), (N > 1, M >= 0) probes */ - for (nr_probes = 0; old[nr_probes].func; nr_probes++) { - if (!probe || - (old[nr_probes].func == probe && - old[nr_probes].data == data)) - nr_del++; + if (probe) { + for (nr_probes = 0; old[nr_probes].func; nr_probes++) { + if (old[nr_probes].func == probe && + old[nr_probes].data == data) + nr_del++; + } } if (nr_probes - nr_del == 0) { @@ -202,8 +223,7 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe, if (new == NULL) return ERR_PTR(-ENOMEM); for (i = 0; old[i].func; i++) - if (probe && - (old[i].func != probe || old[i].data != data)) + if (old[i].func != probe || old[i].data != data) new[j++] = old[i]; new[nr_probes - nr_del].func = NULL; entry->refcount = nr_probes - nr_del; @@ -243,7 +263,8 @@ static struct tracepoint_entry *get_tracepoint(const char *name) * Add the tracepoint to the tracepoint hash table. Must be called with * 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; @@ -274,6 +295,7 @@ static struct tracepoint_entry *add_tracepoint(const char *name) e->name[name_len] = '\0'; e->probes = NULL; e->refcount = 0; + e->signature = signature; cds_hlist_add_head(&e->hlist, head); return e; } @@ -295,6 +317,23 @@ static void set_tracepoint(struct tracepoint_entry **entry, struct tracepoint *elem, int active) { 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 @@ -319,6 +358,77 @@ static void disable_tracepoint(struct tracepoint *elem) rcu_assign_pointer(elem->probes, NULL); } +/* + * Add the callsite to the callsite hash table. Must be called with + * tracepoint mutex held. + */ +static void add_callsite(struct tracepoint_lib * lib, struct tracepoint *tp) +{ + struct cds_hlist_head *head; + struct callsite_entry *e; + const char *name = tp->name; + 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 = &callsite_table[hash & (CALLSITE_TABLE_SIZE - 1)]; + e = zmalloc(sizeof(struct callsite_entry)); + assert(e); + cds_hlist_add_head(&e->hlist, head); + e->tp = tp; + cds_list_add(&e->node, &lib->callsites); +} + +/* + * Remove the callsite from the callsite hash table and from lib + * callsite list. Must be called with tracepoint mutex held. + */ +static void remove_callsite(struct callsite_entry *e) +{ + cds_hlist_del(&e->hlist); + cds_list_del(&e->node); + free(e); +} + +/* + * Enable/disable all callsites based on the state of a specific + * tracepoint entry. + * Must be called with tracepoint mutex held. + */ +static void tracepoint_sync_callsites(const char *name) +{ + struct cds_hlist_head *head; + struct cds_hlist_node *node; + struct callsite_entry *e; + size_t name_len = strlen(name); + uint32_t hash; + struct tracepoint_entry *tp_entry; + + tp_entry = get_tracepoint(name); + 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 = &callsite_table[hash & (CALLSITE_TABLE_SIZE - 1)]; + cds_hlist_for_each_entry(e, node, head, hlist) { + struct tracepoint *tp = e->tp; + + if (strncmp(name, tp->name, LTTNG_UST_SYM_NAME_LEN - 1)) + continue; + if (tp_entry) { + set_tracepoint(&tp_entry, tp, + !!tp_entry->refcount); + } else { + disable_tracepoint(tp); + } + } +} + /** * tracepoint_update_probe_range - Update a probe range * @begin: beginning of the range @@ -350,34 +460,61 @@ void tracepoint_update_probe_range(struct tracepoint * const *begin, } } -static void lib_update_tracepoints(void) +static void lib_update_tracepoints(struct tracepoint_lib *lib) { - struct tracepoint_lib *lib; + tracepoint_update_probe_range(lib->tracepoints_start, + lib->tracepoints_start + lib->tracepoints_count); +} - cds_list_for_each_entry(lib, &libs, list) { - tracepoint_update_probe_range(lib->tracepoints_start, - lib->tracepoints_start + lib->tracepoints_count); +static void lib_register_callsites(struct tracepoint_lib *lib) +{ + struct tracepoint * const *begin; + struct tracepoint * const *end; + struct tracepoint * const *iter; + + begin = lib->tracepoints_start; + end = lib->tracepoints_start + lib->tracepoints_count; + + for (iter = begin; iter < end; iter++) { + if (!*iter) + continue; /* skip dummy */ + if (!(*iter)->name) { + continue; + } + add_callsite(lib, *iter); } } +static void lib_unregister_callsites(struct tracepoint_lib *lib) +{ + struct callsite_entry *callsite, *tmp; + + cds_list_for_each_entry_safe(callsite, tmp, &lib->callsites, node) + remove_callsite(callsite); +} + /* * Update probes, removing the faulty probes. */ static void tracepoint_update_probes(void) { + struct tracepoint_lib *lib; + /* tracepoints registered from libraries and executable. */ - lib_update_tracepoints(); + cds_list_for_each_entry(lib, &libs, list) + lib_update_tracepoints(lib); } static struct tracepoint_probe * -tracepoint_add_probe(const char *name, void *probe, void *data) +tracepoint_add_probe(const char *name, void (*probe)(void), 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; } @@ -396,7 +533,8 @@ tracepoint_add_probe(const char *name, void *probe, void *data) * The probe address must at least be aligned on the architecture pointer size. * 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), + void *data, const char *signature) { void *old; int ret = 0; @@ -404,20 +542,21 @@ int __tracepoint_probe_register(const char *name, void *probe, void *data) DBG("Registering probe to tracepoint %s", name); pthread_mutex_lock(&tracepoint_mutex); - old = tracepoint_add_probe(name, probe, data); + old = tracepoint_add_probe(name, probe, data, signature); if (IS_ERR(old)) { ret = PTR_ERR(old); goto end; } - tracepoint_update_probes(); /* may update entry */ + tracepoint_sync_callsites(name); release_probes(old); end: pthread_mutex_unlock(&tracepoint_mutex); return ret; } -static void *tracepoint_remove_probe(const char *name, void *probe, void *data) +static void *tracepoint_remove_probe(const char *name, void (*probe)(void), + void *data) { struct tracepoint_entry *entry; void *old; @@ -439,7 +578,8 @@ static void *tracepoint_remove_probe(const char *name, void *probe, void *data) * @probe: probe function pointer * @probe: probe data pointer */ -int __tracepoint_probe_unregister(const char *name, void *probe, void *data) +int __tracepoint_probe_unregister(const char *name, void (*probe)(void), + void *data) { void *old; int ret = 0; @@ -452,7 +592,7 @@ int __tracepoint_probe_unregister(const char *name, void *probe, void *data) ret = PTR_ERR(old); goto end; } - tracepoint_update_probes(); /* may update entry */ + tracepoint_sync_callsites(name); release_probes(old); end: pthread_mutex_unlock(&tracepoint_mutex); @@ -476,14 +616,14 @@ static void tracepoint_add_old_probes(void *old) * * caller must call tracepoint_probe_update_all() */ -int tracepoint_probe_register_noupdate(const char *name, void *probe, - void *data) +int tracepoint_probe_register_noupdate(const char *name, void (*probe)(void), + void *data, const char *signature) { void *old; int ret = 0; pthread_mutex_lock(&tracepoint_mutex); - old = tracepoint_add_probe(name, probe, data); + old = tracepoint_add_probe(name, probe, data, signature); if (IS_ERR(old)) { ret = PTR_ERR(old); goto end; @@ -502,7 +642,7 @@ end: * caller must call tracepoint_probe_update_all() * Called with the tracepoint mutex held. */ -int tracepoint_probe_unregister_noupdate(const char *name, void *probe, +int tracepoint_probe_unregister_noupdate(const char *name, void (*probe)(void), void *data) { void *old; @@ -566,11 +706,15 @@ static void new_tracepoints(struct tracepoint * const *start, struct tracepoint } static -void lib_disable_tracepoints(struct tracepoint * const *begin, - struct tracepoint * const *end) +void lib_disable_tracepoints(struct tracepoint_lib *lib) { + struct tracepoint * const *begin; + struct tracepoint * const *end; struct tracepoint * const *iter; + begin = lib->tracepoints_start; + end = lib->tracepoints_start + lib->tracepoints_count; + for (iter = begin; iter < end; iter++) { if (!*iter) continue; /* skip dummy */ @@ -590,6 +734,7 @@ int tracepoint_register_lib(struct tracepoint * const *tracepoints_start, pl->tracepoints_start = tracepoints_start; pl->tracepoints_count = tracepoints_count; + CDS_INIT_LIST_HEAD(&pl->callsites); pthread_mutex_lock(&tracepoint_mutex); /* @@ -607,9 +752,8 @@ int tracepoint_register_lib(struct tracepoint * const *tracepoints_start, cds_list_add(&pl->list, &libs); lib_added: new_tracepoints(tracepoints_start, tracepoints_start + tracepoints_count); - - /* TODO: update just the loaded lib */ - lib_update_tracepoints(); + lib_register_callsites(pl); + lib_update_tracepoints(pl); pthread_mutex_unlock(&tracepoint_mutex); DBG("just registered a tracepoints section from %p and having %d tracepoints", @@ -628,33 +772,27 @@ lib_added: int tracepoint_unregister_lib(struct tracepoint * const *tracepoints_start) { struct tracepoint_lib *lib; - int tracepoints_count; pthread_mutex_lock(&tracepoint_mutex); cds_list_for_each_entry(lib, &libs, list) { - if (lib->tracepoints_start == tracepoints_start) { - struct tracepoint_lib *lib2free = lib; + if (lib->tracepoints_start != tracepoints_start) + continue; - cds_list_del(&lib->list); - tracepoints_count = lib->tracepoints_count; - free(lib2free); - goto found; - } + cds_list_del(&lib->list); + /* + * 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(lib); + lib_unregister_callsites(lib); + DBG("just unregistered a tracepoints section from %p", + lib->tracepoints_start); + free(lib); + break; } - 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; }