X-Git-Url: http://git.liburcu.org/?a=blobdiff_plain;f=liblttng-ust%2Ftracepoint.c;h=2c25d46250b603c6ed3e5c987f24aae905ef482a;hb=2c05c691a10f3130f9339a15369140ad6ca70f85;hp=8c137a3892a99558d26ce4066db1c341e87639a4;hpb=8a7ad54be134997ab5985968ac6ada9ea58cafb9;p=lttng-ust.git diff --git a/liblttng-ust/tracepoint.c b/liblttng-ust/tracepoint.c index 8c137a38..2c25d462 100644 --- a/liblttng-ust/tracepoint.c +++ b/liblttng-ust/tracepoint.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include /* for LTTNG_UST_SYM_NAME_LEN */ @@ -42,10 +43,17 @@ #include "jhash.h" #include "error.h" +/* Test compiler support for weak symbols with hidden visibility. */ +int __tracepoint_test_symbol1 __attribute__((weak, visibility("hidden"))); +void *__tracepoint_test_symbol2 __attribute__((weak, visibility("hidden"))); +struct { + char a[24]; +} __tracepoint_test_symbol3 __attribute__((weak, visibility("hidden"))); + /* 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. @@ -85,6 +93,9 @@ static struct cds_hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; static CDS_LIST_HEAD(old_probes); static int need_update; +static CDS_LIST_HEAD(release_queue); +static int release_queue_need_update; + /* * Note about RCU : * It is used to to delay the free of multiple probes array until a quiescent @@ -93,11 +104,11 @@ 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]; + char *signature; + char *name; }; struct tp_probes { @@ -106,7 +117,7 @@ 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]; }; /* @@ -120,14 +131,16 @@ 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; + struct lttng_ust_tracepoint *tp; + bool tp_entry_callsite_ref; /* Has a tp_entry took a ref on this callsite */ }; /* 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; } @@ -158,7 +171,7 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void (*probe)(void), void *data) { int nr_probes = 0; - struct tracepoint_probe *old, *new; + struct lttng_ust_tracepoint_probe *old, *new; if (!probe) { WARN_ON(1); @@ -178,7 +191,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; @@ -193,7 +207,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; @@ -271,6 +285,8 @@ static struct tracepoint_entry *add_tracepoint(const char *name, struct cds_hlist_node *node; struct tracepoint_entry *e; size_t name_len = strlen(name); + size_t sig_len = strlen(signature); + size_t sig_off, name_off; uint32_t hash; if (name_len > LTTNG_UST_SYM_NAME_LEN - 1) { @@ -285,19 +301,29 @@ static struct tracepoint_entry *add_tracepoint(const char *name, return ERR_PTR(-EEXIST); /* Already there */ } } + /* - * Using zmalloc here to allocate a variable length element. Could - * cause some memory fragmentation if overused. + * Using zmalloc here to allocate a variable length elements: name and + * signature. Could cause some memory fragmentation if overused. */ - e = zmalloc(sizeof(struct tracepoint_entry) + name_len + 1); + name_off = sizeof(struct tracepoint_entry); + sig_off = name_off + name_len + 1; + + e = zmalloc(sizeof(struct tracepoint_entry) + name_len + 1 + sig_len + 1); if (!e) return ERR_PTR(-ENOMEM); - memcpy(&e->name[0], name, name_len + 1); + e->name = (char *) e + name_off; + memcpy(e->name, name, name_len + 1); e->name[name_len] = '\0'; + + e->signature = (char *) e + sig_off; + memcpy(e->signature, signature, sig_len + 1); + e->signature[sig_len] = '\0'; + e->probes = NULL; e->refcount = 0; e->callsite_refcount = 0; - e->signature = signature; + cds_hlist_add_head(&e->hlist, head); return e; } @@ -316,7 +342,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); /* @@ -345,7 +371,7 @@ static void set_tracepoint(struct tracepoint_entry **entry, * is used. */ rcu_assign_pointer(elem->probes, (*entry)->probes); - elem->state = active; + CMM_STORE_SHARED(elem->state, active); } /* @@ -354,9 +380,9 @@ 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; + CMM_STORE_SHARED(elem->state, 0); rcu_assign_pointer(elem->probes, NULL); } @@ -364,7 +390,7 @@ static void disable_tracepoint(struct tracepoint *elem) * 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) +static void add_callsite(struct tracepoint_lib * lib, struct lttng_ust_tracepoint *tp) { struct cds_hlist_head *head; struct callsite_entry *e; @@ -380,7 +406,10 @@ static void add_callsite(struct tracepoint_lib * lib, struct tracepoint *tp) hash = jhash(name, name_len, 0); head = &callsite_table[hash & (CALLSITE_TABLE_SIZE - 1)]; e = zmalloc(sizeof(struct callsite_entry)); - assert(e); + 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); @@ -389,6 +418,7 @@ static void add_callsite(struct tracepoint_lib * lib, struct tracepoint *tp) if (!tp_entry) return; tp_entry->callsite_refcount++; + e->tp_entry_callsite_ref = true; } /* @@ -401,7 +431,8 @@ static void remove_callsite(struct callsite_entry *e) tp_entry = get_tracepoint(e->tp->name); if (tp_entry) { - tp_entry->callsite_refcount--; + if (e->tp_entry_callsite_ref) + tp_entry->callsite_refcount--; if (tp_entry->callsite_refcount == 0) disable_tracepoint(e->tp); } @@ -432,15 +463,20 @@ static void tracepoint_sync_callsites(const char *name) 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; + struct lttng_ust_tracepoint *tp = e->tp; if (strncmp(name, tp->name, LTTNG_UST_SYM_NAME_LEN - 1)) continue; if (tp_entry) { + if (!e->tp_entry_callsite_ref) { + tp_entry->callsite_refcount++; + e->tp_entry_callsite_ref = true; + } set_tracepoint(&tp_entry, tp, !!tp_entry->refcount); } else { disable_tracepoint(tp); + e->tp_entry_callsite_ref = false; } } } @@ -453,10 +489,10 @@ static void tracepoint_sync_callsites(const char *name) * 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++) { @@ -484,9 +520,9 @@ static void lib_update_tracepoints(struct tracepoint_lib *lib) static void lib_register_callsites(struct tracepoint_lib *lib) { - struct tracepoint * const *begin; - struct tracepoint * const *end; - struct tracepoint * const *iter; + 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; @@ -521,18 +557,23 @@ static void tracepoint_update_probes(void) 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) { + if (entry) { + if (strcmp(entry->signature, signature) != 0) { + ERR("Tracepoint and probe signature do not match."); + return ERR_PTR(-EINVAL); + } + } else { 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) @@ -540,6 +581,16 @@ tracepoint_add_probe(const char *name, void (*probe)(void), void *data, return old; } +static void tracepoint_release_queue_add_old_probes(void *old) +{ + release_queue_need_update = 1; + if (old) { + struct tp_probes *tp_probes = caa_container_of(old, + struct tp_probes, probes[0]); + cds_list_add(&tp_probes->u.list, &release_queue); + } +} + /** * __tracepoint_probe_register - Connect a probe to a tracepoint * @name: tracepoint name @@ -571,6 +622,33 @@ end: return ret; } +/* + * Caller needs to invoke __tracepoint_probe_release_queue() after + * calling __tracepoint_probe_register_queue_release() one or multiple + * times to ensure it does not leak memory. + */ +int __tracepoint_probe_register_queue_release(const char *name, + void (*probe)(void), void *data, const char *signature) +{ + void *old; + int ret = 0; + + DBG("Registering probe to tracepoint %s. Queuing release.", 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_sync_callsites(name); + tracepoint_release_queue_add_old_probes(old); +end: + pthread_mutex_unlock(&tracepoint_mutex); + return ret; +} + static void *tracepoint_remove_probe(const char *name, void (*probe)(void), void *data) { @@ -615,6 +693,57 @@ end: return ret; } +/* + * Caller needs to invoke __tracepoint_probe_release_queue() after + * calling __tracepoint_probe_unregister_queue_release() one or multiple + * times to ensure it does not leak memory. + */ +int __tracepoint_probe_unregister_queue_release(const char *name, + void (*probe)(void), void *data) +{ + void *old; + int ret = 0; + + DBG("Un-registering probe from tracepoint %s. Queuing release.", name); + + pthread_mutex_lock(&tracepoint_mutex); + old = tracepoint_remove_probe(name, probe, data); + if (IS_ERR(old)) { + ret = PTR_ERR(old); + goto end; + } + tracepoint_sync_callsites(name); + tracepoint_release_queue_add_old_probes(old); +end: + pthread_mutex_unlock(&tracepoint_mutex); + return ret; +} + +void __tracepoint_probe_prune_release_queue(void) +{ + CDS_LIST_HEAD(release_probes); + struct tp_probes *pos, *next; + + DBG("Release queue of unregistered tracepoint probes."); + + pthread_mutex_lock(&tracepoint_mutex); + if (!release_queue_need_update) + goto end; + if (!cds_list_empty(&release_queue)) + cds_list_replace_init(&release_queue, &release_probes); + release_queue_need_update = 0; + + /* Wait for grace period between all sync_callsites and free. */ + synchronize_rcu(); + + cds_list_for_each_entry_safe(pos, next, &release_probes, u.list) { + cds_list_del(&pos->u.list); + free(pos); + } +end: + pthread_mutex_unlock(&tracepoint_mutex); +} + static void tracepoint_add_old_probes(void *old) { need_update = 1; @@ -695,24 +824,26 @@ void tracepoint_probe_update_all(void) need_update = 0; tracepoint_update_probes(); + /* Wait for grace period between update_probes and free. */ + synchronize_rcu(); cds_list_for_each_entry_safe(pos, next, &release_probes, u.list) { cds_list_del(&pos->u.list); - synchronize_rcu(); free(pos); } 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) @@ -721,7 +852,7 @@ static void new_tracepoints(struct tracepoint * const *start, struct tracepoint } } -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; @@ -729,7 +860,10 @@ 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); @@ -767,7 +901,7 @@ 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; @@ -793,11 +927,34 @@ int tracepoint_unregister_lib(struct tracepoint * const *tracepoints_start) return 0; } +/* + * Report in debug message whether the compiler correctly supports weak + * hidden symbols. This test checks that the address associated with two + * weak symbols with hidden visibility is the same when declared within + * two compile units part of the same module. + */ +static void check_weak_hidden(void) +{ + DBG("Your compiler treats weak symbols with hidden visibility for integer objects as %s between compile units part of the same module.", + &__tracepoint_test_symbol1 == lttng_ust_tp_check_weak_hidden1() ? + "SAME address" : + "DIFFERENT addresses"); + DBG("Your compiler treats weak symbols with hidden visibility for pointer objects as %s between compile units part of the same module.", + &__tracepoint_test_symbol2 == lttng_ust_tp_check_weak_hidden2() ? + "SAME address" : + "DIFFERENT addresses"); + DBG("Your compiler treats weak symbols with hidden visibility for 24-byte structure objects as %s between compile units part of the same module.", + &__tracepoint_test_symbol3 == lttng_ust_tp_check_weak_hidden3() ? + "SAME address" : + "DIFFERENT addresses"); +} + void init_tracepoint(void) { if (uatomic_xchg(&initialized, 1) == 1) return; init_usterr(); + check_weak_hidden(); } void exit_tracepoint(void)