X-Git-Url: http://git.liburcu.org/?a=blobdiff_plain;f=liblttng-ust%2Ftracepoint.c;h=a4a79ba59b3f8298b30a2889bf6e7e30ef04aad2;hb=1a20609484df56e378318d65f07f39dcd8ec1db2;hp=7de56ca0361dbbe5b55c0704cdcbe1eca44fb58a;hpb=814f7df1401ce02529847840edd84836c1ce646a;p=lttng-ust.git diff --git a/liblttng-ust/tracepoint.c b/liblttng-ust/tracepoint.c index 7de56ca0..a4a79ba5 100644 --- a/liblttng-ust/tracepoint.c +++ b/liblttng-ust/tracepoint.c @@ -45,7 +45,7 @@ /* Set to 1 to enable tracepoint debug output */ static const int tracepoint_debug; static int initialized; -static void (*new_tracepoint_cb)(struct tracepoint *); +static void (*new_tracepoint_cb)(struct lttng_ust_tracepoint *); /* * tracepoint_mutex nests inside UST mutex. @@ -93,8 +93,9 @@ static int need_update; */ struct tracepoint_entry { struct cds_hlist_node hlist; - struct tracepoint_probe *probes; + struct lttng_ust_tracepoint_probe *probes; int refcount; /* Number of times armed. 0 if disarmed. */ + int callsite_refcount; /* how many libs use this tracepoint */ const char *signature; char name[0]; }; @@ -105,16 +106,33 @@ struct tp_probes { /* Field below only used for call_rcu scheme */ /* struct rcu_head head; */ } u; - struct tracepoint_probe probes[0]; + struct lttng_ust_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 lttng_ust_tracepoint *tp; +}; + +/* coverity[+alloc] */ static void *allocate_probes(int count) { - struct tp_probes *p = zmalloc(count * sizeof(struct tracepoint_probe) - + sizeof(struct tp_probes)); + struct tp_probes *p = + zmalloc(count * sizeof(struct lttng_ust_tracepoint_probe) + + sizeof(struct tp_probes)); return p == NULL ? NULL : p->probes; } +/* coverity[+free : arg-0] */ static void release_probes(void *old) { if (old) { @@ -141,10 +159,12 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void (*probe)(void), void *data) { int nr_probes = 0; - struct tracepoint_probe *old, *new; - - WARN_ON(!probe); + struct lttng_ust_tracepoint_probe *old, *new; + if (!probe) { + WARN_ON(1); + return ERR_PTR(-EINVAL); + } debug_print_probes(entry); old = entry->probes; if (old) { @@ -159,7 +179,8 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, if (new == NULL) return ERR_PTR(-ENOMEM); if (old) - memcpy(new, old, nr_probes * sizeof(struct tracepoint_probe)); + memcpy(new, old, + nr_probes * sizeof(struct lttng_ust_tracepoint_probe)); new[nr_probes].func = probe; new[nr_probes].data = data; new[nr_probes + 1].func = NULL; @@ -174,7 +195,7 @@ 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; + struct lttng_ust_tracepoint_probe *old, *new; old = entry->probes; @@ -183,11 +204,12 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, 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) { @@ -204,8 +226,7 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, 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; @@ -277,6 +298,7 @@ static struct tracepoint_entry *add_tracepoint(const char *name, e->name[name_len] = '\0'; e->probes = NULL; e->refcount = 0; + e->callsite_refcount = 0; e->signature = signature; cds_hlist_add_head(&e->hlist, head); return e; @@ -296,7 +318,7 @@ static void remove_tracepoint(struct tracepoint_entry *e) * Sets the probe callback corresponding to one tracepoint. */ static void set_tracepoint(struct tracepoint_entry **entry, - struct tracepoint *elem, int active) + struct lttng_ust_tracepoint *elem, int active) { WARN_ON(strncmp((*entry)->name, elem->name, LTTNG_UST_SYM_NAME_LEN - 1) != 0); /* @@ -334,12 +356,100 @@ static void set_tracepoint(struct tracepoint_entry **entry, * function insures that the original callback is not used anymore. This insured * by preempt_disable around the call site. */ -static void disable_tracepoint(struct tracepoint *elem) +static void disable_tracepoint(struct lttng_ust_tracepoint *elem) { elem->state = 0; 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 lttng_ust_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; + struct tracepoint_entry *tp_entry; + + 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)); + if (!e) { + PERROR("Unable to add callsite for tracepoint \"%s\"", name); + return; + } + cds_hlist_add_head(&e->hlist, head); + e->tp = tp; + cds_list_add(&e->node, &lib->callsites); + + tp_entry = get_tracepoint(name); + if (!tp_entry) + return; + tp_entry->callsite_refcount++; +} + +/* + * 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) +{ + struct tracepoint_entry *tp_entry; + + tp_entry = get_tracepoint(e->tp->name); + if (tp_entry) { + tp_entry->callsite_refcount--; + if (tp_entry->callsite_refcount == 0) + disable_tracepoint(e->tp); + } + 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 lttng_ust_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 @@ -348,10 +458,10 @@ static void disable_tracepoint(struct tracepoint *elem) * Updates the probe callback corresponding to a range of tracepoints. */ static -void tracepoint_update_probe_range(struct tracepoint * const *begin, - struct tracepoint * const *end) +void tracepoint_update_probe_range(struct lttng_ust_tracepoint * const *begin, + struct lttng_ust_tracepoint * const *end) { - struct tracepoint * const *iter; + struct lttng_ust_tracepoint * const *iter; struct tracepoint_entry *mark_entry; for (iter = begin; iter < end; iter++) { @@ -371,37 +481,63 @@ 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 lttng_ust_tracepoint * const *begin; + struct lttng_ust_tracepoint * const *end; + struct lttng_ust_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 * +static struct lttng_ust_tracepoint_probe * tracepoint_add_probe(const char *name, void (*probe)(void), void *data, const char *signature) { struct tracepoint_entry *entry; - struct tracepoint_probe *old; + struct lttng_ust_tracepoint_probe *old; entry = get_tracepoint(name); if (!entry) { entry = add_tracepoint(name, signature); if (IS_ERR(entry)) - return (struct tracepoint_probe *)entry; + return (struct lttng_ust_tracepoint_probe *)entry; } old = tracepoint_entry_add_probe(entry, probe, data); if (IS_ERR(old) && !entry->refcount) @@ -433,7 +569,7 @@ int __tracepoint_probe_register(const char *name, void (*probe)(void), goto end; } - tracepoint_update_probes(); /* may update entry */ + tracepoint_sync_callsites(name); release_probes(old); end: pthread_mutex_unlock(&tracepoint_mutex); @@ -477,7 +613,7 @@ int __tracepoint_probe_unregister(const char *name, void (*probe)(void), 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); @@ -573,15 +709,16 @@ end: pthread_mutex_unlock(&tracepoint_mutex); } -void tracepoint_set_new_tracepoint_cb(void (*cb)(struct tracepoint *)) +void tracepoint_set_new_tracepoint_cb(void (*cb)(struct lttng_ust_tracepoint *)) { new_tracepoint_cb = cb; } -static void new_tracepoints(struct tracepoint * const *start, struct tracepoint * const *end) +static void new_tracepoints(struct lttng_ust_tracepoint * const *start, + struct lttng_ust_tracepoint * const *end) { if (new_tracepoint_cb) { - struct tracepoint * const *t; + struct lttng_ust_tracepoint * const *t; for (t = start; t < end; t++) { if (*t) @@ -590,21 +727,7 @@ 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 tracepoint_register_lib(struct lttng_ust_tracepoint * const *tracepoints_start, int tracepoints_count) { struct tracepoint_lib *pl, *iter; @@ -612,9 +735,13 @@ int tracepoint_register_lib(struct tracepoint * const *tracepoints_start, init_tracepoint(); pl = (struct tracepoint_lib *) zmalloc(sizeof(struct tracepoint_lib)); - + if (!pl) { + PERROR("Unable to register tracepoint lib"); + return -1; + } pl->tracepoints_start = tracepoints_start; pl->tracepoints_count = tracepoints_count; + CDS_INIT_LIST_HEAD(&pl->callsites); pthread_mutex_lock(&tracepoint_mutex); /* @@ -632,9 +759,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", @@ -650,36 +776,28 @@ lib_added: return 0; } -int tracepoint_unregister_lib(struct tracepoint * const *tracepoints_start) +int tracepoint_unregister_lib(struct lttng_ust_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); + /* + * Unregistering a callsite also decreases the + * callsite reference count of the corresponding + * tracepoint, and disables the tracepoint if + * the reference count drops to zero. + */ + 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; }