X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=lttng-events.c;h=3616e010410134ee3021260cbf3d7de1bb018610;hb=3c9970797850893aaf1b9019d21868d73580e17d;hp=7055c3ed05af5f812edf319d8d7323a93b4a3c84;hpb=9bbf98da13bbaf289ac5787a80325d2e1dd81d0d;p=lttng-modules.git diff --git a/lttng-events.c b/lttng-events.c index 7055c3ed..3616e010 100644 --- a/lttng-events.c +++ b/lttng-events.c @@ -31,6 +31,8 @@ #include #include #include "wrapper/file.h" +#include + #include "wrapper/uuid.h" #include "wrapper/vmalloc.h" /* for wrapper_vmalloc_sync_all() */ #include "wrapper/random.h" @@ -51,6 +53,10 @@ static LIST_HEAD(lttng_transport_list); static DEFINE_MUTEX(sessions_mutex); static struct kmem_cache *event_cache; +static void lttng_session_lazy_sync_enablers(struct lttng_session *session); +static void lttng_session_sync_enablers(struct lttng_session *session); +static void lttng_enabler_destroy(struct lttng_enabler *enabler); + static void _lttng_event_destroy(struct lttng_event *event); static void _lttng_channel_destroy(struct lttng_channel *chan); static int _lttng_event_unregister(struct lttng_event *event); @@ -77,10 +83,35 @@ void synchronize_trace(void) #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) */ } +void lttng_lock_sessions(void) +{ + mutex_lock(&sessions_mutex); +} + +void lttng_unlock_sessions(void) +{ + mutex_unlock(&sessions_mutex); +} + +/* + * Called with sessions lock held. + */ +int lttng_session_active(void) +{ + struct lttng_session *iter; + + list_for_each_entry(iter, &sessions, list) { + if (iter->active) + return 1; + } + return 0; +} + struct lttng_session *lttng_session_create(void) { struct lttng_session *session; struct lttng_metadata_cache *metadata_cache; + int i; mutex_lock(&sessions_mutex); session = kzalloc(sizeof(struct lttng_session), GFP_KERNEL); @@ -104,6 +135,9 @@ struct lttng_session *lttng_session_create(void) INIT_LIST_HEAD(&metadata_cache->metadata_stream); memcpy(&metadata_cache->uuid, &session->uuid, sizeof(metadata_cache->uuid)); + INIT_LIST_HEAD(&session->enablers_head); + for (i = 0; i < LTTNG_EVENT_HT_SIZE; i++) + INIT_HLIST_HEAD(&session->events_ht.table[i]); list_add(&session->list, &sessions); mutex_unlock(&sessions_mutex); return session; @@ -130,6 +164,7 @@ void lttng_session_destroy(struct lttng_session *session) struct lttng_channel *chan, *tmpchan; struct lttng_event *event, *tmpevent; struct lttng_metadata_stream *metadata_stream; + struct lttng_enabler *enabler, *tmpenabler; int ret; mutex_lock(&sessions_mutex); @@ -143,6 +178,9 @@ void lttng_session_destroy(struct lttng_session *session) WARN_ON(ret); } synchronize_trace(); /* Wait for in-flight events to complete */ + list_for_each_entry_safe(enabler, tmpenabler, + &session->enablers_head, node) + lttng_enabler_destroy(enabler); list_for_each_entry_safe(event, tmpevent, &session->events, list) _lttng_event_destroy(event); list_for_each_entry_safe(chan, tmpchan, &session->chan, list) { @@ -170,6 +208,9 @@ int lttng_session_enable(struct lttng_session *session) goto end; } + /* Set transient enabler state to "enabled" */ + session->tstate = 1; + /* * Snapshot the number of events per channel to know the type of header * we need to use. @@ -183,6 +224,9 @@ int lttng_session_enable(struct lttng_session *session) chan->header_type = 2; /* large */ } + /* We need to sync enablers with session before activation. */ + lttng_session_sync_enablers(session); + ACCESS_ONCE(session->active) = 1; ACCESS_ONCE(session->been_active) = 1; ret = _lttng_session_metadata_statedump(session); @@ -208,6 +252,10 @@ int lttng_session_disable(struct lttng_session *session) goto end; } ACCESS_ONCE(session->active) = 0; + + /* Set transient enabler state to "disabled" */ + session->tstate = 0; + lttng_session_sync_enablers(session); end: mutex_unlock(&sessions_mutex); return ret; @@ -215,50 +263,88 @@ end: int lttng_channel_enable(struct lttng_channel *channel) { - int old; + int ret = 0; - if (channel->channel_type == METADATA_CHANNEL) - return -EPERM; - old = xchg(&channel->enabled, 1); - if (old) - return -EEXIST; - return 0; + mutex_lock(&sessions_mutex); + if (channel->channel_type == METADATA_CHANNEL) { + ret = -EPERM; + goto end; + } + if (channel->enabled) { + ret = -EEXIST; + goto end; + } + /* Set transient enabler state to "enabled" */ + channel->tstate = 1; + lttng_session_sync_enablers(channel->session); + /* Set atomically the state to "enabled" */ + ACCESS_ONCE(channel->enabled) = 1; +end: + mutex_unlock(&sessions_mutex); + return ret; } int lttng_channel_disable(struct lttng_channel *channel) { - int old; + int ret = 0; - if (channel->channel_type == METADATA_CHANNEL) - return -EPERM; - old = xchg(&channel->enabled, 0); - if (!old) - return -EEXIST; - return 0; + mutex_lock(&sessions_mutex); + if (channel->channel_type == METADATA_CHANNEL) { + ret = -EPERM; + goto end; + } + if (!channel->enabled) { + ret = -EEXIST; + goto end; + } + /* Set atomically the state to "disabled" */ + ACCESS_ONCE(channel->enabled) = 0; + /* Set transient enabler state to "enabled" */ + channel->tstate = 0; + lttng_session_sync_enablers(channel->session); +end: + mutex_unlock(&sessions_mutex); + return ret; } int lttng_event_enable(struct lttng_event *event) { - int old; + int ret = 0; - if (event->chan->channel_type == METADATA_CHANNEL) - return -EPERM; - old = xchg(&event->enabled, 1); - if (old) - return -EEXIST; - return 0; + mutex_lock(&sessions_mutex); + if (event->chan->channel_type == METADATA_CHANNEL) { + ret = -EPERM; + goto end; + } + if (event->enabled) { + ret = -EEXIST; + goto end; + } + ACCESS_ONCE(event->enabled) = 1; + lttng_session_sync_enablers(event->chan->session); +end: + mutex_unlock(&sessions_mutex); + return ret; } int lttng_event_disable(struct lttng_event *event) { - int old; + int ret = 0; - if (event->chan->channel_type == METADATA_CHANNEL) - return -EPERM; - old = xchg(&event->enabled, 0); - if (!old) - return -EEXIST; - return 0; + mutex_lock(&sessions_mutex); + if (event->chan->channel_type == METADATA_CHANNEL) { + ret = -EPERM; + goto end; + } + if (!event->enabled) { + ret = -EEXIST; + goto end; + } + ACCESS_ONCE(event->enabled) = 0; + lttng_session_sync_enablers(event->chan->session); +end: + mutex_unlock(&sessions_mutex); + return ret; } static struct lttng_transport *lttng_transport_find(const char *name) @@ -312,6 +398,7 @@ struct lttng_channel *lttng_channel_create(struct lttng_session *session, switch_timer_interval, read_timer_interval); if (!chan->chan) goto create_error; + chan->tstate = 1; chan->enabled = 1; chan->transport = transport; chan->channel_type = channel_type; @@ -365,36 +452,55 @@ void _lttng_metadata_channel_hangup(struct lttng_metadata_stream *stream) /* * Supports event creation while tracing session is active. + * Needs to be called with sessions mutex held. */ -struct lttng_event *lttng_event_create(struct lttng_channel *chan, - struct lttng_kernel_event *event_param, - void *filter, - const struct lttng_event_desc *internal_desc) +struct lttng_event *_lttng_event_create(struct lttng_channel *chan, + struct lttng_kernel_event *event_param, + void *filter, + const struct lttng_event_desc *event_desc, + enum lttng_kernel_instrumentation itype) { + struct lttng_session *session = chan->session; struct lttng_event *event; + const char *event_name; + struct hlist_head *head; + size_t name_len; + uint32_t hash; int ret; - mutex_lock(&sessions_mutex); if (chan->free_event_id == -1U) { ret = -EMFILE; goto full; } - /* - * This is O(n^2) (for each event, the loop is called at event - * creation). Might require a hash if we have lots of events. - */ - list_for_each_entry(event, &chan->session->events, list) { - if (!strcmp(event->desc->name, event_param->name)) { - /* - * Allow events with the same name to appear in - * different channels. - */ - if (event->chan == chan) { - ret = -EEXIST; - goto exist; - } + + switch (itype) { + case LTTNG_KERNEL_TRACEPOINT: + event_name = event_desc->name; + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + event_name = event_param->name; + break; + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + goto type_error; + } + name_len = strlen(event_name); + hash = jhash(event_name, name_len, 0); + head = &session->events_ht.table[hash & (LTTNG_EVENT_HT_SIZE - 1)]; + hlist_for_each_entry(event, head, hlist) { + WARN_ON_ONCE(!event->desc); + if (!strncmp(event->desc->name, event_name, + LTTNG_KERNEL_SYM_NAME_LEN - 1) + && chan == event->chan) { + ret = -EEXIST; + goto exist; } } + event = kmem_cache_zalloc(event_cache, GFP_KERNEL); if (!event) { ret = -ENOMEM; @@ -403,27 +509,32 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan, event->chan = chan; event->filter = filter; event->id = chan->free_event_id++; - event->enabled = 1; - event->instrumentation = event_param->instrumentation; - /* Populate lttng_event structure before tracepoint registration. */ - smp_wmb(); - switch (event_param->instrumentation) { + event->instrumentation = itype; + event->evtype = LTTNG_TYPE_EVENT; + INIT_LIST_HEAD(&event->enablers_ref_head); + + switch (itype) { case LTTNG_KERNEL_TRACEPOINT: - event->desc = lttng_event_get(event_param->name); + /* Event will be enabled by enabler sync. */ + event->enabled = 0; + event->registered = 0; + event->desc = lttng_event_get(event_name); if (!event->desc) { ret = -ENOENT; goto register_error; } - ret = lttng_wrapper_tracepoint_probe_register(event->desc->kname, - event->desc->probe_callback, - event); - if (ret) { - ret = -EINVAL; - goto register_error; - } + /* Populate lttng_event structure before event registration. */ + smp_wmb(); break; case LTTNG_KERNEL_KPROBE: - ret = lttng_kprobes_register(event_param->name, + event->enabled = 1; + event->registered = 1; + /* + * Populate lttng_event structure before event + * registration. + */ + smp_wmb(); + ret = lttng_kprobes_register(event_name, event_param->u.kprobe.symbol_name, event_param->u.kprobe.offset, event_param->u.kprobe.addr, @@ -440,6 +551,8 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan, struct lttng_event *event_return; /* kretprobe defines 2 events */ + event->enabled = 1; + event->registered = 1; event_return = kmem_cache_zalloc(event_cache, GFP_KERNEL); if (!event_return) { @@ -450,12 +563,13 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan, event_return->filter = filter; event_return->id = chan->free_event_id++; event_return->enabled = 1; - event_return->instrumentation = event_param->instrumentation; + event_return->registered = 1; + event_return->instrumentation = itype; /* * Populate lttng_event structure before kretprobe registration. */ smp_wmb(); - ret = lttng_kretprobes_register(event_param->name, + ret = lttng_kretprobes_register(event_name, event_param->u.kretprobe.symbol_name, event_param->u.kretprobe.offset, event_param->u.kretprobe.addr, @@ -483,7 +597,14 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan, break; } case LTTNG_KERNEL_FUNCTION: - ret = lttng_ftrace_register(event_param->name, + event->enabled = 1; + event->registered = 1; + /* + * Populate lttng_event structure before event + * registration. + */ + smp_wmb(); + ret = lttng_ftrace_register(event_name, event_param->u.ftrace.symbol_name, event); if (ret) { @@ -493,7 +614,9 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan, WARN_ON_ONCE(!ret); break; case LTTNG_KERNEL_NOOP: - event->desc = internal_desc; + event->enabled = 1; + event->registered = 0; + event->desc = event_desc; if (!event->desc) { ret = -EINVAL; goto register_error; @@ -509,6 +632,7 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan, if (ret) { goto statedump_error; } + hlist_add_head(&event->hlist, head); list_add(&event->list, &chan->session->events); mutex_unlock(&sessions_mutex); return event; @@ -519,11 +643,43 @@ register_error: kmem_cache_free(event_cache, event); cache_error: exist: +type_error: full: - mutex_unlock(&sessions_mutex); return ERR_PTR(ret); } +struct lttng_event *lttng_event_create(struct lttng_channel *chan, + struct lttng_kernel_event *event_param, + void *filter, + const struct lttng_event_desc *event_desc, + enum lttng_kernel_instrumentation itype) +{ + struct lttng_event *event; + + mutex_lock(&sessions_mutex); + event = _lttng_event_create(chan, event_param, filter, event_desc, + itype); + mutex_unlock(&sessions_mutex); + return event; +} + +/* Only used for tracepoints for now. */ +static +void register_event(struct lttng_event *event) +{ + const struct lttng_event_desc *desc; + int ret; + + WARN_ON_ONCE(event->instrumentation != LTTNG_KERNEL_TRACEPOINT); + if (event->registered) + return; + desc = event->desc; + ret = lttng_wrapper_tracepoint_probe_register(desc->kname, + desc->probe_callback, event); + if (!ret) + event->registered = 1; +} + /* * Only used internally at session destruction. */ @@ -531,6 +687,9 @@ int _lttng_event_unregister(struct lttng_event *event) { int ret = -EINVAL; + if (!event->registered) + return 0; + switch (event->instrumentation) { case LTTNG_KERNEL_TRACEPOINT: ret = lttng_wrapper_tracepoint_probe_unregister(event->desc->kname, @@ -557,6 +716,8 @@ int _lttng_event_unregister(struct lttng_event *event) default: WARN_ON_ONCE(1); } + if (!ret) + event->registered = 0; return ret; } @@ -821,6 +982,314 @@ fd_error: return ret; } +/* + * Enabler management. + */ +static +int lttng_desc_match_wildcard_enabler(const struct lttng_event_desc *desc, + struct lttng_enabler *enabler) +{ + WARN_ON_ONCE(enabler->type != LTTNG_ENABLER_WILDCARD); + /* Compare excluding final '*' */ + if (strncmp(desc->name, enabler->event_param.name, + strlen(enabler->event_param.name) - 1)) + return 0; + return 1; +} + +static +int lttng_desc_match_name_enabler(const struct lttng_event_desc *desc, + struct lttng_enabler *enabler) +{ + WARN_ON_ONCE(enabler->type != LTTNG_ENABLER_NAME); + if (strcmp(desc->name, enabler->event_param.name)) + return 0; + return 1; +} + +static +int lttng_desc_match_enabler(const struct lttng_event_desc *desc, + struct lttng_enabler *enabler) +{ + switch (enabler->type) { + case LTTNG_ENABLER_WILDCARD: + return lttng_desc_match_wildcard_enabler(desc, enabler); + case LTTNG_ENABLER_NAME: + return lttng_desc_match_name_enabler(desc, enabler); + default: + return -EINVAL; + } +} + +static +int lttng_event_match_enabler(struct lttng_event *event, + struct lttng_enabler *enabler) +{ + if (lttng_desc_match_enabler(event->desc, enabler) + && event->chan == enabler->chan) + return 1; + else + return 0; +} + +static +struct lttng_enabler_ref *lttng_event_enabler_ref(struct lttng_event *event, + struct lttng_enabler *enabler) +{ + struct lttng_enabler_ref *enabler_ref; + + list_for_each_entry(enabler_ref, + &event->enablers_ref_head, node) { + if (enabler_ref->ref == enabler) + return enabler_ref; + } + return NULL; +} + +/* + * Create struct lttng_event if it is missing and present in the list of + * tracepoint probes. + * Should be called with sessions mutex held. + */ +static +void lttng_create_event_if_missing(struct lttng_enabler *enabler) +{ + struct lttng_session *session = enabler->chan->session; + struct lttng_probe_desc *probe_desc; + const struct lttng_event_desc *desc; + int i; + struct list_head *probe_list; + + probe_list = lttng_get_probe_list_head(); + /* + * For each probe event, if we find that a probe event matches + * our enabler, create an associated lttng_event if not + * already present. + */ + list_for_each_entry(probe_desc, probe_list, head) { + for (i = 0; i < probe_desc->nr_events; i++) { + int found = 0; + struct hlist_head *head; + const char *event_name; + size_t name_len; + uint32_t hash; + struct lttng_event *event; + + desc = probe_desc->event_desc[i]; + if (!lttng_desc_match_enabler(desc, enabler)) + continue; + event_name = desc->name; + name_len = strlen(event_name); + + /* + * Check if already created. + */ + hash = jhash(event_name, name_len, 0); + head = &session->events_ht.table[hash & (LTTNG_EVENT_HT_SIZE - 1)]; + hlist_for_each_entry(event, head, hlist) { + if (event->desc == desc + && event->chan == enabler->chan) + found = 1; + } + if (found) + continue; + + /* + * We need to create an event for this + * event probe. + */ + event = _lttng_event_create(enabler->chan, + NULL, NULL, desc, + LTTNG_KERNEL_TRACEPOINT); + if (!event) { + printk(KERN_INFO "Unable to create event %s\n", + probe_desc->event_desc[i]->name); + } + } + } +} + +/* + * Create events associated with an enabler (if not already present), + * and add backward reference from the event to the enabler. + * Should be called with sessions mutex held. + */ +static +int lttng_enabler_ref_events(struct lttng_enabler *enabler) +{ + struct lttng_session *session = enabler->chan->session; + struct lttng_event *event; + + /* First ensure that probe events are created for this enabler. */ + lttng_create_event_if_missing(enabler); + + /* For each event matching enabler in session event list. */ + list_for_each_entry(event, &session->events, list) { + struct lttng_enabler_ref *enabler_ref; + + if (!lttng_event_match_enabler(event, enabler)) + continue; + enabler_ref = lttng_event_enabler_ref(event, enabler); + if (!enabler_ref) { + /* + * If no backward ref, create it. + * Add backward ref from event to enabler. + */ + enabler_ref = kzalloc(sizeof(*enabler_ref), GFP_KERNEL); + if (!enabler_ref) + return -ENOMEM; + enabler_ref->ref = enabler; + list_add(&enabler_ref->node, + &event->enablers_ref_head); + } + + /* TODO: merge event context. */ + } + return 0; +} + +/* + * Called at module load: connect the probe on all enablers matching + * this event. + * Called with sessions lock held. + */ +int lttng_fix_pending_events(void) +{ + struct lttng_session *session; + + list_for_each_entry(session, &sessions, list) + lttng_session_lazy_sync_enablers(session); + return 0; +} + +struct lttng_enabler *lttng_enabler_create(enum lttng_enabler_type type, + struct lttng_kernel_event *event_param, + struct lttng_channel *chan) +{ + struct lttng_enabler *enabler; + + enabler = kzalloc(sizeof(*enabler), GFP_KERNEL); + if (!enabler) + return NULL; + enabler->type = type; + memcpy(&enabler->event_param, event_param, + sizeof(enabler->event_param)); + enabler->chan = chan; + /* ctx left NULL */ + enabler->enabled = 1; + enabler->evtype = LTTNG_TYPE_ENABLER; + mutex_lock(&sessions_mutex); + list_add(&enabler->node, &enabler->chan->session->enablers_head); + lttng_session_lazy_sync_enablers(enabler->chan->session); + mutex_unlock(&sessions_mutex); + return enabler; +} + +int lttng_enabler_enable(struct lttng_enabler *enabler) +{ + mutex_lock(&sessions_mutex); + enabler->enabled = 1; + lttng_session_lazy_sync_enablers(enabler->chan->session); + mutex_unlock(&sessions_mutex); + return 0; +} + +int lttng_enabler_disable(struct lttng_enabler *enabler) +{ + mutex_lock(&sessions_mutex); + enabler->enabled = 0; + lttng_session_lazy_sync_enablers(enabler->chan->session); + mutex_unlock(&sessions_mutex); + return 0; +} + +int lttng_enabler_attach_context(struct lttng_enabler *enabler, + struct lttng_kernel_context *context_param) +{ + return -ENOSYS; +} + +static +void lttng_enabler_destroy(struct lttng_enabler *enabler) +{ + /* Destroy contexts */ + lttng_destroy_context(enabler->ctx); + + list_del(&enabler->node); + kfree(enabler); +} + +/* + * lttng_session_sync_enablers should be called just before starting a + * session. + * Should be called with sessions mutex held. + */ +static +void lttng_session_sync_enablers(struct lttng_session *session) +{ + struct lttng_enabler *enabler; + struct lttng_event *event; + + list_for_each_entry(enabler, &session->enablers_head, node) + lttng_enabler_ref_events(enabler); + /* + * For each event, if at least one of its enablers is enabled, + * and its channel and session transient states are enabled, we + * enable the event, else we disable it. + */ + list_for_each_entry(event, &session->events, list) { + struct lttng_enabler_ref *enabler_ref; + int enabled = 0; + + if (event->instrumentation == LTTNG_KERNEL_TRACEPOINT) { + /* Enable events */ + list_for_each_entry(enabler_ref, + &event->enablers_ref_head, node) { + if (enabler_ref->ref->enabled) { + enabled = 1; + break; + } + } + } else { + /* Not handled with lazy sync. */ + continue; + } + /* + * Enabled state is based on union of enablers, with + * intesection of session and channel transient enable + * states. + */ + enabled = enabled && session->tstate && event->chan->tstate; + + ACCESS_ONCE(event->enabled) = enabled; + /* + * Sync tracepoint registration with event enabled + * state. + */ + if (enabled) { + register_event(event); + } else { + _lttng_event_unregister(event); + } + } +} + +/* + * Apply enablers to session events, adding events to session if need + * be. It is required after each modification applied to an active + * session, and right before session "start". + * "lazy" sync means we only sync if required. + * Should be called with sessions mutex held. + */ +static +void lttng_session_lazy_sync_enablers(struct lttng_session *session) +{ + /* We can skip if session is not active */ + if (!session->active) + return; + lttng_session_sync_enablers(session); +} + /* * Serialize at most one packet worth of metadata into a metadata * channel.