SoW-2019-0002: Dynamic Snapshot
[deliverable/lttng-modules.git] / lttng-events.c
index 566080a3a4c1be92ea01e9e6b2af497fd040d007..73cb361081c5f93b21eae258d3279057a7cc0378 100644 (file)
 #include <linux/file.h>
 #include <linux/anon_inodes.h>
 #include <wrapper/file.h>
-#include <linux/jhash.h>
 #include <linux/uaccess.h>
 #include <linux/vmalloc.h>
 #include <linux/uuid.h>
+#include <linux/dmi.h>
 
 #include <wrapper/vmalloc.h>   /* for wrapper_vmalloc_sync_all() */
 #include <wrapper/random.h>
 #include <lttng-abi-old.h>
 #include <lttng-endian.h>
 #include <lttng-string-utils.h>
+#include <lttng-utils.h>
 #include <wrapper/ringbuffer/backend.h>
 #include <wrapper/ringbuffer/frontend.h>
 
 #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, &lttng_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, &lttng_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, &lttng_tracker_pids_list_seq_ops);
+       return seq_open(file, &lttng_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]",
-                                         &lttng_tracker_pids_list_fops,
+       tracker_ids_list_file = anon_inode_getfile("[lttng_tracker_ids_list]",
+                                         &lttng_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",
This page took 0.048167 seconds and 5 git commands to generate.