X-Git-Url: http://git.liburcu.org/?a=blobdiff_plain;f=liblttng-ust%2Flttng-events.c;h=7afee7fc4043ac58a1be6a46df6852a93e10882c;hb=8306baea3da8b1f429e14218b1ef7413ee56ed19;hp=939dcd8627f340793d838140a20a24bc847c77dc;hpb=89f26d57d2880f83c9fee7fbc6ec870d1def0d2c;p=lttng-ust.git diff --git a/liblttng-ust/lttng-events.c b/liblttng-ust/lttng-events.c index 939dcd86..7afee7fc 100644 --- a/liblttng-ust/lttng-events.c +++ b/liblttng-ust/lttng-events.c @@ -21,6 +21,7 @@ */ #define _GNU_SOURCE +#define _LGPL_SOURCE #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include "clock.h" @@ -47,14 +49,17 @@ #include #include #include +#include +#include #include "error.h" #include "compat.h" #include "lttng-ust-uuid.h" #include "tracepoint-internal.h" +#include "string-utils.h" #include "lttng-tracer.h" #include "lttng-tracer-core.h" -#include "lttng-ust-baddr.h" +#include "lttng-ust-statedump.h" #include "wait.h" #include "../libringbuffer/shm.h" #include "jhash.h" @@ -72,6 +77,7 @@ struct cds_list_head *_lttng_get_sessions(void) } static void _lttng_event_destroy(struct lttng_event *event); +static void _lttng_enum_destroy(struct lttng_enum *_enum); static void lttng_session_lazy_sync_enablers(struct lttng_session *session); @@ -137,11 +143,18 @@ struct lttng_session *lttng_session_create(void) session = zmalloc(sizeof(struct lttng_session)); if (!session) return NULL; + if (lttng_session_context_init(&session->ctx)) { + free(session); + return NULL; + } CDS_INIT_LIST_HEAD(&session->chan_head); CDS_INIT_LIST_HEAD(&session->events_head); + CDS_INIT_LIST_HEAD(&session->enums_head); CDS_INIT_LIST_HEAD(&session->enablers_head); for (i = 0; i < LTTNG_UST_EVENT_HT_SIZE; i++) CDS_INIT_HLIST_HEAD(&session->events_ht.table[i]); + for (i = 0; i < LTTNG_UST_ENUM_HT_SIZE; i++) + CDS_INIT_HLIST_HEAD(&session->enums_ht.table[i]); cds_list_add(&session->node, &sessions); return session; } @@ -174,7 +187,7 @@ void register_event(struct lttng_event *event) assert(event->registered == 0); desc = event->desc; - ret = __tracepoint_probe_register(desc->name, + ret = __tracepoint_probe_register_queue_release(desc->name, desc->probe_callback, event, desc->signature); WARN_ON_ONCE(ret); @@ -190,7 +203,7 @@ void unregister_event(struct lttng_event *event) assert(event->registered == 1); desc = event->desc; - ret = __tracepoint_probe_unregister(desc->name, + ret = __tracepoint_probe_unregister_queue_release(desc->name, desc->probe_callback, event); WARN_ON_ONCE(ret); @@ -212,6 +225,7 @@ void lttng_session_destroy(struct lttng_session *session) { struct lttng_channel *chan, *tmpchan; struct lttng_event *event, *tmpevent; + struct lttng_enum *_enum, *tmp_enum; struct lttng_enabler *enabler, *tmpenabler; CMM_ACCESS_ONCE(session->active) = 0; @@ -219,18 +233,170 @@ void lttng_session_destroy(struct lttng_session *session) _lttng_event_unregister(event); } synchronize_trace(); /* Wait for in-flight events to complete */ + __tracepoint_probe_prune_release_queue(); cds_list_for_each_entry_safe(enabler, tmpenabler, &session->enablers_head, node) lttng_enabler_destroy(enabler); cds_list_for_each_entry_safe(event, tmpevent, &session->events_head, node) _lttng_event_destroy(event); + cds_list_for_each_entry_safe(_enum, tmp_enum, + &session->enums_head, node) + _lttng_enum_destroy(_enum); cds_list_for_each_entry_safe(chan, tmpchan, &session->chan_head, node) _lttng_channel_unmap(chan); cds_list_del(&session->node); + lttng_destroy_context(session->ctx); free(session); } +static +int lttng_enum_create(const struct lttng_enum_desc *desc, + struct lttng_session *session) +{ + const char *enum_name = desc->name; + struct lttng_enum *_enum; + struct cds_hlist_head *head; + int ret = 0; + size_t name_len = strlen(enum_name); + uint32_t hash; + int notify_socket; + + /* Check if this enum is already registered for this session. */ + hash = jhash(enum_name, name_len, 0); + head = &session->enums_ht.table[hash & (LTTNG_UST_ENUM_HT_SIZE - 1)]; + + _enum = lttng_ust_enum_get_from_desc(session, desc); + if (_enum) { + ret = -EEXIST; + goto exist; + } + + notify_socket = lttng_get_notify_socket(session->owner); + if (notify_socket < 0) { + ret = notify_socket; + goto socket_error; + } + + _enum = zmalloc(sizeof(*_enum)); + if (!_enum) { + ret = -ENOMEM; + goto cache_error; + } + _enum->session = session; + _enum->desc = desc; + + ret = ustcomm_register_enum(notify_socket, + session->objd, + enum_name, + desc->nr_entries, + desc->entries, + &_enum->id); + if (ret < 0) { + DBG("Error (%d) registering enumeration to sessiond", ret); + goto sessiond_register_error; + } + cds_list_add(&_enum->node, &session->enums_head); + cds_hlist_add_head(&_enum->hlist, head); + return 0; + +sessiond_register_error: + free(_enum); +cache_error: +socket_error: +exist: + return ret; +} + +static +int lttng_create_enum_check(const struct lttng_type *type, + struct lttng_session *session) +{ + switch (type->atype) { + case atype_enum: + { + const struct lttng_enum_desc *enum_desc; + int ret; + + enum_desc = type->u.basic.enumeration.desc; + ret = lttng_enum_create(enum_desc, session); + if (ret && ret != -EEXIST) { + DBG("Unable to create enum error: (%d)", ret); + return ret; + } + break; + } + case atype_dynamic: + { + const struct lttng_event_field *tag_field_generic; + const struct lttng_enum_desc *enum_desc; + int ret; + + tag_field_generic = lttng_ust_dynamic_type_tag_field(); + enum_desc = tag_field_generic->type.u.basic.enumeration.desc; + ret = lttng_enum_create(enum_desc, session); + if (ret && ret != -EEXIST) { + DBG("Unable to create enum error: (%d)", ret); + return ret; + } + break; + } + default: + /* TODO: nested types when they become supported. */ + break; + } + return 0; +} + +static +int lttng_create_all_event_enums(size_t nr_fields, + const struct lttng_event_field *event_fields, + struct lttng_session *session) +{ + size_t i; + int ret; + + /* For each field, ensure enum is part of the session. */ + for (i = 0; i < nr_fields; i++) { + const struct lttng_type *type = &event_fields[i].type; + + ret = lttng_create_enum_check(type, session); + if (ret) + return ret; + } + return 0; +} + +static +int lttng_create_all_ctx_enums(size_t nr_fields, + const struct lttng_ctx_field *ctx_fields, + struct lttng_session *session) +{ + size_t i; + int ret; + + /* For each field, ensure enum is part of the session. */ + for (i = 0; i < nr_fields; i++) { + const struct lttng_type *type = &ctx_fields[i].event_field.type; + + ret = lttng_create_enum_check(type, session); + if (ret) + return ret; + } + return 0; +} + +/* + * Ensure that a state-dump will be performed for this session at the end + * of the current handle_message(). + */ +int lttng_session_statedump(struct lttng_session *session) +{ + session->statedump_pending = 1; + lttng_ust_sockinfo_session_enabled(session->owner); + return 0; +} + int lttng_session_enable(struct lttng_session *session) { int ret = 0; @@ -249,6 +415,9 @@ int lttng_session_enable(struct lttng_session *session) /* Set transient enabler state to "enabled" */ session->tstate = 1; + /* We need to sync enablers with session before activation. */ + lttng_session_sync_enablers(session); + /* * Snapshot the number of events per channel to know the type of header * we need to use. @@ -266,8 +435,15 @@ int lttng_session_enable(struct lttng_session *session) if (ctx) { nr_fields = ctx->nr_fields; fields = ctx->fields; + ret = lttng_create_all_ctx_enums(nr_fields, fields, + session); + if (ret < 0) { + DBG("Error (%d) adding enum to session", ret); + return ret; + } } ret = ustcomm_register_channel(notify_socket, + session, session->objd, chan->objd, nr_fields, @@ -285,15 +461,13 @@ int lttng_session_enable(struct lttng_session *session) } } - /* We need to sync enablers with session before activation. */ - lttng_session_sync_enablers(session); - /* Set atomically the state to "active" */ CMM_ACCESS_ONCE(session->active) = 1; CMM_ACCESS_ONCE(session->been_active) = 1; - session->statedump_pending = 1; - lttng_ust_sockinfo_session_enabled(session->owner); + ret = lttng_session_statedump(session); + if (ret) + return ret; end: return ret; } @@ -361,7 +535,6 @@ int lttng_event_create(const struct lttng_event_desc *desc, struct lttng_event *event; struct lttng_session *session = chan->session; struct cds_hlist_head *head; - struct cds_hlist_node *node; int ret = 0; size_t name_len = strlen(event_name); uint32_t hash; @@ -370,15 +543,6 @@ int lttng_event_create(const struct lttng_event_desc *desc, hash = jhash(event_name, name_len, 0); head = &chan->session->events_ht.table[hash & (LTTNG_UST_EVENT_HT_SIZE - 1)]; - cds_hlist_for_each_entry(event, node, head, hlist) { - assert(event->desc); - if (!strncmp(event->desc->name, desc->name, - LTTNG_UST_SYM_NAME_LEN - 1) - && chan == event->chan) { - ret = -EEXIST; - goto exist; - } - } notify_socket = lttng_get_notify_socket(session->owner); if (notify_socket < 0) { @@ -386,6 +550,13 @@ int lttng_event_create(const struct lttng_event_desc *desc, goto socket_error; } + ret = lttng_create_all_event_enums(desc->nr_fields, desc->fields, + session); + if (ret < 0) { + DBG("Error (%d) adding enum to session", ret); + goto create_enum_error; + } + /* * Check if loglevel match. Refuse to connect event if not. */ @@ -414,6 +585,7 @@ int lttng_event_create(const struct lttng_event_desc *desc, /* Fetch event ID from sessiond */ ret = ustcomm_register_event(notify_socket, + session, session->objd, chan->objd, event_name, @@ -428,8 +600,6 @@ int lttng_event_create(const struct lttng_event_desc *desc, goto sessiond_register_error; } - /* Populate lttng_event structure before tracepoint registration. */ - cmm_smp_wmb(); cds_list_add(&event->node, &chan->session->events_head); cds_hlist_add_head(&event->hlist, head); return 0; @@ -437,22 +607,21 @@ int lttng_event_create(const struct lttng_event_desc *desc, sessiond_register_error: free(event); cache_error: +create_enum_error: socket_error: -exist: return ret; } static -int lttng_desc_match_wildcard_enabler(const struct lttng_event_desc *desc, +int lttng_desc_match_star_glob_enabler(const struct lttng_event_desc *desc, struct lttng_enabler *enabler) { int loglevel = 0; unsigned int has_loglevel = 0; - assert(enabler->type == LTTNG_ENABLER_WILDCARD); - /* Compare excluding final '*' */ - if (strncmp(desc->name, enabler->event_param.name, - strlen(enabler->event_param.name) - 1)) + assert(enabler->type == LTTNG_ENABLER_STAR_GLOB); + if (!strutils_star_glob_match(enabler->event_param.name, SIZE_MAX, + desc->name, SIZE_MAX)) return 0; if (desc->loglevel) { loglevel = *(*desc->loglevel); @@ -492,34 +661,35 @@ static int lttng_desc_match_enabler(const struct lttng_event_desc *desc, struct lttng_enabler *enabler) { - struct lttng_ust_excluder_node *excluder; - - /* If event matches with an excluder, return 'does not match' */ - cds_list_for_each_entry(excluder, &enabler->excluder_head, node) { - int count; - - for (count = 0; count < excluder->excluder.count; count++) { - int found, len; - char *excluder_name; - - excluder_name = (char *) (excluder->excluder.names) - + count * LTTNG_UST_SYM_NAME_LEN; - len = strnlen(excluder_name, LTTNG_UST_SYM_NAME_LEN); - if (len > 0 && excluder_name[len - 1] == '*') { - found = !strncmp(desc->name, excluder_name, - len - 1); - } else { - found = !strncmp(desc->name, excluder_name, - LTTNG_UST_SYM_NAME_LEN - 1); - } - if (found) { - return 0; + switch (enabler->type) { + case LTTNG_ENABLER_STAR_GLOB: + { + struct lttng_ust_excluder_node *excluder; + + if (!lttng_desc_match_star_glob_enabler(desc, enabler)) { + return 0; + } + + /* + * If the matching event matches with an excluder, + * return 'does not match' + */ + cds_list_for_each_entry(excluder, &enabler->excluder_head, node) { + int count; + + for (count = 0; count < excluder->excluder.count; count++) { + int len; + char *excluder_name; + + excluder_name = (char *) (excluder->excluder.names) + + count * LTTNG_UST_SYM_NAME_LEN; + len = strnlen(excluder_name, LTTNG_UST_SYM_NAME_LEN); + if (len > 0 && strutils_star_glob_match(excluder_name, len, desc->name, SIZE_MAX)) + return 0; } } + return 1; } - switch (enabler->type) { - case LTTNG_ENABLER_WILDCARD: - return lttng_desc_match_wildcard_enabler(desc, enabler); case LTTNG_ENABLER_EVENT: return lttng_desc_match_event_enabler(desc, enabler); default: @@ -614,6 +784,111 @@ void lttng_create_event_if_missing(struct lttng_enabler *enabler) } } +/* + * Iterate over all the UST sessions to unregister and destroy all probes from + * the probe provider descriptor received as argument. Must me called with the + * ust_lock held. + */ +void lttng_probe_provider_unregister_events(struct lttng_probe_desc *provider_desc) +{ + struct cds_hlist_node *node, *tmp_node; + struct cds_list_head *sessionsp; + struct lttng_session *session; + struct cds_hlist_head *head; + struct lttng_event *event; + unsigned int i, j; + + /* Get handle on list of sessions. */ + sessionsp = _lttng_get_sessions(); + + /* + * Iterate over all events in the probe provider descriptions and sessions + * to queue the unregistration of the events. + */ + for (i = 0; i < provider_desc->nr_events; i++) { + const struct lttng_event_desc *event_desc; + const char *event_name; + size_t name_len; + uint32_t hash; + + event_desc = provider_desc->event_desc[i]; + event_name = event_desc->name; + name_len = strlen(event_name); + hash = jhash(event_name, name_len, 0); + + /* Iterate over all session to find the current event description. */ + cds_list_for_each_entry(session, sessionsp, node) { + /* + * Get the list of events in the hashtable bucket and iterate to + * find the event matching this descriptor. + */ + head = &session->events_ht.table[hash & (LTTNG_UST_EVENT_HT_SIZE - 1)]; + cds_hlist_for_each_entry(event, node, head, hlist) { + if (event_desc == event->desc) { + /* Queue the unregistration of this event. */ + _lttng_event_unregister(event); + break; + } + } + } + } + + /* Wait for grace period. */ + synchronize_trace(); + /* Prune the unregistration queue. */ + __tracepoint_probe_prune_release_queue(); + + /* + * It is now safe to destroy the events and remove them from the event list + * and hashtables. + */ + for (i = 0; i < provider_desc->nr_events; i++) { + const struct lttng_event_desc *event_desc; + const char *event_name; + size_t name_len; + uint32_t hash; + + event_desc = provider_desc->event_desc[i]; + event_name = event_desc->name; + name_len = strlen(event_name); + hash = jhash(event_name, name_len, 0); + + /* Iterate over all sessions to find the current event description. */ + cds_list_for_each_entry(session, sessionsp, node) { + /* + * Get the list of events in the hashtable bucket and iterate to + * find the event matching this descriptor. + */ + head = &session->events_ht.table[hash & (LTTNG_UST_EVENT_HT_SIZE - 1)]; + cds_hlist_for_each_entry_safe(event, node, tmp_node, head, hlist) { + if (event_desc == event->desc) { + /* Destroy enums of the current event. */ + for (j = 0; j < event->desc->nr_fields; j++) { + const struct lttng_enum_desc *enum_desc; + const struct lttng_event_field *field; + struct lttng_enum *curr_enum; + + field = &(event->desc->fields[j]); + if (field->type.atype != atype_enum) { + continue; + } + + enum_desc = field->type.u.basic.enumeration.desc; + curr_enum = lttng_ust_enum_get_from_desc(session, enum_desc); + if (curr_enum) { + _lttng_enum_destroy(curr_enum); + } + } + + /* Destroy event. */ + _lttng_event_destroy(event); + break; + } + } + } + } +} + /* * Create events associated with an enabler (if not already present), * and add backward reference from the event to the enabler. @@ -683,7 +958,7 @@ void lttng_handle_pending_statedump(void *owner) struct lttng_session *session; /* Execute state dump */ - lttng_ust_baddr_statedump(owner); + do_lttng_ust_statedump(owner); /* Clear pending state dump */ if (ust_lock()) { @@ -709,7 +984,11 @@ void _lttng_event_destroy(struct lttng_event *event) { struct lttng_enabler_ref *enabler_ref, *tmp_enabler_ref; + /* Remove from event list. */ cds_list_del(&event->node); + /* Remove from event hash table. */ + cds_hlist_del(&event->hlist); + lttng_destroy_context(event->ctx); lttng_free_event_filter_runtime(event); /* Free event enabler refs */ @@ -719,6 +998,14 @@ void _lttng_event_destroy(struct lttng_event *event) free(event); } +static +void _lttng_enum_destroy(struct lttng_enum *_enum) +{ + cds_list_del(&_enum->node); + cds_hlist_del(&_enum->hlist); + free(_enum); +} + void lttng_ust_events_exit(void) { struct lttng_session *session, *tmpsession; @@ -785,6 +1072,7 @@ int lttng_enabler_attach_exclusion(struct lttng_enabler *enabler, } int lttng_attach_context(struct lttng_ust_context *context_param, + union ust_args *uargs, struct lttng_ctx **ctx, struct lttng_session *session) { /* @@ -817,6 +1105,37 @@ int lttng_attach_context(struct lttng_ust_context *context_param, return lttng_add_procname_to_ctx(ctx); case LTTNG_UST_CONTEXT_IP: return lttng_add_ip_to_ctx(ctx); + case LTTNG_UST_CONTEXT_CPU_ID: + return lttng_add_cpu_id_to_ctx(ctx); + case LTTNG_UST_CONTEXT_APP_CONTEXT: + return lttng_ust_add_app_context_to_ctx_rcu(uargs->app_context.ctxname, + ctx); + case LTTNG_UST_CONTEXT_CGROUP_NS: + return lttng_add_cgroup_ns_to_ctx(ctx); + case LTTNG_UST_CONTEXT_IPC_NS: + return lttng_add_ipc_ns_to_ctx(ctx); + case LTTNG_UST_CONTEXT_MNT_NS: + return lttng_add_mnt_ns_to_ctx(ctx); + case LTTNG_UST_CONTEXT_NET_NS: + return lttng_add_net_ns_to_ctx(ctx); + case LTTNG_UST_CONTEXT_PID_NS: + return lttng_add_pid_ns_to_ctx(ctx); + case LTTNG_UST_CONTEXT_USER_NS: + return lttng_add_user_ns_to_ctx(ctx); + case LTTNG_UST_CONTEXT_UTS_NS: + return lttng_add_uts_ns_to_ctx(ctx); + case LTTNG_UST_CONTEXT_VUID: + return lttng_add_vuid_to_ctx(ctx); + case LTTNG_UST_CONTEXT_VEUID: + return lttng_add_veuid_to_ctx(ctx); + case LTTNG_UST_CONTEXT_VSUID: + return lttng_add_vsuid_to_ctx(ctx); + case LTTNG_UST_CONTEXT_VGID: + return lttng_add_vgid_to_ctx(ctx); + case LTTNG_UST_CONTEXT_VEGID: + return lttng_add_vegid_to_ctx(ctx); + case LTTNG_UST_CONTEXT_VSGID: + return lttng_add_vsgid_to_ctx(ctx); default: return -EINVAL; } @@ -931,6 +1250,7 @@ void lttng_session_sync_enablers(struct lttng_session *session) lttng_filter_sync_state(runtime); } } + __tracepoint_probe_prune_release_queue(); } /* @@ -947,3 +1267,44 @@ void lttng_session_lazy_sync_enablers(struct lttng_session *session) return; lttng_session_sync_enablers(session); } + +/* + * Update all sessions with the given app context. + * Called with ust lock held. + * This is invoked when an application context gets loaded/unloaded. It + * ensures the context callbacks are in sync with the application + * context (either app context callbacks, or dummy callbacks). + */ +void lttng_ust_context_set_session_provider(const char *name, + size_t (*get_size)(struct lttng_ctx_field *field, size_t offset), + void (*record)(struct lttng_ctx_field *field, + struct lttng_ust_lib_ring_buffer_ctx *ctx, + struct lttng_channel *chan), + void (*get_value)(struct lttng_ctx_field *field, + struct lttng_ctx_value *value)) +{ + struct lttng_session *session; + + cds_list_for_each_entry(session, &sessions, node) { + struct lttng_channel *chan; + struct lttng_event *event; + int ret; + + ret = lttng_ust_context_set_provider_rcu(&session->ctx, + name, get_size, record, get_value); + if (ret) + abort(); + cds_list_for_each_entry(chan, &session->chan_head, node) { + ret = lttng_ust_context_set_provider_rcu(&chan->ctx, + name, get_size, record, get_value); + if (ret) + abort(); + } + cds_list_for_each_entry(event, &session->events_head, node) { + ret = lttng_ust_context_set_provider_rcu(&event->ctx, + name, get_size, record, get_value); + if (ret) + abort(); + } + } +}