Move to kernel style SPDX license identifiers
[lttng-ust.git] / liblttng-ust / lttng-events.c
index d9efebe6bae215427d304335b6c564fb6939168b..6196fc6a59fbbd89a8336f4526e62c5060109875 100644 (file)
@@ -1,31 +1,17 @@
 /*
- * lttng-events.c
- *
- * Holds LTTng per-session event registry.
+ * SPDX-License-Identifier: LGPL-2.1-only
  *
  * Copyright (C) 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; only
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Holds LTTng per-session event registry.
  */
 
 #define _LGPL_SOURCE
 #include <stdio.h>
-#include <urcu/list.h>
-#include <urcu/hlist.h>
-#include <pthread.h>
+#include <assert.h>
 #include <errno.h>
+#include <limits.h>
+#include <pthread.h>
 #include <sys/shm.h>
 #include <sys/ipc.h>
 #include <stdint.h>
 #include <inttypes.h>
 #include <time.h>
 #include <stdbool.h>
+#include <unistd.h>
+#include <dlfcn.h>
 #include <lttng/ust-endian.h>
-#include "clock.h"
 
-#include <urcu-bp.h>
+#include <urcu/arch.h>
 #include <urcu/compiler.h>
+#include <urcu/hlist.h>
+#include <urcu/list.h>
 #include <urcu/uatomic.h>
-#include <urcu/arch.h>
 
 #include <lttng/tracepoint.h>
 #include <lttng/ust-events.h>
@@ -48,6 +36,7 @@
 #include <helper.h>
 #include <lttng/ust-ctl.h>
 #include <ust-comm.h>
+#include <ust-fd.h>
 #include <lttng/ust-dynamic-type.h>
 #include <lttng/ust-context-provider.h>
 #include "error.h"
 
 #include "tracepoint-internal.h"
 #include "string-utils.h"
+#include "lttng-bytecode.h"
 #include "lttng-tracer.h"
 #include "lttng-tracer-core.h"
 #include "lttng-ust-statedump.h"
+#include "context-internal.h"
 #include "ust-events-internal.h"
 #include "wait.h"
 #include "../libringbuffer/shm.h"
+#include "../libcounter/counter.h"
 #include "jhash.h"
+#include <lttng/ust-abi.h>
 
 /*
  * All operations within this file are called by the communication
@@ -70,6 +63,7 @@
  */
 
 static CDS_LIST_HEAD(sessions);
+static CDS_LIST_HEAD(event_notifier_groups);
 
 struct cds_list_head *_lttng_get_sessions(void)
 {
@@ -77,6 +71,8 @@ struct cds_list_head *_lttng_get_sessions(void)
 }
 
 static void _lttng_event_destroy(struct lttng_event *event);
+static void _lttng_event_notifier_destroy(
+               struct lttng_event_notifier *event_notifier);
 static void _lttng_enum_destroy(struct lttng_enum *_enum);
 
 static
@@ -84,6 +80,9 @@ 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_notifier_group_sync_enablers(
+               struct lttng_event_notifier_group *event_notifier_group);
+static
 void lttng_enabler_destroy(struct lttng_enabler *enabler);
 
 /*
@@ -130,11 +129,6 @@ int lttng_loglevel_match(int loglevel,
        }
 }
 
-void synchronize_trace(void)
-{
-       synchronize_rcu();
-}
-
 struct lttng_session *lttng_session_create(void)
 {
        struct lttng_session *session;
@@ -143,7 +137,7 @@ struct lttng_session *lttng_session_create(void)
        session = zmalloc(sizeof(struct lttng_session));
        if (!session)
                return NULL;
-       if (lttng_session_context_init(&session->ctx)) {
+       if (lttng_context_init_all(&session->ctx)) {
                free(session);
                return NULL;
        }
@@ -159,6 +153,71 @@ struct lttng_session *lttng_session_create(void)
        return session;
 }
 
+struct lttng_counter *lttng_ust_counter_create(
+               const char *counter_transport_name,
+               size_t number_dimensions, const struct lttng_counter_dimension *dimensions)
+{
+       struct lttng_counter_transport *counter_transport = NULL;
+       struct lttng_counter *counter = NULL;
+
+       counter_transport = lttng_counter_transport_find(counter_transport_name);
+       if (!counter_transport)
+               goto notransport;
+       counter = zmalloc(sizeof(struct lttng_counter));
+       if (!counter)
+               goto nomem;
+
+       counter->ops = &counter_transport->ops;
+       counter->transport = counter_transport;
+
+       counter->counter = counter->ops->counter_create(
+                       number_dimensions, dimensions, 0,
+                       -1, 0, NULL, false);
+       if (!counter->counter) {
+               goto create_error;
+       }
+
+       return counter;
+
+create_error:
+       free(counter);
+nomem:
+notransport:
+       return NULL;
+}
+
+static
+void lttng_ust_counter_destroy(struct lttng_counter *counter)
+{
+       counter->ops->counter_destroy(counter->counter);
+       free(counter);
+}
+
+struct lttng_event_notifier_group *lttng_event_notifier_group_create(void)
+{
+       struct lttng_event_notifier_group *event_notifier_group;
+       int i;
+
+       event_notifier_group = zmalloc(sizeof(struct lttng_event_notifier_group));
+       if (!event_notifier_group)
+               return NULL;
+
+       /* Add all contexts. */
+       if (lttng_context_init_all(&event_notifier_group->ctx)) {
+               free(event_notifier_group);
+               return NULL;
+       }
+
+       CDS_INIT_LIST_HEAD(&event_notifier_group->enablers_head);
+       CDS_INIT_LIST_HEAD(&event_notifier_group->event_notifiers_head);
+       for (i = 0; i < LTTNG_UST_EVENT_NOTIFIER_HT_SIZE; i++)
+               CDS_INIT_HLIST_HEAD(&event_notifier_group->event_notifiers_ht.table[i]);
+
+       cds_list_add(&event_notifier_group->node, &event_notifier_groups);
+
+       return event_notifier_group;
+}
+
 /*
  * Only used internally at session destruction.
  */
@@ -195,6 +254,21 @@ void register_event(struct lttng_event *event)
                event->registered = 1;
 }
 
+static
+void register_event_notifier(struct lttng_event_notifier *event_notifier)
+{
+       int ret;
+       const struct lttng_event_desc *desc;
+
+       assert(event_notifier->registered == 0);
+       desc = event_notifier->desc;
+       ret = __tracepoint_probe_register_queue_release(desc->name,
+               desc->u.ext.event_notifier_callback, event_notifier, desc->signature);
+       WARN_ON_ONCE(ret);
+       if (!ret)
+               event_notifier->registered = 1;
+}
+
 static
 void unregister_event(struct lttng_event *event)
 {
@@ -211,6 +285,21 @@ void unregister_event(struct lttng_event *event)
                event->registered = 0;
 }
 
+static
+void unregister_event_notifier(struct lttng_event_notifier *event_notifier)
+{
+       int ret;
+       const struct lttng_event_desc *desc;
+
+       assert(event_notifier->registered == 1);
+       desc = event_notifier->desc;
+       ret = __tracepoint_probe_unregister_queue_release(desc->name,
+               desc->u.ext.event_notifier_callback, event_notifier);
+       WARN_ON_ONCE(ret);
+       if (!ret)
+               event_notifier->registered = 0;
+}
+
 /*
  * Only used internally at session destruction.
  */
@@ -221,6 +310,16 @@ void _lttng_event_unregister(struct lttng_event *event)
                unregister_event(event);
 }
 
+/*
+ * Only used internally at session destruction.
+ */
+static
+void _lttng_event_notifier_unregister(struct lttng_event_notifier *event_notifier)
+{
+       if (event_notifier->registered)
+               unregister_event_notifier(event_notifier);
+}
+
 void lttng_session_destroy(struct lttng_session *session)
 {
        struct lttng_channel *chan, *tmpchan;
@@ -232,7 +331,7 @@ void lttng_session_destroy(struct lttng_session *session)
        cds_list_for_each_entry(event, &session->events_head, node) {
                _lttng_event_unregister(event);
        }
-       synchronize_trace();    /* Wait for in-flight events to complete */
+       lttng_ust_synchronize_trace();  /* Wait for in-flight events to complete */
        __tracepoint_probe_prune_release_queue();
        cds_list_for_each_entry_safe(event_enabler, event_tmpenabler,
                        &session->enablers_head, node)
@@ -250,10 +349,56 @@ void lttng_session_destroy(struct lttng_session *session)
        free(session);
 }
 
+void lttng_event_notifier_group_destroy(
+               struct lttng_event_notifier_group *event_notifier_group)
+{
+       int close_ret;
+       struct lttng_event_notifier_enabler *notifier_enabler, *tmpnotifier_enabler;
+       struct lttng_event_notifier *notifier, *tmpnotifier;
+
+       if (!event_notifier_group) {
+               return;
+       }
+
+       cds_list_for_each_entry(notifier,
+                       &event_notifier_group->event_notifiers_head, node)
+               _lttng_event_notifier_unregister(notifier);
+
+       lttng_ust_synchronize_trace();
+
+       cds_list_for_each_entry_safe(notifier_enabler, tmpnotifier_enabler,
+                       &event_notifier_group->enablers_head, node)
+               lttng_event_notifier_enabler_destroy(notifier_enabler);
+
+       cds_list_for_each_entry_safe(notifier, tmpnotifier,
+                       &event_notifier_group->event_notifiers_head, node)
+               _lttng_event_notifier_destroy(notifier);
+
+       if (event_notifier_group->error_counter)
+               lttng_ust_counter_destroy(event_notifier_group->error_counter);
+
+       /* Close the notification fd to the listener of event_notifiers. */
+
+       lttng_ust_lock_fd_tracker();
+       close_ret = close(event_notifier_group->notification_fd);
+       if (!close_ret) {
+               lttng_ust_delete_fd_from_tracker(
+                               event_notifier_group->notification_fd);
+       } else {
+               PERROR("close");
+               abort();
+       }
+       lttng_ust_unlock_fd_tracker();
+
+       cds_list_del(&event_notifier_group->node);
+
+       free(event_notifier_group);
+}
+
 static
 void lttng_enabler_destroy(struct lttng_enabler *enabler)
 {
-       struct lttng_ust_filter_bytecode_node *filter_node, *tmp_filter_node;
+       struct lttng_ust_bytecode_node *filter_node, *tmp_filter_node;
        struct lttng_ust_excluder_node *excluder_node, *tmp_excluder_node;
 
        if (!enabler) {
@@ -273,6 +418,19 @@ void lttng_enabler_destroy(struct lttng_enabler *enabler)
        }
 }
 
+ void lttng_event_notifier_enabler_destroy(struct lttng_event_notifier_enabler *event_notifier_enabler)
+{
+       if (!event_notifier_enabler) {
+               return;
+       }
+
+       cds_list_del(&event_notifier_enabler->node);
+
+       lttng_enabler_destroy(lttng_event_notifier_enabler_as_enabler(event_notifier_enabler));
+
+       free(event_notifier_enabler);
+}
+
 static
 int lttng_enum_create(const struct lttng_enum_desc *desc,
                struct lttng_session *session)
@@ -620,7 +778,7 @@ int lttng_event_create(const struct lttng_event_desc *desc,
        /* Event will be enabled by enabler sync. */
        event->enabled = 0;
        event->registered = 0;
-       CDS_INIT_LIST_HEAD(&event->bytecode_runtime_head);
+       CDS_INIT_LIST_HEAD(&event->filter_bytecode_runtime_head);
        CDS_INIT_LIST_HEAD(&event->enablers_ref_head);
        event->desc = desc;
 
@@ -662,6 +820,72 @@ socket_error:
        return ret;
 }
 
+static
+int lttng_event_notifier_create(const struct lttng_event_desc *desc,
+               uint64_t token, uint64_t error_counter_index,
+               struct lttng_event_notifier_group *event_notifier_group)
+{
+       struct lttng_event_notifier *event_notifier;
+       struct cds_hlist_head *head;
+       int ret = 0;
+
+       /*
+        * Get the hashtable bucket the created lttng_event_notifier object
+        * should be inserted.
+        */
+       head = borrow_hash_table_bucket(
+               event_notifier_group->event_notifiers_ht.table,
+               LTTNG_UST_EVENT_NOTIFIER_HT_SIZE, desc);
+
+       event_notifier = zmalloc(sizeof(struct lttng_event_notifier));
+       if (!event_notifier) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       event_notifier->group = event_notifier_group;
+       event_notifier->user_token = token;
+       event_notifier->error_counter_index = error_counter_index;
+
+       /* Event notifier will be enabled by enabler sync. */
+       event_notifier->enabled = 0;
+       event_notifier->registered = 0;
+
+       CDS_INIT_LIST_HEAD(&event_notifier->filter_bytecode_runtime_head);
+       CDS_INIT_LIST_HEAD(&event_notifier->capture_bytecode_runtime_head);
+       CDS_INIT_LIST_HEAD(&event_notifier->enablers_ref_head);
+       event_notifier->desc = desc;
+       event_notifier->notification_send = lttng_event_notifier_notification_send;
+
+       cds_list_add(&event_notifier->node,
+                       &event_notifier_group->event_notifiers_head);
+       cds_hlist_add_head(&event_notifier->hlist, head);
+
+       return 0;
+
+error:
+       return ret;
+}
+
+static
+void _lttng_event_notifier_destroy(struct lttng_event_notifier *event_notifier)
+{
+       struct lttng_enabler_ref *enabler_ref, *tmp_enabler_ref;
+
+       /* Remove from event_notifier list. */
+       cds_list_del(&event_notifier->node);
+       /* Remove from event_notifier hash table. */
+       cds_hlist_del(&event_notifier->hlist);
+
+       lttng_free_event_notifier_filter_runtime(event_notifier);
+
+       /* Free event_notifier enabler refs */
+       cds_list_for_each_entry_safe(enabler_ref, tmp_enabler_ref,
+                       &event_notifier->enablers_ref_head, node)
+               free(enabler_ref);
+       free(event_notifier);
+}
+
 static
 int lttng_desc_match_star_glob_enabler(const struct lttng_event_desc *desc,
                struct lttng_enabler *enabler)
@@ -759,6 +983,21 @@ int lttng_event_enabler_match_event(struct lttng_event_enabler *event_enabler,
                return 0;
 }
 
+static
+int lttng_event_notifier_enabler_match_event_notifier(
+               struct lttng_event_notifier_enabler *event_notifier_enabler,
+               struct lttng_event_notifier *event_notifier)
+{
+       int desc_matches = lttng_desc_match_enabler(event_notifier->desc,
+               lttng_event_notifier_enabler_as_enabler(event_notifier_enabler));
+
+       if (desc_matches && event_notifier->group == event_notifier_enabler->group &&
+                       event_notifier->user_token == event_notifier_enabler->user_token)
+               return 1;
+       else
+               return 0;
+}
+
 static
 struct lttng_enabler_ref *lttng_enabler_ref(
                struct cds_list_head *enabler_ref_list,
@@ -833,103 +1072,71 @@ void lttng_create_event_if_missing(struct lttng_event_enabler *event_enabler)
        }
 }
 
-/*
- * Iterate over all the UST sessions to unregister and destroy all probes from
- * the probe provider descriptor received as argument. Must me called with the
- * ust_lock held.
- */
-void lttng_probe_provider_unregister_events(struct lttng_probe_desc *provider_desc)
+static
+void probe_provider_event_for_each(struct lttng_probe_desc *provider_desc,
+               void (*event_func)(struct lttng_session *session,
+                       struct lttng_event *event),
+               void (*event_notifier_func)(struct lttng_event_notifier *event_notifier))
 {
        struct cds_hlist_node *node, *tmp_node;
        struct cds_list_head *sessionsp;
-       struct lttng_session *session;
-       struct cds_hlist_head *head;
-       struct lttng_event *event;
-       unsigned int i, j;
+       unsigned int i;
 
        /* Get handle on list of sessions. */
        sessionsp = _lttng_get_sessions();
 
        /*
-        * Iterate over all events in the probe provider descriptions and sessions
-        * to queue the unregistration of the events.
+        * Iterate over all events in the probe provider descriptions and
+        * sessions to queue the unregistration of the events.
         */
        for (i = 0; i < provider_desc->nr_events; i++) {
                const struct lttng_event_desc *event_desc;
+               struct lttng_event_notifier_group *event_notifier_group;
+               struct lttng_event_notifier *event_notifier;
+               struct lttng_session *session;
+               struct cds_hlist_head *head;
+               struct lttng_event *event;
 
                event_desc = provider_desc->event_desc[i];
 
-               /* Iterate over all session to find the current event description. */
+               /*
+                * Iterate over all session to find the current event
+                * description.
+                */
                cds_list_for_each_entry(session, sessionsp, node) {
                        /*
-                        * Get the list of events in the hashtable bucket and iterate to
-                        * find the event matching this descriptor.
+                        * Get the list of events in the hashtable bucket and
+                        * iterate to find the event matching this descriptor.
                         */
                        head = borrow_hash_table_bucket(
                                session->events_ht.table,
                                LTTNG_UST_EVENT_HT_SIZE, event_desc);
 
-                       cds_hlist_for_each_entry(event, node, head, hlist) {
+                       cds_hlist_for_each_entry_safe(event, node, tmp_node, head, hlist) {
                                if (event_desc == event->desc) {
-                                       /* Queue the unregistration of this event. */
-                                       _lttng_event_unregister(event);
+                                       event_func(session, event);
                                        break;
                                }
                        }
                }
-       }
 
-       /* Wait for grace period. */
-       synchronize_trace();
-       /* Prune the unregistration queue. */
-       __tracepoint_probe_prune_release_queue();
-
-       /*
-        * It is now safe to destroy the events and remove them from the event list
-        * and hashtables.
-        */
-       for (i = 0; i < provider_desc->nr_events; i++) {
-               const struct lttng_event_desc *event_desc;
-
-               event_desc = provider_desc->event_desc[i];
-
-               /* Iterate over all sessions to find the current event description. */
-               cds_list_for_each_entry(session, sessionsp, node) {
+               /*
+                * Iterate over all event_notifier groups to find the current event
+                * description.
+                */
+               cds_list_for_each_entry(event_notifier_group, &event_notifier_groups, node) {
                        /*
-                        * Get the list of events in the hashtable bucket and iterate to
-                        * find the event matching this descriptor.
+                        * Get the list of event_notifiers in the hashtable bucket and
+                        * iterate to find the event_notifier matching this
+                        * descriptor.
                         */
                        head = borrow_hash_table_bucket(
-                               session->events_ht.table,
-                               LTTNG_UST_EVENT_HT_SIZE, event_desc);
+                               event_notifier_group->event_notifiers_ht.table,
+                               LTTNG_UST_EVENT_NOTIFIER_HT_SIZE, event_desc);
 
-                       cds_hlist_for_each_entry_safe(event, node, tmp_node, head, hlist) {
-                               if (event_desc == event->desc) {
-                                       /* Destroy enums of the current event. */
-                                       for (j = 0; j < event->desc->nr_fields; j++) {
-                                               const struct lttng_enum_desc *enum_desc;
-                                               const struct lttng_event_field *field;
-                                               struct lttng_enum *curr_enum;
-
-                                               field = &(event->desc->fields[j]);
-                                               switch (field->type.atype) {
-                                               case atype_enum:
-                                                       enum_desc = field->type.u.legacy.basic.enumeration.desc;
-                                                       break;
-                                               case atype_enum_nestable:
-                                                       enum_desc = field->type.u.enum_nestable.desc;
-                                                       break;
-                                               default:
-                                                       continue;
-                                               }
-                                               curr_enum = lttng_ust_enum_get_from_desc(session, enum_desc);
-                                               if (curr_enum) {
-                                                       _lttng_enum_destroy(curr_enum);
-                                               }
-                                       }
-
-                                       /* Destroy event. */
-                                       _lttng_event_destroy(event);
+                       cds_hlist_for_each_entry_safe(event_notifier, node, tmp_node, head, hlist) {
+                               if (event_desc == event_notifier->desc) {
+                                       event_notifier_func(event_notifier);
                                        break;
                                }
                        }
@@ -937,8 +1144,77 @@ void lttng_probe_provider_unregister_events(struct lttng_probe_desc *provider_de
        }
 }
 
+static
+void _unregister_event(struct lttng_session *session,
+               struct lttng_event *event)
+{
+       _lttng_event_unregister(event);
+}
+
+static
+void _event_enum_destroy(struct lttng_session *session,
+               struct lttng_event *event)
+{
+       unsigned int i;
+
+       /* Destroy enums of the current event. */
+       for (i = 0; i < event->desc->nr_fields; i++) {
+               const struct lttng_enum_desc *enum_desc;
+               const struct lttng_event_field *field;
+               struct lttng_enum *curr_enum;
+
+               field = &(event->desc->fields[i]);
+               switch (field->type.atype) {
+               case atype_enum:
+                       enum_desc = field->type.u.legacy.basic.enumeration.desc;
+                       break;
+               case atype_enum_nestable:
+                       enum_desc = field->type.u.enum_nestable.desc;
+                       break;
+               default:
+                       continue;
+               }
+
+               curr_enum = lttng_ust_enum_get_from_desc(session, enum_desc);
+               if (curr_enum) {
+                       _lttng_enum_destroy(curr_enum);
+               }
+       }
+
+       /* Destroy event. */
+       _lttng_event_destroy(event);
+}
+
 /*
- * Create events associated with an enabler (if not already present),
+ * Iterate over all the UST sessions to unregister and destroy all probes from
+ * the probe provider descriptor received as argument. Must me called with the
+ * ust_lock held.
+ */
+void lttng_probe_provider_unregister_events(
+               struct lttng_probe_desc *provider_desc)
+{
+       /*
+        * Iterate over all events in the probe provider descriptions and sessions
+        * to queue the unregistration of the events.
+        */
+       probe_provider_event_for_each(provider_desc, _unregister_event,
+               _lttng_event_notifier_unregister);
+
+       /* Wait for grace period. */
+       lttng_ust_synchronize_trace();
+       /* Prune the unregistration queue. */
+       __tracepoint_probe_prune_release_queue();
+
+       /*
+        * It is now safe to destroy the events and remove them from the event list
+        * and hashtables.
+        */
+       probe_provider_event_for_each(provider_desc, _event_enum_destroy,
+               _lttng_event_notifier_destroy);
+}
+
+/*
+ * Create events associated with an event enabler (if not already present),
  * and add backward reference from the event to the enabler.
  */
 static
@@ -981,8 +1257,8 @@ int lttng_event_enabler_ref_events(struct lttng_event_enabler *event_enabler)
                 */
                lttng_enabler_link_bytecode(event->desc,
                        &session->ctx,
-                       &event->bytecode_runtime_head,
-                       lttng_event_enabler_as_enabler(event_enabler));
+                       &event->filter_bytecode_runtime_head,
+                       &lttng_event_enabler_as_enabler(event_enabler)->filter_bytecode_head);
 
                /* TODO: merge event context. */
        }
@@ -1005,6 +1281,16 @@ int lttng_fix_pending_events(void)
        return 0;
 }
 
+int lttng_fix_pending_event_notifiers(void)
+{
+       struct lttng_event_notifier_group *event_notifier_group;
+
+       cds_list_for_each_entry(event_notifier_group, &event_notifier_groups, node) {
+               lttng_event_notifier_group_sync_enablers(event_notifier_group);
+       }
+       return 0;
+}
+
 /*
  * For each session of the owner thread, execute pending statedump.
  * Only dump state for the sessions owned by the caller thread, because
@@ -1098,6 +1384,46 @@ struct lttng_event_enabler *lttng_event_enabler_create(
        return event_enabler;
 }
 
+struct lttng_event_notifier_enabler *lttng_event_notifier_enabler_create(
+               struct lttng_event_notifier_group *event_notifier_group,
+               enum lttng_enabler_format_type format_type,
+               struct lttng_ust_event_notifier *event_notifier_param)
+{
+       struct lttng_event_notifier_enabler *event_notifier_enabler;
+
+       event_notifier_enabler = zmalloc(sizeof(*event_notifier_enabler));
+       if (!event_notifier_enabler)
+               return NULL;
+       event_notifier_enabler->base.format_type = format_type;
+       CDS_INIT_LIST_HEAD(&event_notifier_enabler->base.filter_bytecode_head);
+       CDS_INIT_LIST_HEAD(&event_notifier_enabler->capture_bytecode_head);
+       CDS_INIT_LIST_HEAD(&event_notifier_enabler->base.excluder_head);
+
+       event_notifier_enabler->user_token = event_notifier_param->event.token;
+       event_notifier_enabler->error_counter_index = event_notifier_param->error_counter_index;
+       event_notifier_enabler->num_captures = 0;
+
+       memcpy(&event_notifier_enabler->base.event_param.name,
+               event_notifier_param->event.name,
+               sizeof(event_notifier_enabler->base.event_param.name));
+       event_notifier_enabler->base.event_param.instrumentation =
+               event_notifier_param->event.instrumentation;
+       event_notifier_enabler->base.event_param.loglevel =
+               event_notifier_param->event.loglevel;
+       event_notifier_enabler->base.event_param.loglevel_type =
+               event_notifier_param->event.loglevel_type;
+
+       event_notifier_enabler->base.enabled = 0;
+       event_notifier_enabler->group = event_notifier_group;
+
+       cds_list_add(&event_notifier_enabler->node,
+                       &event_notifier_group->enablers_head);
+
+       lttng_event_notifier_group_sync_enablers(event_notifier_group);
+
+       return event_notifier_enabler;
+}
+
 int lttng_event_enabler_enable(struct lttng_event_enabler *event_enabler)
 {
        lttng_event_enabler_as_enabler(event_enabler)->enabled = 1;
@@ -1115,17 +1441,19 @@ int lttng_event_enabler_disable(struct lttng_event_enabler *event_enabler)
 }
 
 static
-void _lttng_enabler_attach_bytecode(struct lttng_enabler *enabler,
-               struct lttng_ust_filter_bytecode_node *bytecode)
+void _lttng_enabler_attach_filter_bytecode(struct lttng_enabler *enabler,
+               struct lttng_ust_bytecode_node **bytecode)
 {
-       bytecode->enabler = enabler;
-       cds_list_add_tail(&bytecode->node, &enabler->filter_bytecode_head);
+       (*bytecode)->enabler = enabler;
+       cds_list_add_tail(&(*bytecode)->node, &enabler->filter_bytecode_head);
+       /* Take ownership of bytecode */
+       *bytecode = NULL;
 }
 
-int lttng_event_enabler_attach_bytecode(struct lttng_event_enabler *event_enabler,
-               struct lttng_ust_filter_bytecode_node *bytecode)
+int lttng_event_enabler_attach_filter_bytecode(struct lttng_event_enabler *event_enabler,
+               struct lttng_ust_bytecode_node **bytecode)
 {
-       _lttng_enabler_attach_bytecode(
+       _lttng_enabler_attach_filter_bytecode(
                lttng_event_enabler_as_enabler(event_enabler), bytecode);
 
        lttng_session_lazy_sync_event_enablers(event_enabler->chan->session);
@@ -1134,14 +1462,16 @@ int lttng_event_enabler_attach_bytecode(struct lttng_event_enabler *event_enable
 
 static
 void _lttng_enabler_attach_exclusion(struct lttng_enabler *enabler,
-               struct lttng_ust_excluder_node *excluder)
+               struct lttng_ust_excluder_node **excluder)
 {
-       excluder->enabler = enabler;
-       cds_list_add_tail(&excluder->node, &enabler->excluder_head);
+       (*excluder)->enabler = enabler;
+       cds_list_add_tail(&(*excluder)->node, &enabler->excluder_head);
+       /* Take ownership of excluder */
+       *excluder = NULL;
 }
 
 int lttng_event_enabler_attach_exclusion(struct lttng_event_enabler *event_enabler,
-               struct lttng_ust_excluder_node *excluder)
+               struct lttng_ust_excluder_node **excluder)
 {
        _lttng_enabler_attach_exclusion(
                lttng_event_enabler_as_enabler(event_enabler), excluder);
@@ -1150,6 +1480,64 @@ int lttng_event_enabler_attach_exclusion(struct lttng_event_enabler *event_enabl
        return 0;
 }
 
+int lttng_event_notifier_enabler_enable(
+               struct lttng_event_notifier_enabler *event_notifier_enabler)
+{
+       lttng_event_notifier_enabler_as_enabler(event_notifier_enabler)->enabled = 1;
+       lttng_event_notifier_group_sync_enablers(event_notifier_enabler->group);
+
+       return 0;
+}
+
+int lttng_event_notifier_enabler_disable(
+               struct lttng_event_notifier_enabler *event_notifier_enabler)
+{
+       lttng_event_notifier_enabler_as_enabler(event_notifier_enabler)->enabled = 0;
+       lttng_event_notifier_group_sync_enablers(event_notifier_enabler->group);
+
+       return 0;
+}
+
+int lttng_event_notifier_enabler_attach_filter_bytecode(
+               struct lttng_event_notifier_enabler *event_notifier_enabler,
+               struct lttng_ust_bytecode_node **bytecode)
+{
+       _lttng_enabler_attach_filter_bytecode(
+               lttng_event_notifier_enabler_as_enabler(event_notifier_enabler),
+               bytecode);
+
+       lttng_event_notifier_group_sync_enablers(event_notifier_enabler->group);
+       return 0;
+}
+
+int lttng_event_notifier_enabler_attach_capture_bytecode(
+               struct lttng_event_notifier_enabler *event_notifier_enabler,
+               struct lttng_ust_bytecode_node **bytecode)
+{
+       (*bytecode)->enabler = lttng_event_notifier_enabler_as_enabler(
+                       event_notifier_enabler);
+       cds_list_add_tail(&(*bytecode)->node,
+                       &event_notifier_enabler->capture_bytecode_head);
+       /* Take ownership of bytecode */
+       *bytecode = NULL;
+       event_notifier_enabler->num_captures++;
+
+       lttng_event_notifier_group_sync_enablers(event_notifier_enabler->group);
+       return 0;
+}
+
+int lttng_event_notifier_enabler_attach_exclusion(
+               struct lttng_event_notifier_enabler *event_notifier_enabler,
+               struct lttng_ust_excluder_node **excluder)
+{
+       _lttng_enabler_attach_exclusion(
+               lttng_event_notifier_enabler_as_enabler(event_notifier_enabler),
+               excluder);
+
+       lttng_event_notifier_group_sync_enablers(event_notifier_enabler->group);
+       return 0;
+}
+
 int lttng_attach_context(struct lttng_ust_context *context_param,
                union ust_args *uargs,
                struct lttng_ctx **ctx, struct lttng_session *session)
@@ -1305,8 +1693,226 @@ void lttng_session_sync_event_enablers(struct lttng_session *session)
 
                /* Enable filters */
                cds_list_for_each_entry(runtime,
-                               &event->bytecode_runtime_head, node) {
-                       lttng_filter_sync_state(runtime);
+                               &event->filter_bytecode_runtime_head, node) {
+                       lttng_bytecode_filter_sync_state(runtime);
+               }
+       }
+       __tracepoint_probe_prune_release_queue();
+}
+
+/* Support for event notifier is introduced by probe provider major version 2. */
+static
+bool lttng_ust_probe_supports_event_notifier(struct lttng_probe_desc *probe_desc)
+{
+       return probe_desc->major >= 2;
+}
+
+static
+void lttng_create_event_notifier_if_missing(
+               struct lttng_event_notifier_enabler *event_notifier_enabler)
+{
+       struct lttng_event_notifier_group *event_notifier_group = event_notifier_enabler->group;
+       struct lttng_probe_desc *probe_desc;
+       struct cds_list_head *probe_list;
+       int i;
+
+       probe_list = lttng_get_probe_list_head();
+
+       cds_list_for_each_entry(probe_desc, probe_list, head) {
+               for (i = 0; i < probe_desc->nr_events; i++) {
+                       int ret;
+                       bool found = false;
+                       const struct lttng_event_desc *desc;
+                       struct lttng_event_notifier *event_notifier;
+                       struct cds_hlist_head *head;
+                       struct cds_hlist_node *node;
+
+                       desc = probe_desc->event_desc[i];
+
+                       if (!lttng_desc_match_enabler(desc,
+                                       lttng_event_notifier_enabler_as_enabler(event_notifier_enabler)))
+                               continue;
+
+                       /*
+                        * Given the current event_notifier group, get the bucket that
+                        * the target event_notifier would be if it was already
+                        * created.
+                        */
+                       head = borrow_hash_table_bucket(
+                               event_notifier_group->event_notifiers_ht.table,
+                               LTTNG_UST_EVENT_NOTIFIER_HT_SIZE, desc);
+
+                       cds_hlist_for_each_entry(event_notifier, node, head, hlist) {
+                               /*
+                                * Check if event_notifier already exists by checking
+                                * if the event_notifier and enabler share the same
+                                * description and id.
+                                */
+                               if (event_notifier->desc == desc &&
+                                               event_notifier->user_token == event_notifier_enabler->user_token) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+
+                       if (found)
+                               continue;
+
+                       /* Check that the probe supports event notifiers, else report the error. */
+                       if (!lttng_ust_probe_supports_event_notifier(probe_desc)) {
+                               ERR("Probe \"%s\" contains event \"%s\" which matches an enabled event notifier, "
+                                       "but its version (%u.%u) is too old and does not implement event notifiers. "
+                                       "It needs to be recompiled against a newer version of LTTng-UST, otherwise "
+                                       "this event will not generate any notification.",
+                                       probe_desc->provider,
+                                       desc->name,
+                                       probe_desc->major,
+                                       probe_desc->minor);
+                               continue;
+                       }
+                       /*
+                        * We need to create a event_notifier for this event probe.
+                        */
+                       ret = lttng_event_notifier_create(desc,
+                               event_notifier_enabler->user_token,
+                               event_notifier_enabler->error_counter_index,
+                               event_notifier_group);
+                       if (ret) {
+                               DBG("Unable to create event_notifier %s, error %d\n",
+                                       probe_desc->event_desc[i]->name, ret);
+                       }
+               }
+       }
+}
+
+/*
+ * Create event_notifiers associated with a event_notifier enabler (if not already present).
+ */
+static
+int lttng_event_notifier_enabler_ref_event_notifiers(
+               struct lttng_event_notifier_enabler *event_notifier_enabler)
+{
+       struct lttng_event_notifier_group *event_notifier_group = event_notifier_enabler->group;
+       struct lttng_event_notifier *event_notifier;
+
+        /*
+         * Only try to create event_notifiers for enablers that are enabled, the user
+         * might still be attaching filter or exclusion to the
+         * event_notifier_enabler.
+         */
+       if (!lttng_event_notifier_enabler_as_enabler(event_notifier_enabler)->enabled)
+               goto end;
+
+       /* First, ensure that probe event_notifiers are created for this enabler. */
+       lttng_create_event_notifier_if_missing(event_notifier_enabler);
+
+       /* Link the created event_notifier with its associated enabler. */
+       cds_list_for_each_entry(event_notifier, &event_notifier_group->event_notifiers_head, node) {
+               struct lttng_enabler_ref *enabler_ref;
+
+               if (!lttng_event_notifier_enabler_match_event_notifier(event_notifier_enabler, event_notifier))
+                       continue;
+
+               enabler_ref = lttng_enabler_ref(&event_notifier->enablers_ref_head,
+                       lttng_event_notifier_enabler_as_enabler(event_notifier_enabler));
+               if (!enabler_ref) {
+                       /*
+                        * If no backward ref, create it.
+                        * Add backward ref from event_notifier to enabler.
+                        */
+                       enabler_ref = zmalloc(sizeof(*enabler_ref));
+                       if (!enabler_ref)
+                               return -ENOMEM;
+
+                       enabler_ref->ref = lttng_event_notifier_enabler_as_enabler(
+                               event_notifier_enabler);
+                       cds_list_add(&enabler_ref->node,
+                               &event_notifier->enablers_ref_head);
+               }
+
+               /*
+                * Link filter bytecodes if not linked yet.
+                */
+               lttng_enabler_link_bytecode(event_notifier->desc,
+                       &event_notifier_group->ctx,
+                       &event_notifier->filter_bytecode_runtime_head,
+                       &lttng_event_notifier_enabler_as_enabler(event_notifier_enabler)->filter_bytecode_head);
+
+               /*
+                * Link capture bytecodes if not linked yet.
+                */
+               lttng_enabler_link_bytecode(event_notifier->desc,
+                       &event_notifier_group->ctx, &event_notifier->capture_bytecode_runtime_head,
+                       &event_notifier_enabler->capture_bytecode_head);
+
+               event_notifier->num_captures = event_notifier_enabler->num_captures;
+       }
+end:
+       return 0;
+}
+
+static
+void lttng_event_notifier_group_sync_enablers(struct lttng_event_notifier_group *event_notifier_group)
+{
+       struct lttng_event_notifier_enabler *event_notifier_enabler;
+       struct lttng_event_notifier *event_notifier;
+
+       cds_list_for_each_entry(event_notifier_enabler, &event_notifier_group->enablers_head, node)
+               lttng_event_notifier_enabler_ref_event_notifiers(event_notifier_enabler);
+
+       /*
+        * For each event_notifier, if at least one of its enablers is enabled,
+        * we enable the event_notifier, else we disable it.
+        */
+       cds_list_for_each_entry(event_notifier, &event_notifier_group->event_notifiers_head, node) {
+               struct lttng_enabler_ref *enabler_ref;
+               struct lttng_bytecode_runtime *runtime;
+               int enabled = 0, has_enablers_without_bytecode = 0;
+
+               /* Enable event_notifiers */
+               cds_list_for_each_entry(enabler_ref,
+                               &event_notifier->enablers_ref_head, node) {
+                       if (enabler_ref->ref->enabled) {
+                               enabled = 1;
+                               break;
+                       }
+               }
+
+               CMM_STORE_SHARED(event_notifier->enabled, enabled);
+               /*
+                * Sync tracepoint registration with event_notifier enabled
+                * state.
+                */
+               if (enabled) {
+                       if (!event_notifier->registered)
+                               register_event_notifier(event_notifier);
+               } else {
+                       if (event_notifier->registered)
+                               unregister_event_notifier(event_notifier);
+               }
+
+               /* Check if has enablers without bytecode enabled */
+               cds_list_for_each_entry(enabler_ref,
+                               &event_notifier->enablers_ref_head, node) {
+                       if (enabler_ref->ref->enabled
+                                       && cds_list_empty(&enabler_ref->ref->filter_bytecode_head)) {
+                               has_enablers_without_bytecode = 1;
+                               break;
+                       }
+               }
+               event_notifier->has_enablers_without_bytecode =
+                       has_enablers_without_bytecode;
+
+               /* Enable filters */
+               cds_list_for_each_entry(runtime,
+                               &event_notifier->filter_bytecode_runtime_head, node) {
+                       lttng_bytecode_filter_sync_state(runtime);
+               }
+
+               /* Enable captures. */
+               cds_list_for_each_entry(runtime,
+                               &event_notifier->capture_bytecode_runtime_head, node) {
+                       lttng_bytecode_capture_sync_state(runtime);
                }
        }
        __tracepoint_probe_prune_release_queue();
@@ -1367,3 +1973,31 @@ void lttng_ust_context_set_session_provider(const char *name,
                }
        }
 }
+
+/*
+ * Update all event_notifier groups with the given app context.
+ * Called with ust lock held.
+ * This is invoked when an application context gets loaded/unloaded. It
+ * ensures the context callbacks are in sync with the application
+ * context (either app context callbacks, or dummy callbacks).
+ */
+void lttng_ust_context_set_event_notifier_group_provider(const char *name,
+               size_t (*get_size)(struct lttng_ctx_field *field, size_t offset),
+               void (*record)(struct lttng_ctx_field *field,
+                       struct lttng_ust_lib_ring_buffer_ctx *ctx,
+                       struct lttng_channel *chan),
+               void (*get_value)(struct lttng_ctx_field *field,
+                       struct lttng_ctx_value *value))
+{
+       struct lttng_event_notifier_group *event_notifier_group;
+
+       cds_list_for_each_entry(event_notifier_group, &event_notifier_groups, node) {
+               int ret;
+
+               ret = lttng_ust_context_set_provider_rcu(
+                               &event_notifier_group->ctx,
+                               name, get_size, record, get_value);
+               if (ret)
+                       abort();
+       }
+}
This page took 0.034757 seconds and 4 git commands to generate.