Implement event notifier
authorFrancis Deslauriers <francis.deslauriers@efficios.com>
Fri, 29 Nov 2019 21:35:45 +0000 (16:35 -0500)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Thu, 26 Nov 2020 18:20:41 +0000 (13:20 -0500)
The event notifier feature allows the user to get notify through a pipe
that an callsite was reached. It's really similar to a regular UST event
in that firing can be controlled by filters and exclusions. It diverges
because firing a event notifier does not end up writing to the tracing
buffers. In fact, event notifiers live outside of any sessions (i.e. no
session is needed).

Definitions
===========
A `Event notifier` is defined as an event description (event name, log level,
etc.) and a unique event notifier token.

A `Event notifier group` is a set of event notifiers sharing the same
pipe to the liblttng-ust-ctl user.

Sequence of operations
======================
  event_notifier_group_handle = ustctl_create_event_notifier_group(notification_fd)
  event_notifier_handle = ustctl_create_event_notifier(event_notifier_group_handle, event_notifier_id);
  ustctl_set_filter(event_notifier_handle, filter)
  ustctl_set_exclusion(event_notifier_handle, exclusion)
  ustctl_enable(event_notifier_handle)
  ...
  ustctl_disable(event_notifier_handle)
  ustctl_release_object(event_notifier_handle)
  ustctl_release_object(event_notifier_group_handle)

Highlevel changes
=================
- Add the event notifier probe generation macros,
- Add event notifier enabling/disabling machinery,
- Add event notifier filter machinery,
- Add event notifier exclusion machinery,
- Expose ABI structures
- Expose ABI enums
- Expose ABI cmds
- Expose ustctl functions

Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Change-Id: I493d784cc6830cd6c7979f6f08b209521676f05c

17 files changed:
include/lttng/ust-abi.h
include/lttng/ust-ctl.h
include/lttng/ust-events.h
include/lttng/ust-tracepoint-event.h
include/ust-comm.h
liblttng-ust-comm/lttng-ust-comm.c
liblttng-ust-ctl/ustctl.c
liblttng-ust/Makefile.am
liblttng-ust/context-provider-internal.h [new file with mode: 0644]
liblttng-ust/event-notifier-notification.c [new file with mode: 0644]
liblttng-ust/lttng-context-provider.c
liblttng-ust/lttng-events.c
liblttng-ust/lttng-filter.c
liblttng-ust/lttng-probes.c
liblttng-ust/lttng-ust-abi.c
liblttng-ust/lttng-ust-comm.c
liblttng-ust/ust-events-internal.h

index 78aae7c73cbd265c651a3bac753273ee0a8e6665..302e6a36b80cb441cca42d7a3d6a297f3fd099ea 100644 (file)
@@ -120,6 +120,18 @@ struct lttng_ust_event {
        } u;
 } LTTNG_PACKED;
 
+#define LTTNG_UST_EVENT_NOTIFIER_PADDING1      16
+struct lttng_ust_event_notifier {
+       struct lttng_ust_event event;
+       char padding[LTTNG_UST_EVENT_NOTIFIER_PADDING1];
+} LTTNG_PACKED;
+
+#define LTTNG_EVENT_NOTIFIER_NOTIFICATION_PADDING 32
+struct lttng_ust_event_notifier_notification {
+       uint64_t token;
+       char padding[LTTNG_EVENT_NOTIFIER_NOTIFICATION_PADDING];
+} LTTNG_PACKED;
+
 enum lttng_ust_field_type {
        LTTNG_UST_FIELD_OTHER                   = 0,
        LTTNG_UST_FIELD_INTEGER                 = 1,
@@ -218,6 +230,8 @@ enum lttng_ust_object_type {
        LTTNG_UST_OBJECT_TYPE_STREAM = 1,
        LTTNG_UST_OBJECT_TYPE_EVENT = 2,
        LTTNG_UST_OBJECT_TYPE_CONTEXT = 3,
+       LTTNG_UST_OBJECT_TYPE_EVENT_NOTIFIER_GROUP = 4,
+       LTTNG_UST_OBJECT_TYPE_EVENT_NOTIFIER = 5,
 };
 
 #define LTTNG_UST_OBJECT_DATA_PADDING1 32
@@ -292,6 +306,7 @@ struct lttng_ust_event_exclusion {
 #define LTTNG_UST_WAIT_QUIESCENT               _UST_CMD(0x43)
 #define LTTNG_UST_REGISTER_DONE                        _UST_CMD(0x44)
 #define LTTNG_UST_TRACEPOINT_FIELD_LIST                _UST_CMD(0x45)
+#define LTTNG_UST_EVENT_NOTIFIER_GROUP_CREATE  _UST_CMD(0x46)
 
 /* Session FD commands */
 #define LTTNG_UST_CHANNEL                      \
@@ -323,6 +338,10 @@ struct lttng_ust_event_exclusion {
 #define LTTNG_UST_FILTER                       _UST_CMD(0xA0)
 #define LTTNG_UST_EXCLUSION                    _UST_CMD(0xA1)
 
+/* Event notifier group commands */
+#define LTTNG_UST_EVENT_NOTIFIER_CREATE                \
+       _UST_CMDW(0xB0, struct lttng_ust_event_notifier)
+
 #define LTTNG_UST_ROOT_HANDLE  0
 
 struct lttng_ust_obj;
@@ -342,6 +361,9 @@ union ust_args {
        struct {
                char *ctxname;
        } app_context;
+       struct {
+               int event_notifier_notif_fd;
+       } event_notifier_handle;
 };
 
 struct lttng_ust_objd_ops {
index 19fba726b56c049a141b5259b2105d1f76d6fadb..a4d54c42bbdec1506133c53f7f92cc315d5d137d 100644 (file)
@@ -101,6 +101,28 @@ int ustctl_disable(int sock, struct lttng_ust_object_data *object);
 int ustctl_start_session(int sock, int handle);
 int ustctl_stop_session(int sock, int handle);
 
+/*
+ * ustctl_create_event notifier_group creates a event notifier group. It
+ * establishes the connection with the application by providing a file
+ * descriptor of the pipe to be used by the application when a event notifier
+ * of that group is fired. It returns a handle to be used when creating event
+ * notifier in that group.
+ */
+int ustctl_create_event_notifier_group(int sock, int pipe_fd,
+               struct lttng_ust_object_data **event_notifier_group);
+
+/*
+ * ustctl_create_event notifier creates a event notifier in a event notifier
+ * group giving a event notifier description and a event notifier group handle.
+ * It returns a event notifier handle to be used when enabling the event
+ * notifier, attaching filter, attaching exclusion, and disabling the event
+ * notifier.
+ */
+int ustctl_create_event_notifier(int sock,
+               struct lttng_ust_event_notifier *event_notifier,
+               struct lttng_ust_object_data *event_notifier_group,
+               struct lttng_ust_object_data **event_notifier_data);
+
 /*
  * ustctl_tracepoint_list returns a tracepoint list handle, or negative
  * error value.
index 1e359b004791d9225fbbb15cff33e56b392ca854..d89ddea6147a5659b694af6b53d11dba71861a85 100644 (file)
@@ -360,6 +360,7 @@ struct lttng_event_desc {
        union {
                struct {
                        const char **model_emf_uri;
+                       void (*event_notifier_callback)(void);
                } ext;
                char padding[LTTNG_UST_EVENT_DESC_PADDING];
        } u;
@@ -446,14 +447,20 @@ struct lttng_bytecode_runtime {
        int link_failed;
        struct cds_list_head node;      /* list of bytecode runtime in event */
        /*
-        * Pointer to a `struct lttng_session`-owned and URCU-protected
-        * pointer.
+        * Pointer to a URCU-protected pointer owned by an `struct
+        * lttng_session`or `struct lttng_event_notifier_group`.
         */
        struct lttng_ctx **pctx;
 };
 
 /*
- * Objects in a linked-list of enablers, owned by an event.
+ * Objects in a linked-list of enablers, owned by an event or event_notifier.
+ * This is used because an event (or a event_notifier) can be enabled by more
+ * than one enabler and we want a quick way to iterate over all enablers of an
+ * object.
+ *
+ * For example, event rules "my_app:a*" and "my_app:ab*" will both match the
+ * event with the name "my_app:abc".
  */
 struct lttng_enabler_ref {
        struct cds_list_head node;              /* enabler ref list */
@@ -492,6 +499,19 @@ struct lttng_event {
        int registered;                 /* has reg'd tracepoint probe */
 };
 
+struct lttng_event_notifier {
+       uint64_t user_token;
+       int enabled;
+       int registered;                 /* has reg'd tracepoint probe */
+       struct cds_list_head filter_bytecode_runtime_head;
+       int has_enablers_without_bytecode;
+       struct cds_list_head enablers_ref_head;
+       const struct lttng_event_desc *desc;
+       struct cds_hlist_node hlist;    /* hashtable of event_notifiers */
+       struct cds_list_head node;      /* event_notifier list in session */
+       struct lttng_event_notifier_group *group; /* weak ref */
+};
+
 struct lttng_enum {
        const struct lttng_enum_desc *desc;
        struct lttng_session *session;
@@ -600,6 +620,12 @@ struct lttng_ust_event_ht {
        struct cds_hlist_head table[LTTNG_UST_EVENT_HT_SIZE];
 };
 
+#define LTTNG_UST_EVENT_NOTIFIER_HT_BITS               12
+#define LTTNG_UST_EVENT_NOTIFIER_HT_SIZE               (1U << LTTNG_UST_EVENT_NOTIFIER_HT_BITS)
+struct lttng_ust_event_notifier_ht {
+       struct cds_hlist_head table[LTTNG_UST_EVENT_NOTIFIER_HT_SIZE];
+};
+
 #define LTTNG_UST_ENUM_HT_BITS         12
 #define LTTNG_UST_ENUM_HT_SIZE         (1U << LTTNG_UST_ENUM_HT_BITS)
 
@@ -640,6 +666,17 @@ struct lttng_session {
        struct lttng_ctx *ctx;                  /* contexts for filters. */
 };
 
+struct lttng_event_notifier_group {
+       int objd;
+       void *owner;
+       int notification_fd;
+       struct cds_list_head node;              /* Event notifier group handle list */
+       struct cds_list_head enablers_head;
+       struct cds_list_head event_notifiers_head;      /* list of event_notifiers */
+       struct lttng_ust_event_notifier_ht event_notifiers_ht; /* hashtable of event_notifiers */
+       struct lttng_ctx *ctx;                  /* contexts for filters. */
+};
+
 struct lttng_transport {
        char *name;
        struct cds_list_head node;
@@ -653,6 +690,9 @@ int lttng_session_disable(struct lttng_session *session);
 int lttng_session_statedump(struct lttng_session *session);
 void lttng_session_destroy(struct lttng_session *session);
 
+void lttng_event_notifier_notification_send(
+               struct lttng_event_notifier *event_notifier);
+
 struct lttng_channel *lttng_channel_create(struct lttng_session *session,
                                       const char *transport_name,
                                       void *buf_addr,
index 622befe0be71183a86ce2502cd1b6670fde4f1f0..c9c7592938427bfd228d3dd9a031189fbfc9aeca 100644 (file)
@@ -433,6 +433,24 @@ static void __event_probe__##_provider##___##_name(_TP_ARGS_DATA_PROTO(_args));
 
 #include TRACEPOINT_INCLUDE
 
+/*
+ * Stage 2.1 of tracepoint event generation.
+ *
+ * Create probe event notifier callback prototypes.
+ */
+
+/* Reset all macros within TRACEPOINT_EVENT */
+#include <lttng/ust-tracepoint-event-reset.h>
+
+#undef TP_ARGS
+#define TP_ARGS(...) __VA_ARGS__
+
+#undef TRACEPOINT_EVENT_CLASS
+#define TRACEPOINT_EVENT_CLASS(_provider, _name, _args, _fields)               \
+static void __event_notifier_probe__##_provider##___##_name(_TP_ARGS_DATA_PROTO(_args));
+
+#include TRACEPOINT_INCLUDE
+
 /*
  * Stage 3.0 of tracepoint event generation.
  *
@@ -913,6 +931,48 @@ static const char __tp_event_signature___##_provider##___##_name[] =       \
 
 #undef _TP_EXTRACT_STRING2
 
+/*
+ * Stage 5.2 of tracepoint event generation.
+ *
+ * Create the event notifier probe function.
+ */
+#undef TRACEPOINT_EVENT_CLASS
+#define TRACEPOINT_EVENT_CLASS(_provider, _name, _args, _fields)             \
+static lttng_ust_notrace                                                     \
+void __event_notifier_probe__##_provider##___##_name(_TP_ARGS_DATA_PROTO(_args));    \
+static                                                                       \
+void __event_notifier_probe__##_provider##___##_name(_TP_ARGS_DATA_PROTO(_args))     \
+{                                                                            \
+       struct lttng_event_notifier *__event_notifier = (struct lttng_event_notifier *) __tp_data; \
+       const size_t __num_fields = _TP_ARRAY_SIZE(__event_fields___##_provider##___##_name) - 1;\
+       union {                                                               \
+               size_t __dynamic_len[__num_fields];                           \
+               char __filter_stack_data[2 * sizeof(unsigned long) * __num_fields]; \
+       } __stackvar;                                                         \
+       if (caa_unlikely(!CMM_ACCESS_ONCE(__event_notifier->enabled)))        \
+               return;                                                       \
+       if (caa_unlikely(!TP_RCU_LINK_TEST()))                                \
+               return;                                                       \
+       if (caa_unlikely(!cds_list_empty(&__event_notifier->filter_bytecode_runtime_head))) { \
+               struct lttng_bytecode_runtime *__filter_bc_runtime;                    \
+               int __filter_record = __event_notifier->has_enablers_without_bytecode; \
+                                                                             \
+               __event_prepare_filter_stack__##_provider##___##_name(__stackvar.__filter_stack_data, \
+                       _TP_ARGS_DATA_VAR(_args));                            \
+               tp_list_for_each_entry_rcu(__filter_bc_runtime, &__event_notifier->filter_bytecode_runtime_head, node) { \
+                       if (caa_unlikely(__filter_bc_runtime->filter(__filter_bc_runtime,            \
+                                       __stackvar.__filter_stack_data) & LTTNG_FILTER_RECORD_FLAG)) \
+                               __filter_record = 1;                          \
+               }                                                             \
+               if (caa_likely(!__filter_record))                             \
+                       return;                                               \
+       }                                                                     \
+                                                                             \
+       lttng_event_notifier_notification_send(__event_notifier);             \
+}
+
+#include TRACEPOINT_INCLUDE
+
 /*
  * Stage 6 of tracepoint event generation.
  *
@@ -1008,6 +1068,7 @@ static const struct lttng_event_desc __event_desc___##_provider##_##_name = {
        .u = {                                                                 \
            .ext = {                                                           \
                  .model_emf_uri = &__ref_model_emf_uri___##_provider##___##_name, \
+                 .event_notifier_callback = (void (*)(void)) &__event_notifier_probe__##_provider##___##_template,\
                },                                                             \
        },                                                                     \
 };
index ed241e30468734df5565fa46c0fab7b4d0f50730..c9c8ca419f6a4aaffd4cf30d2834820b6b55c58a 100644 (file)
@@ -88,6 +88,7 @@ struct ustcomm_ust_msg {
        uint32_t cmd;
        char padding[USTCOMM_MSG_PADDING1];
        union {
+               struct lttng_ust_event_notifier event_notifier;
                struct lttng_ust_channel channel;
                struct lttng_ust_stream stream;
                struct lttng_ust_event event;
@@ -219,6 +220,8 @@ ssize_t ustcomm_recv_channel_from_sessiond(int sock,
 int ustcomm_recv_stream_from_sessiond(int sock,
                uint64_t *memory_map_size,
                int *shm_fd, int *wakeup_fd);
+ssize_t ustcomm_recv_event_notifier_notif_fd_from_sessiond(int sock,
+               int *event_notifier_notif_fd);
 
 /*
  * Returns 0 on success, negative error value on error.
index 2447c9a938492f78f491a5868b7102f07ef157a2..9786abe7cb1b2767da8aa3a9535c178ac9a02774 100644 (file)
@@ -668,6 +668,46 @@ error_check:
        return len;
 }
 
+ssize_t ustcomm_recv_event_notifier_notif_fd_from_sessiond(int sock,
+               int *_event_notifier_notif_fd)
+{
+       ssize_t nr_fd;
+       int event_notifier_notif_fd, ret;
+
+       /* Receive event_notifier notification fd */
+       lttng_ust_lock_fd_tracker();
+       nr_fd = ustcomm_recv_fds_unix_sock(sock, &event_notifier_notif_fd, 1);
+       if (nr_fd <= 0) {
+               lttng_ust_unlock_fd_tracker();
+               if (nr_fd < 0) {
+                       ret = nr_fd;
+                       goto error;
+               } else {
+                       ret = -EIO;
+                       goto error;
+               }
+       }
+
+       ret = lttng_ust_add_fd_to_tracker(event_notifier_notif_fd);
+       if (ret < 0) {
+               ret = close(event_notifier_notif_fd);
+               if (ret) {
+                       PERROR("close on event_notifier notif fd");
+               }
+               ret = -EIO;
+               lttng_ust_unlock_fd_tracker();
+               goto error;
+       }
+
+       *_event_notifier_notif_fd = ret;
+       lttng_ust_unlock_fd_tracker();
+
+       ret = nr_fd;
+
+error:
+       return ret;
+}
+
 int ustcomm_recv_stream_from_sessiond(int sock,
                uint64_t *memory_map_size,
                int *shm_fd, int *wakeup_fd)
index 01214eca60129650c12136205b294b9e1e1993c6..744f66ba6dc9824c00329540d41c88294644bb2c 100644 (file)
@@ -135,6 +135,8 @@ int ustctl_release_object(int sock, struct lttng_ust_object_data *data)
                break;
        case LTTNG_UST_OBJECT_TYPE_EVENT:
        case LTTNG_UST_OBJECT_TYPE_CONTEXT:
+       case LTTNG_UST_OBJECT_TYPE_EVENT_NOTIFIER_GROUP:
+       case LTTNG_UST_OBJECT_TYPE_EVENT_NOTIFIER:
                break;
        default:
                assert(0);
@@ -421,6 +423,97 @@ int ustctl_stop_session(int sock, int handle)
        return ustctl_disable(sock, &obj);
 }
 
+int ustctl_create_event_notifier_group(int sock, int pipe_fd,
+               struct lttng_ust_object_data **_event_notifier_group_data)
+{
+       struct lttng_ust_object_data *event_notifier_group_data;
+       struct ustcomm_ust_msg lum;
+       struct ustcomm_ust_reply lur;
+       ssize_t len;
+       int ret;
+
+       if (!_event_notifier_group_data)
+               return -EINVAL;
+
+       event_notifier_group_data = zmalloc(sizeof(*event_notifier_group_data));
+       if (!event_notifier_group_data)
+               return -ENOMEM;
+
+       event_notifier_group_data->type = LTTNG_UST_OBJECT_TYPE_EVENT_NOTIFIER_GROUP;
+
+       memset(&lum, 0, sizeof(lum));
+       lum.handle = LTTNG_UST_ROOT_HANDLE;
+       lum.cmd = LTTNG_UST_EVENT_NOTIFIER_GROUP_CREATE;
+
+       ret = ustcomm_send_app_msg(sock, &lum);
+       if (ret)
+               goto error;
+
+       /* Send event_notifier notification pipe. */
+       len = ustcomm_send_fds_unix_sock(sock, &pipe_fd, 1);
+       if (len <= 0) {
+               ret = len;
+               goto error;
+       }
+
+       ret = ustcomm_recv_app_reply(sock, &lur, lum.handle, lum.cmd);
+       if (ret)
+               goto error;
+
+       event_notifier_group_data->handle = lur.ret_val;
+       DBG("received event_notifier group handle %d", event_notifier_group_data->handle);
+
+       *_event_notifier_group_data = event_notifier_group_data;
+
+       ret = 0;
+       goto end;
+error:
+       free(event_notifier_group_data);
+
+end:
+       return ret;
+}
+
+int ustctl_create_event_notifier(int sock, struct lttng_ust_event_notifier *event_notifier,
+               struct lttng_ust_object_data *event_notifier_group,
+               struct lttng_ust_object_data **_event_notifier_data)
+{
+       struct ustcomm_ust_msg lum;
+       struct ustcomm_ust_reply lur;
+       struct lttng_ust_object_data *event_notifier_data;
+       int ret;
+
+       if (!event_notifier_group || !_event_notifier_data)
+               return -EINVAL;
+
+       event_notifier_data = zmalloc(sizeof(*event_notifier_data));
+       if (!event_notifier_data)
+               return -ENOMEM;
+
+       event_notifier_data->type = LTTNG_UST_OBJECT_TYPE_EVENT_NOTIFIER;
+
+       memset(&lum, 0, sizeof(lum));
+       lum.handle = event_notifier_group->handle;
+       lum.cmd = LTTNG_UST_EVENT_NOTIFIER_CREATE;
+
+       strncpy(lum.u.event_notifier.event.name, event_notifier->event.name,
+               LTTNG_UST_SYM_NAME_LEN);
+       lum.u.event_notifier.event.instrumentation = event_notifier->event.instrumentation;
+       lum.u.event_notifier.event.loglevel_type = event_notifier->event.loglevel_type;
+       lum.u.event_notifier.event.loglevel = event_notifier->event.loglevel;
+       lum.u.event_notifier.event.token = event_notifier->event.token;
+       ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+       if (ret) {
+               free(event_notifier_data);
+               return ret;
+       }
+       event_notifier_data->handle = lur.ret_val;
+       DBG("received event_notifier handle %u", event_notifier_data->handle);
+       *_event_notifier_data = event_notifier_data;
+
+       return ret;
+}
+
 int ustctl_tracepoint_list(int sock)
 {
        struct ustcomm_ust_msg lum;
index d85cf60273120969d7a69015c3e33484c21e1eed..f2cc835e11df5e6a3736accb87e4aac417eedb02 100644 (file)
@@ -60,6 +60,7 @@ liblttng_ust_runtime_la_SOURCES = \
        lttng-ust-statedump-provider.h \
        ust_lib.c \
        ust_lib.h \
+       context-provider-internal.h \
        tracepoint-internal.h \
        ust-events-internal.h \
        clock.h \
@@ -75,6 +76,7 @@ liblttng_ust_runtime_la_SOURCES = \
        getenv.h \
        string-utils.c \
        string-utils.h \
+       event-notifier-notification.c \
        ns.h \
        creds.h
 
diff --git a/liblttng-ust/context-provider-internal.h b/liblttng-ust/context-provider-internal.h
new file mode 100644 (file)
index 0000000..dacfb39
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _LTTNG_UST_CONTEXT_PROVIDER_INTERNAL_H
+#define _LTTNG_UST_CONTEXT_PROVIDER_INTERNAL_H
+
+/*
+ * Copyright 2019 - Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stddef.h>
+#include <lttng/ust-events.h>
+
+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));
+
+#endif /* _LTTNG_UST_CONTEXT_PROVIDER_INTERNAL_H */
diff --git a/liblttng-ust/event-notifier-notification.c b/liblttng-ust/event-notifier-notification.c
new file mode 100644 (file)
index 0000000..3df0643
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * event-notifier-notification.c
+ *
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@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
+ */
+
+#define _LGPL_SOURCE
+
+#include <errno.h>
+#include <lttng/ust-events.h>
+#include <usterr-signal-safe.h>
+
+#include "share.h"
+
+void lttng_event_notifier_notification_send(
+               struct lttng_event_notifier *event_notifier)
+{
+       /*
+        * We want this write to be atomic AND non-blocking, meaning that we
+        * want to write either everything OR nothing.
+        * According to `pipe(7)`, writes that are smaller that the `PIPE_BUF`
+        * value must be atomic, so we assert that the message we send is less
+        * than PIPE_BUF.
+        */
+       struct lttng_ust_event_notifier_notification notif;
+       ssize_t ret;
+
+       assert(event_notifier);
+       assert(event_notifier->group);
+       assert(sizeof(notif) <= PIPE_BUF);
+
+       notif.token = event_notifier->user_token;
+
+       ret = patient_write(event_notifier->group->notification_fd, &notif,
+               sizeof(notif));
+       if (ret == -1) {
+               if (errno == EAGAIN) {
+                       DBG("Cannot send event notifier notification without blocking: %s",
+                               strerror(errno));
+               } else {
+                       DBG("Error to sending event notifier notification: %s",
+                               strerror(errno));
+                       abort();
+               }
+       }
+}
index 50f73c6253cf340d90485540ce154dfdafd24d72..fe831366779a0332531fcfad63769742481549a0 100644 (file)
 #include <unistd.h>
 
 #include <lttng/ust-context-provider.h>
+
 #include "lttng-tracer-core.h"
 #include "jhash.h"
+#include "context-provider-internal.h"
 #include <helper.h>
 
 #define CONTEXT_PROVIDER_HT_BITS       12
@@ -90,9 +92,14 @@ int lttng_ust_context_provider_register(struct lttng_ust_context_provider *provi
        hash = jhash(provider->name, name_len, 0);
        head = &context_provider_ht.table[hash & (CONTEXT_PROVIDER_HT_SIZE - 1)];
        cds_hlist_add_head(&provider->node, head);
+
        lttng_ust_context_set_session_provider(provider->name,
                provider->get_size, provider->record,
                provider->get_value);
+
+       lttng_ust_context_set_event_notifier_group_provider(provider->name,
+               provider->get_size, provider->record,
+               provider->get_value);
 end:
        ust_unlock();
        return ret;
@@ -107,6 +114,11 @@ void lttng_ust_context_provider_unregister(struct lttng_ust_context_provider *pr
        lttng_ust_context_set_session_provider(provider->name,
                lttng_ust_dummy_get_size, lttng_ust_dummy_record,
                lttng_ust_dummy_get_value);
+
+       lttng_ust_context_set_event_notifier_group_provider(provider->name,
+               lttng_ust_dummy_get_size, lttng_ust_dummy_record,
+               lttng_ust_dummy_get_value);
+
        cds_hlist_del(&provider->node);
 end:
        ust_unlock();
index 0c508af7bc9a263f3a4f1dd39ed5e535b906d234..98def9a59ef2c023c5bf171400ac0f81f2102c21 100644 (file)
 
 #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 <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 +50,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"
@@ -63,6 +66,7 @@
 #include "wait.h"
 #include "../libringbuffer/shm.h"
 #include "jhash.h"
+#include <lttng/ust-abi.h>
 
 /*
  * All operations within this file are called by the communication
@@ -70,6 +74,7 @@
  */
 
 static CDS_LIST_HEAD(sessions);
+static CDS_LIST_HEAD(event_notifier_groups);
 
 struct cds_list_head *_lttng_get_sessions(void)
 {
@@ -77,6 +82,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 +91,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);
 
 /*
@@ -159,6 +169,25 @@ struct lttng_session *lttng_session_create(void)
        return session;
 }
 
+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;
+
+       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 +224,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 +255,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 +280,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;
@@ -250,6 +319,49 @@ 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);
+
+       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);
+
+       /* 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)
 {
@@ -273,6 +385,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)
@@ -662,6 +787,69 @@ socket_error:
        return ret;
 }
 
+static
+int lttng_event_notifier_create(const struct lttng_event_desc *desc,
+               uint64_t token,
+               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 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->enablers_ref_head);
+       event_notifier->desc = desc;
+
+       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 +947,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,
@@ -835,7 +1038,9 @@ void lttng_create_event_if_missing(struct lttng_event_enabler *event_enabler)
 
 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_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;
@@ -850,6 +1055,8 @@ void probe_provider_event_for_each(struct lttng_probe_desc *provider_desc,
         */
        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;
@@ -876,6 +1083,28 @@ void probe_provider_event_for_each(struct lttng_probe_desc *provider_desc,
                                }
                        }
                }
+
+               /*
+                * 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 event_notifiers in the hashtable bucket and
+                        * iterate to find the event_notifier matching this
+                        * descriptor.
+                        */
+                       head = borrow_hash_table_bucket(
+                               event_notifier_group->event_notifiers_ht.table,
+                               LTTNG_UST_EVENT_NOTIFIER_HT_SIZE, event_desc);
+
+                       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;
+                               }
+                       }
+               }
        }
 }
 
@@ -932,7 +1161,8 @@ void lttng_probe_provider_unregister_events(
         * 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);
+       probe_provider_event_for_each(provider_desc, _unregister_event,
+               _lttng_event_notifier_unregister);
 
        /* Wait for grace period. */
        synchronize_trace();
@@ -943,11 +1173,12 @@ void lttng_probe_provider_unregister_events(
         * 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);
+       probe_provider_event_for_each(provider_desc, _event_enum_destroy,
+               _lttng_event_notifier_destroy);
 }
 
 /*
- * 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.
  */
 static
@@ -1014,6 +1245,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
@@ -1107,6 +1348,43 @@ 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->base.excluder_head);
+
+       event_notifier_enabler->user_token = event_notifier_param->event.token;
+
+       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;
@@ -1159,6 +1437,48 @@ 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_bytecode(
+               struct lttng_event_notifier_enabler *event_notifier_enabler,
+               struct lttng_ust_filter_bytecode_node *bytecode)
+{
+       _lttng_enabler_attach_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_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)
@@ -1321,6 +1641,187 @@ void lttng_session_sync_event_enablers(struct lttng_session *session)
        __tracepoint_probe_prune_release_queue();
 }
 
+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;
+
+                       /*
+                        * We need to create a event_notifier for this event probe.
+                        */
+                       ret = lttng_event_notifier_create(desc,
+                               event_notifier_enabler->user_token,
+                               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));
+       }
+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_filter_sync_state(runtime);
+               }
+       }
+       __tracepoint_probe_prune_release_queue();
+}
+
 /*
  * Apply enablers to session events, adding events to session if need
  * be. It is required after each modification applied to an active
@@ -1376,3 +1877,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();
+       }
+}
index 5ee12d26e8fb9fa08c91c437652b7b31d2d46a57..70e489fc16841b27d4c7e5fa61dc4d53e61fe2d0 100644 (file)
@@ -584,3 +584,9 @@ void lttng_free_event_filter_runtime(struct lttng_event *event)
 {
        free_filter_runtime(&event->bytecode_runtime_head);
 }
+
+void lttng_free_event_notifier_filter_runtime(
+               struct lttng_event_notifier *event_notifier)
+{
+       free_filter_runtime(&event_notifier->filter_bytecode_runtime_head);
+}
index 9b3bacc41f7c5559789f1501e7b47724dc8ae19e..e328e2c0f34ddf3219ccdae1e680f659d596301e 100644 (file)
@@ -35,6 +35,7 @@
 #include "lttng-tracer-core.h"
 #include "jhash.h"
 #include "error.h"
+#include "ust-events-internal.h"
 
 /*
  * probe list is protected by ust_lock()/ust_unlock().
@@ -204,6 +205,8 @@ int lttng_probe_register(struct lttng_probe_desc *desc)
        if (lttng_session_active())
                fixup_lazy_probes();
 
+       lttng_fix_pending_event_notifiers();
+
        ust_unlock();
        return ret;
 }
index fb124c2a6a263300dbc13d62310e3ad701d75823..d5877c3d80591ad7ef408c597faaf426b9a6bc47 100644 (file)
@@ -38,6 +38,7 @@
  */
 
 #define _LGPL_SOURCE
+#include <fcntl.h>
 #include <stdint.h>
 #include <unistd.h>
 
@@ -280,9 +281,11 @@ void lttng_ust_objd_table_owner_cleanup(void *owner)
  */
 
 static const struct lttng_ust_objd_ops lttng_ops;
+static const struct lttng_ust_objd_ops lttng_event_notifier_group_ops;
 static const struct lttng_ust_objd_ops lttng_session_ops;
 static const struct lttng_ust_objd_ops lttng_channel_ops;
 static const struct lttng_ust_objd_ops lttng_event_enabler_ops;
+static const struct lttng_ust_objd_ops lttng_event_notifier_enabler_ops;
 static const struct lttng_ust_objd_ops lttng_tracepoint_list_ops;
 static const struct lttng_ust_objd_ops lttng_tracepoint_field_list_ops;
 
@@ -340,6 +343,53 @@ long lttng_abi_tracer_version(int objd,
        return 0;
 }
 
+static
+int lttng_abi_event_notifier_send_fd(void *owner, int event_notifier_notif_fd)
+{
+       struct lttng_event_notifier_group *event_notifier_group;
+       int event_notifier_group_objd, ret, fd_flag, close_ret;
+
+       event_notifier_group = lttng_event_notifier_group_create();
+       if (!event_notifier_group)
+               return -ENOMEM;
+
+       /*
+        * Set this file descriptor as NON-BLOCKING.
+        */
+       fd_flag = fcntl(event_notifier_notif_fd, F_GETFL);
+
+       fd_flag |= O_NONBLOCK;
+
+       ret = fcntl(event_notifier_notif_fd, F_SETFL, fd_flag);
+       if (ret) {
+               ret = -errno;
+               goto fd_error;
+       }
+
+       event_notifier_group_objd = objd_alloc(event_notifier_group,
+               &lttng_event_notifier_group_ops, owner, "event_notifier_group");
+       if (event_notifier_group_objd < 0) {
+               ret = event_notifier_group_objd;
+               goto objd_error;
+       }
+
+       event_notifier_group->objd = event_notifier_group_objd;
+       event_notifier_group->owner = owner;
+       event_notifier_group->notification_fd = event_notifier_notif_fd;
+
+       return event_notifier_group_objd;
+
+objd_error:
+       lttng_event_notifier_group_destroy(event_notifier_group);
+fd_error:
+       close_ret = close(event_notifier_notif_fd);
+       if (close_ret) {
+               PERROR("close");
+       }
+
+       return ret;
+}
+
 static
 long lttng_abi_add_context(int objd,
        struct lttng_ust_context *context_param,
@@ -389,6 +439,9 @@ long lttng_cmd(int objd, unsigned int cmd, unsigned long arg,
        case LTTNG_UST_WAIT_QUIESCENT:
                synchronize_trace();
                return 0;
+       case LTTNG_UST_EVENT_NOTIFIER_GROUP_CREATE:
+               return lttng_abi_event_notifier_send_fd(owner,
+                       uargs->event_notifier_handle.event_notifier_notif_fd);
        default:
                return -EINVAL;
        }
@@ -615,6 +668,129 @@ static const struct lttng_ust_objd_ops lttng_session_ops = {
        .cmd = lttng_session_cmd,
 };
 
+static int lttng_ust_event_notifier_enabler_create(int event_notifier_group_obj,
+               void *owner, struct lttng_ust_event_notifier *event_notifier_param,
+               enum lttng_enabler_format_type type)
+{
+       struct lttng_event_notifier_group *event_notifier_group =
+               objd_private(event_notifier_group_obj);
+       struct lttng_event_notifier_enabler *event_notifier_enabler;
+       int event_notifier_objd, ret;
+
+       event_notifier_param->event.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
+       event_notifier_objd = objd_alloc(NULL, &lttng_event_notifier_enabler_ops, owner,
+               "event_notifier enabler");
+       if (event_notifier_objd < 0) {
+               ret = event_notifier_objd;
+               goto objd_error;
+       }
+
+       event_notifier_enabler = lttng_event_notifier_enabler_create(
+               event_notifier_group, type, event_notifier_param);
+       if (!event_notifier_enabler) {
+               ret = -ENOMEM;
+               goto event_notifier_error;
+       }
+
+       objd_set_private(event_notifier_objd, event_notifier_enabler);
+       /* The event_notifier holds a reference on the event_notifier group. */
+       objd_ref(event_notifier_enabler->group->objd);
+
+       return event_notifier_objd;
+
+event_notifier_error:
+       {
+               int err;
+
+               err = lttng_ust_objd_unref(event_notifier_objd, 1);
+               assert(!err);
+       }
+objd_error:
+       return ret;
+}
+
+static
+long lttng_event_notifier_enabler_cmd(int objd, unsigned int cmd, unsigned long arg,
+               union ust_args *uargs, void *owner)
+{
+       struct lttng_event_notifier_enabler *event_notifier_enabler = objd_private(objd);
+       switch (cmd) {
+       case LTTNG_UST_FILTER:
+               return lttng_event_notifier_enabler_attach_bytecode(event_notifier_enabler,
+                       (struct lttng_ust_filter_bytecode_node *) arg);
+       case LTTNG_UST_EXCLUSION:
+               return lttng_event_notifier_enabler_attach_exclusion(event_notifier_enabler,
+                       (struct lttng_ust_excluder_node *) arg);
+       case LTTNG_UST_ENABLE:
+               return lttng_event_notifier_enabler_enable(event_notifier_enabler);
+       case LTTNG_UST_DISABLE:
+               return lttng_event_notifier_enabler_disable(event_notifier_enabler);
+       default:
+               return -EINVAL;
+       }
+}
+
+static
+long lttng_event_notifier_group_cmd(int objd, unsigned int cmd, unsigned long arg,
+               union ust_args *uargs, void *owner)
+{
+       switch (cmd) {
+       case LTTNG_UST_EVENT_NOTIFIER_CREATE:
+       {
+               struct lttng_ust_event_notifier *event_notifier_param =
+                       (struct lttng_ust_event_notifier *) arg;
+               if (strutils_is_star_glob_pattern(event_notifier_param->event.name)) {
+                       /*
+                        * If the event name is a star globbing pattern,
+                        * we create the special star globbing enabler.
+                        */
+                       return lttng_ust_event_notifier_enabler_create(objd,
+                                       owner, event_notifier_param,
+                                       LTTNG_ENABLER_FORMAT_STAR_GLOB);
+               } else {
+                       return lttng_ust_event_notifier_enabler_create(objd,
+                                       owner, event_notifier_param,
+                                       LTTNG_ENABLER_FORMAT_EVENT);
+               }
+       }
+       default:
+               return -EINVAL;
+       }
+}
+
+static
+int lttng_event_notifier_enabler_release(int objd)
+{
+       struct lttng_event_notifier_enabler *event_notifier_enabler = objd_private(objd);
+
+       if (event_notifier_enabler)
+               return lttng_ust_objd_unref(event_notifier_enabler->group->objd, 0);
+       return 0;
+}
+
+static const struct lttng_ust_objd_ops lttng_event_notifier_enabler_ops = {
+       .release = lttng_event_notifier_enabler_release,
+       .cmd = lttng_event_notifier_enabler_cmd,
+};
+
+static
+int lttng_release_event_notifier_group(int objd)
+{
+       struct lttng_event_notifier_group *event_notifier_group = objd_private(objd);
+
+       if (event_notifier_group) {
+               lttng_event_notifier_group_destroy(event_notifier_group);
+               return 0;
+       } else {
+               return -EINVAL;
+       }
+}
+
+static const struct lttng_ust_objd_ops lttng_event_notifier_group_ops = {
+       .release = lttng_release_event_notifier_group,
+       .cmd = lttng_event_notifier_group_cmd,
+};
+
 static
 long lttng_tracepoint_list_cmd(int objd, unsigned int cmd, unsigned long arg,
        union ust_args *uargs, void *owner)
index 87657ef80109851033088de33b5c756ca0d6c24e..530102b54aee47864dc463d63ce9d3b6dc774453 100644 (file)
@@ -321,6 +321,8 @@ static const char *cmd_name_mapping[] = {
        [ LTTNG_UST_REGISTER_DONE ] = "Registration Done",
        [ LTTNG_UST_TRACEPOINT_FIELD_LIST ] = "Create Tracepoint Field List",
 
+       [ LTTNG_UST_EVENT_NOTIFIER_GROUP_CREATE ] = "Create event notifier group",
+
        /* Session FD commands */
        [ LTTNG_UST_CHANNEL ] = "Create Channel",
        [ LTTNG_UST_SESSION_START ] = "Start Session",
@@ -345,6 +347,9 @@ static const char *cmd_name_mapping[] = {
        /* Event FD commands */
        [ LTTNG_UST_FILTER ] = "Create Filter",
        [ LTTNG_UST_EXCLUSION ] = "Add exclusions to event",
+
+       /* Event notifier group commands */
+       [ LTTNG_UST_EVENT_NOTIFIER_CREATE ] = "Create event notifier",
 };
 
 static const char *str_timeout;
@@ -908,6 +913,47 @@ int handle_message(struct sock_info *sock_info,
                }
                break;
        }
+       case LTTNG_UST_EVENT_NOTIFIER_GROUP_CREATE:
+       {
+               int event_notifier_notif_fd;
+
+               len = ustcomm_recv_event_notifier_notif_fd_from_sessiond(sock,
+                       &event_notifier_notif_fd);
+               switch (len) {
+               case 0: /* orderly shutdown */
+                       ret = 0;
+                       goto error;
+               case 1:
+                       break;
+               default:
+                       if (len < 0) {
+                               DBG("Receive failed from lttng-sessiond with errno %d",
+                                               (int) -len);
+                               if (len == -ECONNRESET) {
+                                       ERR("%s remote end closed connection",
+                                                       sock_info->name);
+                                       ret = len;
+                                       goto error;
+                               }
+                               ret = len;
+                               goto error;
+                       } else {
+                               DBG("Incorrect event notifier fd message size: %zd",
+                                               len);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               }
+               args.event_notifier_handle.event_notifier_notif_fd =
+                               event_notifier_notif_fd;
+               if (ops->cmd)
+                       ret = ops->cmd(lum->handle, lum->cmd,
+                                       (unsigned long) &lum->u,
+                                       &args, sock_info);
+               else
+                       ret = -ENOSYS;
+               break;
+       }
        case LTTNG_UST_CHANNEL:
        {
                void *chan_data;
index 649fd21133f1ce7e197e91883945ad1484dd7806..074a7b69bf44fcabfe544a191b3031fefbfe0f45 100644 (file)
@@ -44,6 +44,13 @@ struct lttng_event_enabler {
        struct lttng_ctx *ctx;
 };
 
+struct lttng_event_notifier_enabler {
+       struct lttng_enabler base;
+       struct cds_list_head node;      /* per-app list of event notifier enablers */
+       struct lttng_event_notifier_group *group; /* weak ref */
+       uint64_t user_token;            /* User-provided token */
+};
+
 struct lttng_ust_filter_bytecode_node {
        struct cds_list_head node;
        struct lttng_enabler *enabler;
@@ -71,6 +78,12 @@ struct lttng_enabler *lttng_event_enabler_as_enabler(
        return &event_enabler->base;
 }
 
+static inline
+struct lttng_enabler *lttng_event_notifier_enabler_as_enabler(
+               struct lttng_event_notifier_enabler *event_notifier_enabler)
+{
+       return &event_notifier_enabler->base;
+}
 
 /*
  * Allocate and initialize a `struct lttng_event_enabler` object.
@@ -141,4 +154,84 @@ void lttng_enabler_link_bytecode(const struct lttng_event_desc *event_desc,
                struct cds_list_head *bytecode_runtime_head,
                struct lttng_enabler *enabler);
 
+/*
+ * Allocate and initialize a `struct lttng_event_notifier_group` object.
+ *
+ * On success, returns a `struct lttng_triggre_group`,
+ * on memory error, returns NULL.
+ */
+LTTNG_HIDDEN
+struct lttng_event_notifier_group *lttng_event_notifier_group_create(void);
+
+/*
+ * Destroy a `struct lttng_event_notifier_group` object.
+ */
+LTTNG_HIDDEN
+void lttng_event_notifier_group_destroy(
+               struct lttng_event_notifier_group *event_notifier_group);
+
+/*
+ * Allocate and initialize a `struct lttng_event_notifier_enabler` object.
+ *
+ * On success, returns a `struct lttng_event_notifier_enabler`,
+ * On memory error, returns NULL.
+ */
+LTTNG_HIDDEN
+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);
+
+/*
+ * Destroy a `struct lttng_event_notifier_enabler` object.
+ */
+LTTNG_HIDDEN
+void lttng_event_notifier_enabler_destroy(
+               struct lttng_event_notifier_enabler *event_notifier_enabler);
+
+/*
+ * Enable a `struct lttng_event_notifier_enabler` object and all event
+ * notifiers related to this enabler.
+ */
+LTTNG_HIDDEN
+int lttng_event_notifier_enabler_enable(
+               struct lttng_event_notifier_enabler *event_notifier_enabler);
+
+/*
+ * Disable a `struct lttng_event_notifier_enabler` object and all event
+ * notifiers related to this enabler.
+ */
+LTTNG_HIDDEN
+int lttng_event_notifier_enabler_disable(
+               struct lttng_event_notifier_enabler *event_notifier_enabler);
+
+/*
+ * Attach filter bytecode program to `struct lttng_event_notifier_enabler` and
+ * all event notifiers related to this enabler.
+ */
+LTTNG_HIDDEN
+int lttng_event_notifier_enabler_attach_bytecode(
+               struct lttng_event_notifier_enabler *event_notifier_enabler,
+               struct lttng_ust_filter_bytecode_node *bytecode);
+
+/*
+ * Attach exclusion list to `struct lttng_event_notifier_enabler` and all
+ * event notifiers related to this enabler.
+ */
+LTTNG_HIDDEN
+int lttng_event_notifier_enabler_attach_exclusion(
+               struct lttng_event_notifier_enabler *event_notifier_enabler,
+               struct lttng_ust_excluder_node *excluder);
+
+LTTNG_HIDDEN
+void lttng_free_event_notifier_filter_runtime(
+               struct lttng_event_notifier *event_notifier);
+
+/*
+ * Connect the probe on all enablers matching this event description.
+ * Called on library load.
+ */
+LTTNG_HIDDEN
+int lttng_fix_pending_event_notifiers(void);
+
 #endif /* _LTTNG_UST_EVENTS_INTERNAL_H */
This page took 0.046841 seconds and 4 git commands to generate.