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