X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=lttng-events.c;h=73cb361081c5f93b21eae258d3279057a7cc0378;hb=refs%2Fheads%2Fsow-2019-0002-rev1;hp=566080a3a4c1be92ea01e9e6b2af497fd040d007;hpb=41f229dc03b585cc3f434e1db1a5a68101c5da9a;p=deliverable%2Flttng-modules.git diff --git a/lttng-events.c b/lttng-events.c index 566080a3..73cb3610 100644 --- a/lttng-events.c +++ b/lttng-events.c @@ -24,10 +24,10 @@ #include #include #include -#include #include #include #include +#include #include /* for wrapper_vmalloc_sync_all() */ #include @@ -40,26 +40,33 @@ #include #include #include +#include #include #include #define METADATA_CACHE_DEFAULT_SIZE 4096 static LIST_HEAD(sessions); +static LIST_HEAD(trigger_groups); static LIST_HEAD(lttng_transport_list); /* * Protect the sessions and metadata caches. */ static DEFINE_MUTEX(sessions_mutex); static struct kmem_cache *event_cache; +static struct kmem_cache *trigger_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_session_lazy_sync_event_enablers(struct lttng_session *session); +static void lttng_session_sync_event_enablers(struct lttng_session *session); +static void lttng_event_enabler_destroy(struct lttng_event_enabler *event_enabler); +static void lttng_trigger_enabler_destroy(struct lttng_trigger_enabler *trigger_enabler); +static void lttng_trigger_group_sync_enablers(struct lttng_trigger_group *trigger_group); static void _lttng_event_destroy(struct lttng_event *event); +static void _lttng_trigger_destroy(struct lttng_trigger *trigger); static void _lttng_channel_destroy(struct lttng_channel *chan); static int _lttng_event_unregister(struct lttng_event *event); +static int _lttng_trigger_unregister(struct lttng_trigger *trigger); static int _lttng_event_metadata_statedump(struct lttng_session *session, struct lttng_channel *chan, @@ -75,7 +82,12 @@ int _lttng_field_statedump(struct lttng_session *session, void synchronize_trace(void) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,1,0)) + synchronize_rcu(); +#else synchronize_sched(); +#endif + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) #ifdef CONFIG_PREEMPT_RT_FULL synchronize_rcu(); @@ -97,6 +109,17 @@ void lttng_unlock_sessions(void) mutex_unlock(&sessions_mutex); } +static struct lttng_transport *lttng_transport_find(const char *name) +{ + struct lttng_transport *transport; + + list_for_each_entry(transport, <tng_transport_list, node) { + if (!strcmp(transport->name, name)) + return transport; + } + return NULL; +} + /* * Called with sessions lock held. */ @@ -143,6 +166,18 @@ struct lttng_session *lttng_session_create(void) for (i = 0; i < LTTNG_EVENT_HT_SIZE; i++) INIT_HLIST_HEAD(&session->events_ht.table[i]); list_add(&session->list, &sessions); + session->pid_tracker.session = session; + session->pid_tracker.tracker_type = TRACKER_PID; + session->vpid_tracker.session = session; + session->vpid_tracker.tracker_type = TRACKER_VPID; + session->uid_tracker.session = session; + session->uid_tracker.tracker_type = TRACKER_UID; + session->vuid_tracker.session = session; + session->vuid_tracker.tracker_type = TRACKER_VUID; + session->gid_tracker.session = session; + session->gid_tracker.tracker_type = TRACKER_GID; + session->vgid_tracker.session = session; + session->vgid_tracker.tracker_type = TRACKER_VGID; mutex_unlock(&sessions_mutex); return session; @@ -155,6 +190,63 @@ err: return NULL; } +struct lttng_trigger_group *lttng_trigger_group_create(void) +{ + struct lttng_transport *transport = NULL; + struct lttng_trigger_group *trigger_group; + const char *transport_name = "relay-trigger"; + size_t subbuf_size = 4096; //TODO + size_t num_subbuf = 16; //TODO + unsigned int switch_timer_interval = 0; + unsigned int read_timer_interval = 0; + int i; + + mutex_lock(&sessions_mutex); + + transport = lttng_transport_find(transport_name); + if (!transport) { + printk(KERN_WARNING "LTTng transport %s not found\n", + transport_name); + goto notransport; + } + if (!try_module_get(transport->owner)) { + printk(KERN_WARNING "LTT : Can't lock transport module.\n"); + goto notransport; + } + + trigger_group = lttng_kvzalloc(sizeof(struct lttng_trigger_group), + GFP_KERNEL); + if (!trigger_group) + goto nomem; + + trigger_group->ops = &transport->ops; + trigger_group->chan = transport->ops.channel_create(transport_name, + trigger_group, NULL, subbuf_size, num_subbuf, + switch_timer_interval, read_timer_interval); + if (!trigger_group->chan) + goto create_error; + + trigger_group->transport = transport; + INIT_LIST_HEAD(&trigger_group->enablers_head); + INIT_LIST_HEAD(&trigger_group->triggers_head); + for (i = 0; i < LTTNG_TRIGGER_HT_SIZE; i++) + INIT_HLIST_HEAD(&trigger_group->triggers_ht.table[i]); + + list_add(&trigger_group->node, &trigger_groups); + mutex_unlock(&sessions_mutex); + + return trigger_group; + +create_error: + lttng_kvfree(trigger_group); +nomem: + if (transport) + module_put(transport->owner); +notransport: + mutex_unlock(&sessions_mutex); + return NULL; +} + void metadata_cache_destroy(struct kref *kref) { struct lttng_metadata_cache *cache = @@ -168,13 +260,13 @@ 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; + struct lttng_event_enabler *event_enabler, *tmp_event_enabler; int ret; mutex_lock(&sessions_mutex); WRITE_ONCE(session->active, 0); list_for_each_entry(chan, &session->chan, list) { - ret = lttng_syscalls_unregister(chan); + ret = lttng_syscalls_unregister_event(chan); WARN_ON(ret); } list_for_each_entry(event, &session->events, list) { @@ -182,9 +274,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, + list_for_each_entry_safe(event_enabler, tmp_event_enabler, &session->enablers_head, node) - lttng_enabler_destroy(enabler); + lttng_event_enabler_destroy(event_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) { @@ -193,14 +285,57 @@ void lttng_session_destroy(struct lttng_session *session) } list_for_each_entry(metadata_stream, &session->metadata_cache->metadata_stream, list) _lttng_metadata_channel_hangup(metadata_stream); - if (session->pid_tracker) - lttng_pid_tracker_destroy(session->pid_tracker); + lttng_id_tracker_destroy(&session->pid_tracker, false); + lttng_id_tracker_destroy(&session->vpid_tracker, false); + lttng_id_tracker_destroy(&session->uid_tracker, false); + lttng_id_tracker_destroy(&session->vuid_tracker, false); + lttng_id_tracker_destroy(&session->gid_tracker, false); + lttng_id_tracker_destroy(&session->vgid_tracker, false); kref_put(&session->metadata_cache->refcount, metadata_cache_destroy); list_del(&session->list); mutex_unlock(&sessions_mutex); lttng_kvfree(session); } +void lttng_trigger_group_destroy(struct lttng_trigger_group *trigger_group) +{ + struct lttng_trigger_enabler *trigger_enabler, *tmp_trigger_enabler; + struct lttng_trigger *trigger, *tmptrigger; + int ret; + + if (!trigger_group) + return; + + mutex_lock(&sessions_mutex); + + ret = lttng_syscalls_unregister_trigger(trigger_group); + WARN_ON(ret); + + list_for_each_entry_safe(trigger, tmptrigger, + &trigger_group->triggers_head, list) { + ret = _lttng_trigger_unregister(trigger); + WARN_ON(ret); + } + + synchronize_trace(); /* Wait for in-flight triggers to complete */ + + irq_work_sync(&trigger_group->wakeup_pending); + + list_for_each_entry_safe(trigger_enabler, tmp_trigger_enabler, + &trigger_group->enablers_head, node) + lttng_trigger_enabler_destroy(trigger_enabler); + + list_for_each_entry_safe(trigger, tmptrigger, + &trigger_group->triggers_head, list) + _lttng_trigger_destroy(trigger); + + trigger_group->ops->channel_destroy(trigger_group->chan); + module_put(trigger_group->transport->owner); + list_del(&trigger_group->node); + mutex_unlock(&sessions_mutex); + lttng_kvfree(trigger_group); +} + int lttng_session_statedump(struct lttng_session *session) { int ret; @@ -226,7 +361,7 @@ int lttng_session_enable(struct lttng_session *session) session->tstate = 1; /* We need to sync enablers with session before activation. */ - lttng_session_sync_enablers(session); + lttng_session_sync_event_enablers(session); /* * Snapshot the number of events per channel to know the type of header @@ -276,7 +411,7 @@ int lttng_session_disable(struct lttng_session *session) /* Set transient enabler state to "disabled" */ session->tstate = 0; - lttng_session_sync_enablers(session); + lttng_session_sync_event_enablers(session); /* Set each stream's quiescent state. */ list_for_each_entry(chan, &session->chan, list) { @@ -343,7 +478,7 @@ int lttng_channel_enable(struct lttng_channel *channel) } /* Set transient enabler state to "enabled" */ channel->tstate = 1; - lttng_session_sync_enablers(channel->session); + lttng_session_sync_event_enablers(channel->session); /* Set atomically the state to "enabled" */ WRITE_ONCE(channel->enabled, 1); end: @@ -368,7 +503,7 @@ int lttng_channel_disable(struct lttng_channel *channel) WRITE_ONCE(channel->enabled, 0); /* Set transient enabler state to "enabled" */ channel->tstate = 0; - lttng_session_sync_enablers(channel->session); + lttng_session_sync_event_enablers(channel->session); end: mutex_unlock(&sessions_mutex); return ret; @@ -446,15 +581,64 @@ end: return ret; } -static struct lttng_transport *lttng_transport_find(const char *name) +int lttng_trigger_enable(struct lttng_trigger *trigger) { - struct lttng_transport *transport; + int ret = 0; - list_for_each_entry(transport, <tng_transport_list, node) { - if (!strcmp(transport->name, name)) - return transport; + mutex_lock(&sessions_mutex); + if (trigger->enabled) { + ret = -EEXIST; + goto end; } - return NULL; + switch (trigger->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_SYSCALL: + ret = -EINVAL; + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: + WRITE_ONCE(trigger->enabled, 1); + break; + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_KRETPROBE: + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + } +end: + mutex_unlock(&sessions_mutex); + return ret; +} + +int lttng_trigger_disable(struct lttng_trigger *trigger) +{ + int ret = 0; + + mutex_lock(&sessions_mutex); + if (!trigger->enabled) { + ret = -EEXIST; + goto end; + } + switch (trigger->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_SYSCALL: + ret = -EINVAL; + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: + WRITE_ONCE(trigger->enabled, 0); + break; + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_KRETPROBE: + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + } +end: + mutex_unlock(&sessions_mutex); + return ret; } struct lttng_channel *lttng_channel_create(struct lttng_session *session, @@ -563,8 +747,6 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, struct lttng_event *event; const char *event_name; struct hlist_head *head; - size_t name_len; - uint32_t hash; int ret; if (chan->free_event_id == -1U) { @@ -589,9 +771,9 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, 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)]; + + head = utils_borrow_hash_table_bucket(session->events_ht.table, + LTTNG_EVENT_HT_SIZE, event_name); lttng_hlist_for_each_entry(event, head, hlist) { WARN_ON_ONCE(!event->desc); if (!strncmp(event->desc->name, event_name, @@ -620,7 +802,7 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, /* Event will be enabled by enabler sync. */ event->enabled = 0; event->registered = 0; - event->desc = lttng_event_get(event_name); + event->desc = lttng_event_desc_get(event_name); if (!event->desc) { ret = -ENOENT; goto register_error; @@ -640,7 +822,7 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, * registration. */ smp_wmb(); - ret = lttng_kprobes_register(event_name, + ret = lttng_kprobes_register_event(event_name, event_param->u.kprobe.symbol_name, event_param->u.kprobe.offset, event_param->u.kprobe.addr, @@ -755,7 +937,7 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, */ smp_wmb(); - ret = lttng_uprobes_register(event_param->name, + ret = lttng_uprobes_register_event(event_param->name, event_param->u.uprobe.fd, event); if (ret) @@ -788,6 +970,179 @@ full: return ERR_PTR(ret); } +static +void lttng_trigger_send_notification(struct lttng_trigger *trigger) +{ + struct lttng_trigger_group *trigger_group = trigger->group; + struct lib_ring_buffer_ctx ctx; + int ret; + + if (unlikely(!READ_ONCE(trigger->enabled))) + return; + + lib_ring_buffer_ctx_init(&ctx, trigger_group->chan, NULL, sizeof(trigger->id), + lttng_alignof(trigger->id), -1); + ret = trigger_group->ops->event_reserve(&ctx, 0); + if (ret < 0) { + //TODO: error handling with counter maps + //silently drop for now. WARN_ON_ONCE(1); + return; + } + lib_ring_buffer_align_ctx(&ctx, lttng_alignof(trigger->id)); + trigger_group->ops->event_write(&ctx, &trigger->id, sizeof(trigger->id)); + trigger_group->ops->event_commit(&ctx); + irq_work_queue(&trigger_group->wakeup_pending); +} + +struct lttng_trigger *_lttng_trigger_create( + const struct lttng_event_desc *event_desc, + uint64_t id, struct lttng_trigger_group *trigger_group, + struct lttng_kernel_trigger *trigger_param, void *filter, + enum lttng_kernel_instrumentation itype) +{ + struct lttng_trigger *trigger; + const char *event_name; + struct hlist_head *head; + int ret; + + switch (itype) { + case LTTNG_KERNEL_TRACEPOINT: + event_name = event_desc->name; + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: + case LTTNG_KERNEL_SYSCALL: + event_name = trigger_param->name; + break; + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + goto type_error; + } + + head = utils_borrow_hash_table_bucket(trigger_group->triggers_ht.table, + LTTNG_TRIGGER_HT_SIZE, event_name); + lttng_hlist_for_each_entry(trigger, head, hlist) { + WARN_ON_ONCE(!trigger->desc); + if (!strncmp(trigger->desc->name, event_name, + LTTNG_KERNEL_SYM_NAME_LEN - 1) + && trigger_group == trigger->group + && id == trigger->id) { + ret = -EEXIST; + goto exist; + } + } + + trigger = kmem_cache_zalloc(trigger_cache, GFP_KERNEL); + if (!trigger) { + ret = -ENOMEM; + goto cache_error; + } + trigger->group = trigger_group; + trigger->id = id; + trigger->filter = filter; + trigger->instrumentation = itype; + trigger->evtype = LTTNG_TYPE_EVENT; + trigger->send_notification = lttng_trigger_send_notification; + INIT_LIST_HEAD(&trigger->bytecode_runtime_head); + INIT_LIST_HEAD(&trigger->enablers_ref_head); + + switch (itype) { + case LTTNG_KERNEL_TRACEPOINT: + /* Event will be enabled by enabler sync. */ + trigger->enabled = 0; + trigger->registered = 0; + trigger->desc = lttng_event_desc_get(event_name); + if (!trigger->desc) { + ret = -ENOENT; + goto register_error; + } + /* Populate lttng_trigger structure before event registration. */ + smp_wmb(); + break; + case LTTNG_KERNEL_KPROBE: + /* + * Needs to be explicitly enabled after creation, since + * we may want to apply filters. + */ + trigger->enabled = 0; + trigger->registered = 1; + /* + * Populate lttng_trigger structure before event + * registration. + */ + smp_wmb(); + ret = lttng_kprobes_register_trigger( + trigger_param->u.kprobe.symbol_name, + trigger_param->u.kprobe.offset, + trigger_param->u.kprobe.addr, + trigger); + if (ret) { + ret = -EINVAL; + goto register_error; + } + ret = try_module_get(trigger->desc->owner); + WARN_ON_ONCE(!ret); + break; + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_SYSCALL: + /* + * Needs to be explicitly enabled after creation, since + * we may want to apply filters. + */ + trigger->enabled = 0; + trigger->registered = 0; + trigger->desc = event_desc; + if (!trigger->desc) { + ret = -EINVAL; + goto register_error; + } + break; + case LTTNG_KERNEL_UPROBE: + /* + * Needs to be explicitly enabled after creation, since + * we may want to apply filters. + */ + trigger->enabled = 0; + trigger->registered = 1; + + /* + * Populate lttng_trigger structure before trigger + * registration. + */ + smp_wmb(); + + ret = lttng_uprobes_register_trigger(trigger_param->name, + trigger_param->u.uprobe.fd, + trigger); + if (ret) + goto register_error; + ret = try_module_get(trigger->desc->owner); + WARN_ON_ONCE(!ret); + break; + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + goto register_error; + } + + list_add(&trigger->list, &trigger_group->triggers_head); + hlist_add_head(&trigger->hlist, head); + return trigger; + +register_error: + kmem_cache_free(trigger_cache, trigger); +cache_error: +exist: +type_error: + return ERR_PTR(ret); +} + struct lttng_event *lttng_event_create(struct lttng_channel *chan, struct lttng_kernel_event *event_param, void *filter, @@ -803,6 +1158,21 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan, return event; } +struct lttng_trigger *lttng_trigger_create( + const struct lttng_event_desc *event_desc, + uint64_t id, struct lttng_trigger_group *trigger_group, + struct lttng_kernel_trigger *trigger_param, void *filter, + enum lttng_kernel_instrumentation itype) +{ + struct lttng_trigger *trigger; + + mutex_lock(&sessions_mutex); + trigger = _lttng_trigger_create(event_desc, id, trigger_group, + trigger_param, filter, itype); + mutex_unlock(&sessions_mutex); + return trigger; +} + /* Only used for tracepoints for now. */ static void register_event(struct lttng_event *event) @@ -821,7 +1191,7 @@ void register_event(struct lttng_event *event) event); break; case LTTNG_KERNEL_SYSCALL: - ret = lttng_syscall_filter_enable(event->chan, + ret = lttng_syscall_filter_enable_event(event->chan, desc->name); break; case LTTNG_KERNEL_KPROBE: @@ -857,7 +1227,7 @@ int _lttng_event_unregister(struct lttng_event *event) event); break; case LTTNG_KERNEL_KPROBE: - lttng_kprobes_unregister(event); + lttng_kprobes_unregister_event(event); ret = 0; break; case LTTNG_KERNEL_KRETPROBE: @@ -869,14 +1239,14 @@ int _lttng_event_unregister(struct lttng_event *event) ret = 0; break; case LTTNG_KERNEL_SYSCALL: - ret = lttng_syscall_filter_disable(event->chan, + ret = lttng_syscall_filter_disable_event(event->chan, desc->name); break; case LTTNG_KERNEL_NOOP: ret = 0; break; case LTTNG_KERNEL_UPROBE: - lttng_uprobes_unregister(event); + lttng_uprobes_unregister_event(event); ret = 0; break; default: @@ -887,6 +1257,78 @@ int _lttng_event_unregister(struct lttng_event *event) return ret; } +/* Only used for tracepoints for now. */ +static +void register_trigger(struct lttng_trigger *trigger) +{ + const struct lttng_event_desc *desc; + int ret = -EINVAL; + + if (trigger->registered) + return; + + desc = trigger->desc; + switch (trigger->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + ret = lttng_wrapper_tracepoint_probe_register(desc->kname, + desc->trigger_callback, + trigger); + break; + case LTTNG_KERNEL_SYSCALL: + ret = lttng_syscall_filter_enable_trigger(trigger); + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: + ret = 0; + break; + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + default: + WARN_ON_ONCE(1); + } + if (!ret) + trigger->registered = 1; +} + +static +int _lttng_trigger_unregister(struct lttng_trigger *trigger) +{ + const struct lttng_event_desc *desc; + int ret = -EINVAL; + + if (!trigger->registered) + return 0; + + desc = trigger->desc; + switch (trigger->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + ret = lttng_wrapper_tracepoint_probe_unregister(trigger->desc->kname, + trigger->desc->trigger_callback, + trigger); + break; + case LTTNG_KERNEL_KPROBE: + lttng_kprobes_unregister_trigger(trigger); + ret = 0; + break; + case LTTNG_KERNEL_UPROBE: + lttng_uprobes_unregister_trigger(trigger); + ret = 0; + break; + case LTTNG_KERNEL_SYSCALL: + ret = lttng_syscall_filter_disable_trigger(trigger); + break; + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_NOOP: + default: + WARN_ON_ONCE(1); + } + if (!ret) + trigger->registered = 0; + return ret; +} + /* * Only used internally at session destruction. */ @@ -895,11 +1337,11 @@ void _lttng_event_destroy(struct lttng_event *event) { switch (event->instrumentation) { case LTTNG_KERNEL_TRACEPOINT: - lttng_event_put(event->desc); + lttng_event_desc_put(event->desc); break; case LTTNG_KERNEL_KPROBE: module_put(event->desc->owner); - lttng_kprobes_destroy_private(event); + lttng_kprobes_destroy_event_private(event); break; case LTTNG_KERNEL_KRETPROBE: module_put(event->desc->owner); @@ -914,7 +1356,7 @@ void _lttng_event_destroy(struct lttng_event *event) break; case LTTNG_KERNEL_UPROBE: module_put(event->desc->owner); - lttng_uprobes_destroy_private(event); + lttng_uprobes_destroy_event_private(event); break; default: WARN_ON_ONCE(1); @@ -924,91 +1366,115 @@ void _lttng_event_destroy(struct lttng_event *event) kmem_cache_free(event_cache, event); } -int lttng_session_track_pid(struct lttng_session *session, int pid) +/* + * Only used internally at session destruction. + */ +static +void _lttng_trigger_destroy(struct lttng_trigger *trigger) +{ + switch (trigger->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + lttng_event_desc_put(trigger->desc); + break; + case LTTNG_KERNEL_KPROBE: + module_put(trigger->desc->owner); + lttng_kprobes_destroy_trigger_private(trigger); + break; + case LTTNG_KERNEL_NOOP: + case LTTNG_KERNEL_SYSCALL: + break; + case LTTNG_KERNEL_UPROBE: + module_put(trigger->desc->owner); + lttng_uprobes_destroy_trigger_private(trigger); + break; + case LTTNG_KERNEL_KRETPROBE: + case LTTNG_KERNEL_FUNCTION: + default: + WARN_ON_ONCE(1); + } + list_del(&trigger->list); + kmem_cache_free(trigger_cache, trigger); +} + +struct lttng_id_tracker *get_tracker(struct lttng_session *session, + enum tracker_type tracker_type) +{ + switch (tracker_type) { + case TRACKER_PID: + return &session->pid_tracker; + case TRACKER_VPID: + return &session->vpid_tracker; + case TRACKER_UID: + return &session->uid_tracker; + case TRACKER_VUID: + return &session->vuid_tracker; + case TRACKER_GID: + return &session->gid_tracker; + case TRACKER_VGID: + return &session->vgid_tracker; + default: + WARN_ON_ONCE(1); + return NULL; + } +} + +int lttng_session_track_id(struct lttng_session *session, + enum tracker_type tracker_type, int id) { + struct lttng_id_tracker *tracker; int ret; - if (pid < -1) + tracker = get_tracker(session, tracker_type); + if (!tracker) + return -EINVAL; + if (id < -1) return -EINVAL; mutex_lock(&sessions_mutex); - if (pid == -1) { - /* track all pids: destroy tracker. */ - if (session->pid_tracker) { - struct lttng_pid_tracker *lpf; - - lpf = session->pid_tracker; - rcu_assign_pointer(session->pid_tracker, NULL); - synchronize_trace(); - lttng_pid_tracker_destroy(lpf); - } + if (id == -1) { + /* track all ids: destroy tracker. */ + lttng_id_tracker_destroy(tracker, true); ret = 0; } else { - if (!session->pid_tracker) { - struct lttng_pid_tracker *lpf; - - lpf = lttng_pid_tracker_create(); - if (!lpf) { - ret = -ENOMEM; - goto unlock; - } - ret = lttng_pid_tracker_add(lpf, pid); - rcu_assign_pointer(session->pid_tracker, lpf); - } else { - ret = lttng_pid_tracker_add(session->pid_tracker, pid); - } + ret = lttng_id_tracker_add(tracker, id); } -unlock: mutex_unlock(&sessions_mutex); return ret; } -int lttng_session_untrack_pid(struct lttng_session *session, int pid) +int lttng_session_untrack_id(struct lttng_session *session, + enum tracker_type tracker_type, int id) { + struct lttng_id_tracker *tracker; int ret; - if (pid < -1) + tracker = get_tracker(session, tracker_type); + if (!tracker) + return -EINVAL; + if (id < -1) return -EINVAL; mutex_lock(&sessions_mutex); - if (pid == -1) { - /* untrack all pids: replace by empty tracker. */ - struct lttng_pid_tracker *old_lpf = session->pid_tracker; - struct lttng_pid_tracker *lpf; - - lpf = lttng_pid_tracker_create(); - if (!lpf) { - ret = -ENOMEM; - goto unlock; - } - rcu_assign_pointer(session->pid_tracker, lpf); - synchronize_trace(); - if (old_lpf) - lttng_pid_tracker_destroy(old_lpf); - ret = 0; + if (id == -1) { + /* untrack all ids: replace by empty tracker. */ + ret = lttng_id_tracker_empty_set(tracker); } else { - if (!session->pid_tracker) { - ret = -ENOENT; - goto unlock; - } - ret = lttng_pid_tracker_del(session->pid_tracker, pid); + ret = lttng_id_tracker_del(tracker, id); } -unlock: mutex_unlock(&sessions_mutex); return ret; } static -void *pid_list_start(struct seq_file *m, loff_t *pos) +void *id_list_start(struct seq_file *m, loff_t *pos) { - struct lttng_session *session = m->private; - struct lttng_pid_tracker *lpf; - struct lttng_pid_hash_node *e; + struct lttng_id_tracker *id_tracker = m->private; + struct lttng_id_tracker_rcu *id_tracker_p = id_tracker->p; + struct lttng_id_hash_node *e; int iter = 0, i; mutex_lock(&sessions_mutex); - lpf = session->pid_tracker; - if (lpf) { - for (i = 0; i < LTTNG_PID_TABLE_SIZE; i++) { - struct hlist_head *head = &lpf->pid_hash[i]; + if (id_tracker_p) { + for (i = 0; i < LTTNG_ID_TABLE_SIZE; i++) { + struct hlist_head *head = &id_tracker_p->id_hash[i]; lttng_hlist_for_each_entry(e, head, hlist) { if (iter++ >= *pos) @@ -1016,9 +1482,9 @@ void *pid_list_start(struct seq_file *m, loff_t *pos) } } } else { - /* PID tracker disabled. */ + /* ID tracker disabled. */ if (iter >= *pos && iter == 0) { - return session; /* empty tracker */ + return id_tracker_p; /* empty tracker */ } iter++; } @@ -1028,18 +1494,17 @@ void *pid_list_start(struct seq_file *m, loff_t *pos) /* Called with sessions_mutex held. */ static -void *pid_list_next(struct seq_file *m, void *p, loff_t *ppos) +void *id_list_next(struct seq_file *m, void *p, loff_t *ppos) { - struct lttng_session *session = m->private; - struct lttng_pid_tracker *lpf; - struct lttng_pid_hash_node *e; + struct lttng_id_tracker *id_tracker = m->private; + struct lttng_id_tracker_rcu *id_tracker_p = id_tracker->p; + struct lttng_id_hash_node *e; int iter = 0, i; (*ppos)++; - lpf = session->pid_tracker; - if (lpf) { - for (i = 0; i < LTTNG_PID_TABLE_SIZE; i++) { - struct hlist_head *head = &lpf->pid_hash[i]; + if (id_tracker_p) { + for (i = 0; i < LTTNG_ID_TABLE_SIZE; i++) { + struct hlist_head *head = &id_tracker_p->id_hash[i]; lttng_hlist_for_each_entry(e, head, hlist) { if (iter++ >= *ppos) @@ -1047,9 +1512,9 @@ void *pid_list_next(struct seq_file *m, void *p, loff_t *ppos) } } } else { - /* PID tracker disabled. */ + /* ID tracker disabled. */ if (iter >= *ppos && iter == 0) - return session; /* empty tracker */ + return p; /* empty tracker */ iter++; } @@ -1058,67 +1523,91 @@ void *pid_list_next(struct seq_file *m, void *p, loff_t *ppos) } static -void pid_list_stop(struct seq_file *m, void *p) +void id_list_stop(struct seq_file *m, void *p) { mutex_unlock(&sessions_mutex); } static -int pid_list_show(struct seq_file *m, void *p) +int id_list_show(struct seq_file *m, void *p) { - int pid; + struct lttng_id_tracker *id_tracker = m->private; + struct lttng_id_tracker_rcu *id_tracker_p = id_tracker->p; + int id; - if (p == m->private) { + if (p == id_tracker_p) { /* Tracker disabled. */ - pid = -1; + id = -1; } else { - const struct lttng_pid_hash_node *e = p; + const struct lttng_id_hash_node *e = p; - pid = lttng_pid_tracker_get_node_pid(e); + id = lttng_id_tracker_get_node_id(e); + } + switch (id_tracker->tracker_type) { + case TRACKER_PID: + seq_printf(m, "process { pid = %d; };\n", id); + break; + case TRACKER_VPID: + seq_printf(m, "process { vpid = %d; };\n", id); + break; + case TRACKER_UID: + seq_printf(m, "user { uid = %d; };\n", id); + break; + case TRACKER_VUID: + seq_printf(m, "user { vuid = %d; };\n", id); + break; + case TRACKER_GID: + seq_printf(m, "group { gid = %d; };\n", id); + break; + case TRACKER_VGID: + seq_printf(m, "group { vgid = %d; };\n", id); + break; + default: + seq_printf(m, "UNKNOWN { field = %d };\n", id); } - seq_printf(m, "process { pid = %d; };\n", pid); return 0; } static -const struct seq_operations lttng_tracker_pids_list_seq_ops = { - .start = pid_list_start, - .next = pid_list_next, - .stop = pid_list_stop, - .show = pid_list_show, +const struct seq_operations lttng_tracker_ids_list_seq_ops = { + .start = id_list_start, + .next = id_list_next, + .stop = id_list_stop, + .show = id_list_show, }; static -int lttng_tracker_pids_list_open(struct inode *inode, struct file *file) +int lttng_tracker_ids_list_open(struct inode *inode, struct file *file) { - return seq_open(file, <tng_tracker_pids_list_seq_ops); + return seq_open(file, <tng_tracker_ids_list_seq_ops); } static -int lttng_tracker_pids_list_release(struct inode *inode, struct file *file) +int lttng_tracker_ids_list_release(struct inode *inode, struct file *file) { struct seq_file *m = file->private_data; - struct lttng_session *session = m->private; + struct lttng_id_tracker *id_tracker = m->private; int ret; - WARN_ON_ONCE(!session); + WARN_ON_ONCE(!id_tracker); ret = seq_release(inode, file); - if (!ret && session) - fput(session->file); + if (!ret) + fput(id_tracker->session->file); return ret; } -const struct file_operations lttng_tracker_pids_list_fops = { +const struct file_operations lttng_tracker_ids_list_fops = { .owner = THIS_MODULE, - .open = lttng_tracker_pids_list_open, + .open = lttng_tracker_ids_list_open, .read = seq_read, .llseek = seq_lseek, - .release = lttng_tracker_pids_list_release, + .release = lttng_tracker_ids_list_release, }; -int lttng_session_list_tracker_pids(struct lttng_session *session) +int lttng_session_list_tracker_ids(struct lttng_session *session, + enum tracker_type tracker_type) { - struct file *tracker_pids_list_file; + struct file *tracker_ids_list_file; struct seq_file *m; int file_fd, ret; @@ -1128,30 +1617,32 @@ int lttng_session_list_tracker_pids(struct lttng_session *session) goto fd_error; } - tracker_pids_list_file = anon_inode_getfile("[lttng_tracker_pids_list]", - <tng_tracker_pids_list_fops, + tracker_ids_list_file = anon_inode_getfile("[lttng_tracker_ids_list]", + <tng_tracker_ids_list_fops, NULL, O_RDWR); - if (IS_ERR(tracker_pids_list_file)) { - ret = PTR_ERR(tracker_pids_list_file); + if (IS_ERR(tracker_ids_list_file)) { + ret = PTR_ERR(tracker_ids_list_file); goto file_error; } - if (atomic_long_add_unless(&session->file->f_count, - 1, INT_MAX) == INT_MAX) { + if (!atomic_long_add_unless(&session->file->f_count, 1, LONG_MAX)) { + ret = -EOVERFLOW; goto refcount_error; } - ret = lttng_tracker_pids_list_fops.open(NULL, tracker_pids_list_file); + ret = lttng_tracker_ids_list_fops.open(NULL, tracker_ids_list_file); if (ret < 0) goto open_error; - m = tracker_pids_list_file->private_data; - m->private = session; - fd_install(file_fd, tracker_pids_list_file); + m = tracker_ids_list_file->private_data; + + m->private = get_tracker(session, tracker_type); + BUG_ON(!m->private); + fd_install(file_fd, tracker_ids_list_file); return file_fd; open_error: atomic_long_dec(&session->file->f_count); refcount_error: - fput(tracker_pids_list_file); + fput(tracker_ids_list_file); file_error: put_unused_fd(file_fd); fd_error: @@ -1180,7 +1671,6 @@ int lttng_match_enabler_name(const char *desc_name, return 1; } -static int lttng_desc_match_enabler(const struct lttng_event_desc *desc, struct lttng_enabler *enabler) { @@ -1210,10 +1700,10 @@ int lttng_desc_match_enabler(const struct lttng_event_desc *desc, WARN_ON_ONCE(1); return -EINVAL; } - switch (enabler->type) { - case LTTNG_ENABLER_STAR_GLOB: + switch (enabler->format_type) { + case LTTNG_ENABLER_FORMAT_STAR_GLOB: return lttng_match_enabler_star_glob(desc_name, enabler_name); - case LTTNG_ENABLER_NAME: + case LTTNG_ENABLER_FORMAT_NAME: return lttng_match_enabler_name(desc_name, enabler_name); default: return -EINVAL; @@ -1221,36 +1711,111 @@ int lttng_desc_match_enabler(const struct lttng_event_desc *desc, } static -int lttng_event_match_enabler(struct lttng_event *event, - struct lttng_enabler *enabler) +int lttng_event_enabler_match_event(struct lttng_event_enabler *event_enabler, + struct lttng_event *event) +{ + struct lttng_enabler *base_enabler = lttng_event_enabler_as_enabler( + event_enabler); + + if (base_enabler->event_param.instrumentation != event->instrumentation) + return 0; + if (lttng_desc_match_enabler(event->desc, base_enabler) + && event->chan == event_enabler->chan) + return 1; + else + return 0; +} + +static +int lttng_trigger_enabler_match_trigger(struct lttng_trigger_enabler *trigger_enabler, + struct lttng_trigger *trigger) { - if (enabler->event_param.instrumentation != event->instrumentation) + struct lttng_enabler *base_enabler = lttng_trigger_enabler_as_enabler( + trigger_enabler); + + if (base_enabler->event_param.instrumentation != trigger->instrumentation) return 0; - if (lttng_desc_match_enabler(event->desc, enabler) - && event->chan == enabler->chan) + if (lttng_desc_match_enabler(trigger->desc, base_enabler) + && trigger->group == trigger_enabler->group + && trigger->id == trigger_enabler->id) return 1; else return 0; } static -struct lttng_enabler_ref *lttng_event_enabler_ref(struct lttng_event *event, +struct lttng_enabler_ref *lttng_enabler_ref( + struct list_head *enablers_ref_list, struct lttng_enabler *enabler) { struct lttng_enabler_ref *enabler_ref; - list_for_each_entry(enabler_ref, - &event->enablers_ref_head, node) { + list_for_each_entry(enabler_ref, enablers_ref_list, node) { if (enabler_ref->ref == enabler) return enabler_ref; } - return NULL; + return NULL; +} + +static +void lttng_create_tracepoint_event_if_missing(struct lttng_event_enabler *event_enabler) +{ + struct lttng_session *session = event_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; + struct lttng_event *event; + + desc = probe_desc->event_desc[i]; + if (!lttng_desc_match_enabler(desc, + lttng_event_enabler_as_enabler(event_enabler))) + continue; + + /* + * Check if already created. + */ + head = utils_borrow_hash_table_bucket( + session->events_ht.table, LTTNG_EVENT_HT_SIZE, + desc->name); + lttng_hlist_for_each_entry(event, head, hlist) { + if (event->desc == desc + && event->chan == event_enabler->chan) + found = 1; + } + if (found) + continue; + + /* + * We need to create an event for this + * event probe. + */ + event = _lttng_event_create(event_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); + } + } + } } static -void lttng_create_tracepoint_if_missing(struct lttng_enabler *enabler) +void lttng_create_tracepoint_trigger_if_missing(struct lttng_trigger_enabler *trigger_enabler) { - struct lttng_session *session = enabler->chan->session; + struct lttng_trigger_group *trigger_group = trigger_enabler->group; struct lttng_probe_desc *probe_desc; const struct lttng_event_desc *desc; int i; @@ -1259,46 +1824,42 @@ void lttng_create_tracepoint_if_missing(struct lttng_enabler *enabler) 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 + * our enabler, create an associated lttng_trigger 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; + struct lttng_trigger *trigger; desc = probe_desc->event_desc[i]; - if (!lttng_desc_match_enabler(desc, enabler)) + if (!lttng_desc_match_enabler(desc, + lttng_trigger_enabler_as_enabler(trigger_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)]; - lttng_hlist_for_each_entry(event, head, hlist) { - if (event->desc == desc - && event->chan == enabler->chan) + head = utils_borrow_hash_table_bucket( + trigger_group->triggers_ht.table, + LTTNG_TRIGGER_HT_SIZE, desc->name); + lttng_hlist_for_each_entry(trigger, head, hlist) { + if (trigger->desc == desc + && trigger->id == trigger_enabler->id) found = 1; } if (found) continue; /* - * We need to create an event for this - * event probe. + * We need to create a trigger 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", + trigger = _lttng_trigger_create(desc, + trigger_enabler->id, trigger_group, NULL, NULL, + LTTNG_KERNEL_TRACEPOINT); + if (IS_ERR(trigger)) { + printk(KERN_INFO "Unable to create trigger %s\n", probe_desc->event_desc[i]->name); } } @@ -1306,11 +1867,22 @@ void lttng_create_tracepoint_if_missing(struct lttng_enabler *enabler) } static -void lttng_create_syscall_if_missing(struct lttng_enabler *enabler) +void lttng_create_syscall_event_if_missing(struct lttng_event_enabler *event_enabler) +{ + int ret; + + ret = lttng_syscalls_register_event(event_enabler->chan, NULL); + WARN_ON_ONCE(ret); +} + +static +void lttng_create_syscall_trigger_if_missing(struct lttng_trigger_enabler *trigger_enabler) { int ret; - ret = lttng_syscalls_register(enabler->chan, NULL); + ret = lttng_syscalls_register_trigger(trigger_enabler, NULL); + WARN_ON_ONCE(ret); + ret = lttng_syscals_create_matching_triggers(trigger_enabler, NULL); WARN_ON_ONCE(ret); } @@ -1320,14 +1892,14 @@ void lttng_create_syscall_if_missing(struct lttng_enabler *enabler) * Should be called with sessions mutex held. */ static -void lttng_create_event_if_missing(struct lttng_enabler *enabler) +void lttng_create_event_if_missing(struct lttng_event_enabler *event_enabler) { - switch (enabler->event_param.instrumentation) { + switch (event_enabler->base.event_param.instrumentation) { case LTTNG_KERNEL_TRACEPOINT: - lttng_create_tracepoint_if_missing(enabler); + lttng_create_tracepoint_event_if_missing(event_enabler); break; case LTTNG_KERNEL_SYSCALL: - lttng_create_syscall_if_missing(enabler); + lttng_create_syscall_event_if_missing(event_enabler); break; default: WARN_ON_ONCE(1); @@ -1336,35 +1908,36 @@ void lttng_create_event_if_missing(struct lttng_enabler *enabler) } /* - * Create events associated with an enabler (if not already present), + * Create events associated with an event_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) +int lttng_event_enabler_ref_events(struct lttng_event_enabler *event_enabler) { - struct lttng_session *session = enabler->chan->session; + struct lttng_session *session = event_enabler->chan->session; struct lttng_event *event; /* First ensure that probe events are created for this enabler. */ - lttng_create_event_if_missing(enabler); + lttng_create_event_if_missing(event_enabler); - /* For each event matching enabler in session event list. */ + /* For each event matching event_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)) + if (!lttng_event_enabler_match_event(event_enabler, event)) continue; - enabler_ref = lttng_event_enabler_ref(event, enabler); + enabler_ref = lttng_enabler_ref(&event->enablers_ref_head, + lttng_event_enabler_as_enabler(event_enabler)); if (!enabler_ref) { /* * If no backward ref, create it. - * Add backward ref from event to enabler. + * Add backward ref from event to event_enabler. */ enabler_ref = kzalloc(sizeof(*enabler_ref), GFP_KERNEL); if (!enabler_ref) return -ENOMEM; - enabler_ref->ref = enabler; + enabler_ref->ref = lttng_event_enabler_as_enabler(event_enabler); list_add(&enabler_ref->node, &event->enablers_ref_head); } @@ -1372,13 +1945,83 @@ int lttng_enabler_ref_events(struct lttng_enabler *enabler) /* * Link filter bytecodes if not linked yet. */ - lttng_enabler_event_link_bytecode(event, enabler); + lttng_enabler_link_bytecode(event->desc, + lttng_static_ctx, + &event->bytecode_runtime_head, + lttng_event_enabler_as_enabler(event_enabler)); /* TODO: merge event context. */ } return 0; } +/* + * Create struct lttng_trigger if it is missing and present in the list of + * tracepoint probes. + * Should be called with sessions mutex held. + */ +static +void lttng_create_trigger_if_missing(struct lttng_trigger_enabler *trigger_enabler) +{ + switch (trigger_enabler->base.event_param.instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + lttng_create_tracepoint_trigger_if_missing(trigger_enabler); + break; + case LTTNG_KERNEL_SYSCALL: + lttng_create_syscall_trigger_if_missing(trigger_enabler); + break; + default: + WARN_ON_ONCE(1); + break; + } +} + +/* + * Create triggers associated with a trigger enabler (if not already present). + */ +static +int lttng_trigger_enabler_ref_triggers(struct lttng_trigger_enabler *trigger_enabler) +{ + struct lttng_trigger_group *trigger_group = trigger_enabler->group; + struct lttng_trigger *trigger; + + /* First ensure that probe triggers are created for this enabler. */ + lttng_create_trigger_if_missing(trigger_enabler); + + /* Link the created trigger with its associated enabler. */ + list_for_each_entry(trigger, &trigger_group->triggers_head, list) { + struct lttng_enabler_ref *enabler_ref; + + if (!lttng_trigger_enabler_match_trigger(trigger_enabler, trigger)) + continue; + + enabler_ref = lttng_enabler_ref(&trigger->enablers_ref_head, + lttng_trigger_enabler_as_enabler(trigger_enabler)); + if (!enabler_ref) { + /* + * If no backward ref, create it. + * Add backward ref from trigger to enabler. + */ + enabler_ref = kzalloc(sizeof(*enabler_ref), GFP_KERNEL); + if (!enabler_ref) + return -ENOMEM; + + enabler_ref->ref = lttng_trigger_enabler_as_enabler( + trigger_enabler); + list_add(&enabler_ref->node, + &trigger->enablers_ref_head); + } + + /* + * Link filter bytecodes if not linked yet. + */ + lttng_enabler_link_bytecode(trigger->desc, + trigger_group->ctx, &trigger->bytecode_runtime_head, + lttng_trigger_enabler_as_enabler(trigger_enabler)); + } + return 0; +} + /* * Called at module load: connect the probe on all enablers matching * this event. @@ -1389,52 +2032,87 @@ int lttng_fix_pending_events(void) struct lttng_session *session; list_for_each_entry(session, &sessions, list) - lttng_session_lazy_sync_enablers(session); + lttng_session_lazy_sync_event_enablers(session); + return 0; +} + +static bool lttng_trigger_group_has_active_triggers( + struct lttng_trigger_group *trigger_group) +{ + struct lttng_trigger_enabler *trigger_enabler; + + list_for_each_entry(trigger_enabler, &trigger_group->enablers_head, + node) { + if (trigger_enabler->base.enabled) + return true; + } + return false; +} + +bool lttng_trigger_active(void) +{ + struct lttng_trigger_group *trigger_group; + + list_for_each_entry(trigger_group, &trigger_groups, node) { + if (lttng_trigger_group_has_active_triggers(trigger_group)) + return true; + } + return false; +} + +int lttng_fix_pending_triggers(void) +{ + struct lttng_trigger_group *trigger_group; + + list_for_each_entry(trigger_group, &trigger_groups, node) + lttng_trigger_group_sync_enablers(trigger_group); return 0; } -struct lttng_enabler *lttng_enabler_create(enum lttng_enabler_type type, +struct lttng_event_enabler *lttng_event_enabler_create( + enum lttng_enabler_format_type format_type, struct lttng_kernel_event *event_param, struct lttng_channel *chan) { - struct lttng_enabler *enabler; + struct lttng_event_enabler *event_enabler; - enabler = kzalloc(sizeof(*enabler), GFP_KERNEL); - if (!enabler) + event_enabler = kzalloc(sizeof(*event_enabler), GFP_KERNEL); + if (!event_enabler) return NULL; - enabler->type = type; - INIT_LIST_HEAD(&enabler->filter_bytecode_head); - memcpy(&enabler->event_param, event_param, - sizeof(enabler->event_param)); - enabler->chan = chan; + event_enabler->base.format_type = format_type; + INIT_LIST_HEAD(&event_enabler->base.filter_bytecode_head); + memcpy(&event_enabler->base.event_param, event_param, + sizeof(event_enabler->base.event_param)); + event_enabler->chan = chan; /* ctx left NULL */ - enabler->enabled = 0; - enabler->evtype = LTTNG_TYPE_ENABLER; + event_enabler->base.enabled = 0; + event_enabler->base.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); + list_add(&event_enabler->node, &event_enabler->chan->session->enablers_head); + lttng_session_lazy_sync_event_enablers(event_enabler->chan->session); mutex_unlock(&sessions_mutex); - return enabler; + return event_enabler; } -int lttng_enabler_enable(struct lttng_enabler *enabler) +int lttng_event_enabler_enable(struct lttng_event_enabler *event_enabler) { mutex_lock(&sessions_mutex); - enabler->enabled = 1; - lttng_session_lazy_sync_enablers(enabler->chan->session); + lttng_event_enabler_as_enabler(event_enabler)->enabled = 1; + lttng_session_lazy_sync_event_enablers(event_enabler->chan->session); mutex_unlock(&sessions_mutex); return 0; } -int lttng_enabler_disable(struct lttng_enabler *enabler) +int lttng_event_enabler_disable(struct lttng_event_enabler *event_enabler) { mutex_lock(&sessions_mutex); - enabler->enabled = 0; - lttng_session_lazy_sync_enablers(enabler->chan->session); + lttng_event_enabler_as_enabler(event_enabler)->enabled = 0; + lttng_session_lazy_sync_event_enablers(event_enabler->chan->session); mutex_unlock(&sessions_mutex); return 0; } +static int lttng_enabler_attach_bytecode(struct lttng_enabler *enabler, struct lttng_kernel_filter_bytecode __user *bytecode) { @@ -1453,11 +2131,12 @@ int lttng_enabler_attach_bytecode(struct lttng_enabler *enabler, sizeof(*bytecode) + bytecode_len); if (ret) goto error_free; + bytecode_node->enabler = enabler; /* Enforce length based on allocated size */ bytecode_node->bc.len = bytecode_len; list_add_tail(&bytecode_node->node, &enabler->filter_bytecode_head); - lttng_session_lazy_sync_enablers(enabler->chan->session); + return 0; error_free: @@ -1465,19 +2144,35 @@ error_free: return ret; } +int lttng_event_enabler_attach_bytecode(struct lttng_event_enabler *event_enabler, + struct lttng_kernel_filter_bytecode __user *bytecode) +{ + int ret; + ret = lttng_enabler_attach_bytecode( + lttng_event_enabler_as_enabler(event_enabler), bytecode); + if (ret) + goto error; + + lttng_session_lazy_sync_event_enablers(event_enabler->chan->session); + return 0; + +error: + return ret; +} + int lttng_event_add_callsite(struct lttng_event *event, struct lttng_kernel_event_callsite __user *callsite) { switch (event->instrumentation) { case LTTNG_KERNEL_UPROBE: - return lttng_uprobes_add_callsite(event, callsite); + return lttng_uprobes_event_add_callsite(event, callsite); default: return -EINVAL; } } -int lttng_enabler_attach_context(struct lttng_enabler *enabler, +int lttng_event_enabler_attach_context(struct lttng_event_enabler *event_enabler, struct lttng_kernel_context *context_param) { return -ENOSYS; @@ -1493,27 +2188,132 @@ void lttng_enabler_destroy(struct lttng_enabler *enabler) &enabler->filter_bytecode_head, node) { kfree(filter_node); } +} + +static +void lttng_event_enabler_destroy(struct lttng_event_enabler *event_enabler) +{ + lttng_enabler_destroy(lttng_event_enabler_as_enabler(event_enabler)); /* Destroy contexts */ - lttng_destroy_context(enabler->ctx); + lttng_destroy_context(event_enabler->ctx); + + list_del(&event_enabler->node); + kfree(event_enabler); +} + +struct lttng_trigger_enabler *lttng_trigger_enabler_create( + struct lttng_trigger_group *trigger_group, + enum lttng_enabler_format_type format_type, + struct lttng_kernel_trigger *trigger_param) +{ + struct lttng_trigger_enabler *trigger_enabler; + + trigger_enabler = kzalloc(sizeof(*trigger_enabler), GFP_KERNEL); + if (!trigger_enabler) + return NULL; + + trigger_enabler->base.format_type = format_type; + INIT_LIST_HEAD(&trigger_enabler->base.filter_bytecode_head); + + trigger_enabler->id = trigger_param->id; + + memcpy(&trigger_enabler->base.event_param.name, trigger_param->name, + sizeof(trigger_enabler->base.event_param.name)); + trigger_enabler->base.event_param.instrumentation = trigger_param->instrumentation; + trigger_enabler->base.evtype = LTTNG_TYPE_ENABLER; + + trigger_enabler->base.enabled = 0; + trigger_enabler->group = trigger_group; + + mutex_lock(&sessions_mutex); + list_add(&trigger_enabler->node, &trigger_enabler->group->enablers_head); + lttng_trigger_group_sync_enablers(trigger_enabler->group); + + mutex_unlock(&sessions_mutex); + + return trigger_enabler; +} + +int lttng_trigger_enabler_enable(struct lttng_trigger_enabler *trigger_enabler) +{ + mutex_lock(&sessions_mutex); + lttng_trigger_enabler_as_enabler(trigger_enabler)->enabled = 1; + lttng_trigger_group_sync_enablers(trigger_enabler->group); + mutex_unlock(&sessions_mutex); + return 0; +} + +int lttng_trigger_enabler_disable(struct lttng_trigger_enabler *trigger_enabler) +{ + mutex_lock(&sessions_mutex); + lttng_trigger_enabler_as_enabler(trigger_enabler)->enabled = 0; + lttng_trigger_group_sync_enablers(trigger_enabler->group); + mutex_unlock(&sessions_mutex); + return 0; +} + +int lttng_trigger_enabler_attach_bytecode(struct lttng_trigger_enabler *trigger_enabler, + struct lttng_kernel_filter_bytecode __user *bytecode) +{ + int ret; + + ret = lttng_enabler_attach_bytecode( + lttng_trigger_enabler_as_enabler(trigger_enabler), bytecode); + if (ret) + goto error; + + lttng_trigger_group_sync_enablers(trigger_enabler->group); + return 0; + +error: + return ret; +} + +int lttng_trigger_add_callsite(struct lttng_trigger *trigger, + struct lttng_kernel_event_callsite __user *callsite) +{ + + switch (trigger->instrumentation) { + case LTTNG_KERNEL_UPROBE: + return lttng_uprobes_trigger_add_callsite(trigger, callsite); + default: + return -EINVAL; + } +} + +int lttng_trigger_enabler_attach_context(struct lttng_trigger_enabler *trigger_enabler, + struct lttng_kernel_context *context_param) +{ + return -ENOSYS; +} - list_del(&enabler->node); - kfree(enabler); +static +void lttng_trigger_enabler_destroy(struct lttng_trigger_enabler *trigger_enabler) +{ + if (!trigger_enabler) { + return; + } + + list_del(&trigger_enabler->node); + + lttng_enabler_destroy(lttng_trigger_enabler_as_enabler(trigger_enabler)); + kfree(trigger_enabler); } /* - * lttng_session_sync_enablers should be called just before starting a + * lttng_session_sync_event_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) +void lttng_session_sync_event_enablers(struct lttng_session *session) { - struct lttng_enabler *enabler; + struct lttng_event_enabler *event_enabler; struct lttng_event *event; - list_for_each_entry(enabler, &session->enablers_head, node) - lttng_enabler_ref_events(enabler); + list_for_each_entry(event_enabler, &session->enablers_head, node) + lttng_event_enabler_ref_events(event_enabler); /* * For each event, if at least one of its enablers is enabled, * and its channel and session transient states are enabled, we @@ -1585,12 +2385,79 @@ void lttng_session_sync_enablers(struct lttng_session *session) * Should be called with sessions mutex held. */ static -void lttng_session_lazy_sync_enablers(struct lttng_session *session) +void lttng_session_lazy_sync_event_enablers(struct lttng_session *session) { /* We can skip if session is not active */ if (!session->active) return; - lttng_session_sync_enablers(session); + lttng_session_sync_event_enablers(session); +} + +static +void lttng_trigger_group_sync_enablers(struct lttng_trigger_group *trigger_group) +{ + struct lttng_trigger_enabler *trigger_enabler; + struct lttng_trigger *trigger; + + list_for_each_entry(trigger_enabler, &trigger_group->enablers_head, node) + lttng_trigger_enabler_ref_triggers(trigger_enabler); + + /* + * For each trigger, if at least one of its enablers is enabled, + * we enable the trigger, else we disable it. + */ + list_for_each_entry(trigger, &trigger_group->triggers_head, list) { + struct lttng_enabler_ref *enabler_ref; + struct lttng_bytecode_runtime *runtime; + int enabled = 0, has_enablers_without_bytecode = 0; + + switch (trigger->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_SYSCALL: + /* Enable triggers */ + list_for_each_entry(enabler_ref, + &trigger->enablers_ref_head, node) { + if (enabler_ref->ref->enabled) { + enabled = 1; + break; + } + } + break; + default: + /* Not handled with sync. */ + continue; + } + + WRITE_ONCE(trigger->enabled, enabled); + /* + * Sync tracepoint registration with trigger enabled + * state. + */ + if (enabled) { + if (!trigger->registered) + register_trigger(trigger); + } else { + if (trigger->registered) + _lttng_trigger_unregister(trigger); + } + + /* Check if has enablers without bytecode enabled */ + list_for_each_entry(enabler_ref, + &trigger->enablers_ref_head, node) { + if (enabler_ref->ref->enabled + && list_empty(&enabler_ref->ref->filter_bytecode_head)) { + has_enablers_without_bytecode = 1; + break; + } + } + trigger->has_enablers_without_bytecode = + has_enablers_without_bytecode; + + /* Enable filters */ + list_for_each_entry(runtime, + &trigger->bytecode_runtime_head, node) + lttng_filter_sync_state(runtime); + } } /* @@ -2473,6 +3340,61 @@ int64_t measure_clock_offset(void) return offset; } +static +int print_escaped_ctf_string(struct lttng_session *session, const char *string) +{ + int ret; + size_t i; + char cur; + + i = 0; + cur = string[i]; + while (cur != '\0') { + switch (cur) { + case '\n': + ret = lttng_metadata_printf(session, "%s", "\\n"); + break; + case '\\': + case '"': + ret = lttng_metadata_printf(session, "%c", '\\'); + if (ret) + goto error; + /* We still print the current char */ + /* Fallthrough */ + default: + ret = lttng_metadata_printf(session, "%c", cur); + break; + } + + if (ret) + goto error; + + cur = string[++i]; + } +error: + return ret; +} + +static +int print_metadata_escaped_field(struct lttng_session *session, const char *field, + const char *field_value) +{ + int ret; + + ret = lttng_metadata_printf(session, " %s = \"", field); + if (ret) + goto error; + + ret = print_escaped_ctf_string(session, field_value); + if (ret) + goto error; + + ret = lttng_metadata_printf(session, "\";\n"); + +error: + return ret; +} + /* * Output metadata into this session's metadata buffers. * Must be called with sessions_mutex held. @@ -2482,6 +3404,7 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) { unsigned char *uuid_c = session->uuid.b; unsigned char uuid_s[37], clock_uuid_s[BOOT_ID_LEN]; + const char *product_uuid; struct lttng_channel *chan; struct lttng_event *event; int ret = 0; @@ -2548,7 +3471,7 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) " tracer_major = %d;\n" " tracer_minor = %d;\n" " tracer_patchlevel = %d;\n" - "};\n\n", + " trace_buffering_scheme = \"global\";\n", current->nsproxy->uts_ns->name.nodename, utsname()->sysname, utsname()->release, @@ -2560,6 +3483,30 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) if (ret) goto end; + ret = print_metadata_escaped_field(session, "trace_name", session->name); + if (ret) + goto end; + ret = print_metadata_escaped_field(session, "trace_creation_datetime", + session->creation_time); + if (ret) + goto end; + + /* Add the product UUID to the 'env' section */ + product_uuid = dmi_get_system_info(DMI_PRODUCT_UUID); + if (product_uuid) { + ret = lttng_metadata_printf(session, + " product_uuid = \"%s\";\n", + product_uuid + ); + if (ret) + goto end; + } + + /* Close the 'env' section */ + ret = lttng_metadata_printf(session, "};\n\n"); + if (ret) + goto end; + ret = lttng_metadata_printf(session, "clock {\n" " name = \"%s\";\n", @@ -2830,7 +3777,12 @@ static int __init lttng_events_init(void) event_cache = KMEM_CACHE(lttng_event, 0); if (!event_cache) { ret = -ENOMEM; - goto error_kmem; + goto error_kmem_event; + } + trigger_cache = KMEM_CACHE(lttng_trigger, 0); + if (!trigger_cache) { + ret = -ENOMEM; + goto error_kmem_trigger; } ret = lttng_abi_init(); if (ret) @@ -2864,8 +3816,10 @@ error_hotplug: error_logger: lttng_abi_exit(); error_abi: + kmem_cache_destroy(trigger_cache); +error_kmem_trigger: kmem_cache_destroy(event_cache); -error_kmem: +error_kmem_event: lttng_tracepoint_exit(); error_tp: lttng_context_exit(); @@ -2900,6 +3854,7 @@ static void __exit lttng_events_exit(void) list_for_each_entry_safe(session, tmpsession, &sessions, list) lttng_session_destroy(session); kmem_cache_destroy(event_cache); + kmem_cache_destroy(trigger_cache); lttng_tracepoint_exit(); lttng_context_exit(); printk(KERN_NOTICE "LTTng: Unloaded modules v%s.%s.%s%s (%s)%s%s\n",