From d8d2416dab454962b90222ba46c82cdce0c666a4 Mon Sep 17 00:00:00 2001 From: Francis Deslauriers Date: Fri, 29 Nov 2019 16:35:45 -0500 Subject: [PATCH] Implement event notifier 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 Signed-off-by: Mathieu Desnoyers Change-Id: I493d784cc6830cd6c7979f6f08b209521676f05c --- include/lttng/ust-abi.h | 22 + include/lttng/ust-ctl.h | 22 + include/lttng/ust-events.h | 46 +- include/lttng/ust-tracepoint-event.h | 61 +++ include/ust-comm.h | 3 + liblttng-ust-comm/lttng-ust-comm.c | 40 ++ liblttng-ust-ctl/ustctl.c | 93 ++++ liblttng-ust/Makefile.am | 2 + liblttng-ust/context-provider-internal.h | 37 ++ liblttng-ust/event-notifier-notification.c | 60 +++ liblttng-ust/lttng-context-provider.c | 12 + liblttng-ust/lttng-events.c | 547 ++++++++++++++++++++- liblttng-ust/lttng-filter.c | 6 + liblttng-ust/lttng-probes.c | 3 + liblttng-ust/lttng-ust-abi.c | 176 +++++++ liblttng-ust/lttng-ust-comm.c | 46 ++ liblttng-ust/ust-events-internal.h | 93 ++++ 17 files changed, 1257 insertions(+), 12 deletions(-) create mode 100644 liblttng-ust/context-provider-internal.h create mode 100644 liblttng-ust/event-notifier-notification.c diff --git a/include/lttng/ust-abi.h b/include/lttng/ust-abi.h index 78aae7c7..302e6a36 100644 --- a/include/lttng/ust-abi.h +++ b/include/lttng/ust-abi.h @@ -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 { diff --git a/include/lttng/ust-ctl.h b/include/lttng/ust-ctl.h index 19fba726..a4d54c42 100644 --- a/include/lttng/ust-ctl.h +++ b/include/lttng/ust-ctl.h @@ -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. diff --git a/include/lttng/ust-events.h b/include/lttng/ust-events.h index 1e359b00..d89ddea6 100644 --- a/include/lttng/ust-events.h +++ b/include/lttng/ust-events.h @@ -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, diff --git a/include/lttng/ust-tracepoint-event.h b/include/lttng/ust-tracepoint-event.h index 622befe0..c9c75929 100644 --- a/include/lttng/ust-tracepoint-event.h +++ b/include/lttng/ust-tracepoint-event.h @@ -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 + +#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,\ }, \ }, \ }; diff --git a/include/ust-comm.h b/include/ust-comm.h index ed241e30..c9c8ca41 100644 --- a/include/ust-comm.h +++ b/include/ust-comm.h @@ -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. diff --git a/liblttng-ust-comm/lttng-ust-comm.c b/liblttng-ust-comm/lttng-ust-comm.c index 2447c9a9..9786abe7 100644 --- a/liblttng-ust-comm/lttng-ust-comm.c +++ b/liblttng-ust-comm/lttng-ust-comm.c @@ -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) diff --git a/liblttng-ust-ctl/ustctl.c b/liblttng-ust-ctl/ustctl.c index 01214eca..744f66ba 100644 --- a/liblttng-ust-ctl/ustctl.c +++ b/liblttng-ust-ctl/ustctl.c @@ -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; diff --git a/liblttng-ust/Makefile.am b/liblttng-ust/Makefile.am index d85cf602..f2cc835e 100644 --- a/liblttng-ust/Makefile.am +++ b/liblttng-ust/Makefile.am @@ -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 index 00000000..dacfb39a --- /dev/null +++ b/liblttng-ust/context-provider-internal.h @@ -0,0 +1,37 @@ +#ifndef _LTTNG_UST_CONTEXT_PROVIDER_INTERNAL_H +#define _LTTNG_UST_CONTEXT_PROVIDER_INTERNAL_H + +/* + * Copyright 2019 - Francis Deslauriers + * + * 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 +#include + +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 index 00000000..3df06437 --- /dev/null +++ b/liblttng-ust/event-notifier-notification.c @@ -0,0 +1,60 @@ +/* + * event-notifier-notification.c + * + * Copyright (C) 2020 Francis Deslauriers + * + * 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 +#include +#include + +#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, ¬if, + 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(); + } + } +} diff --git a/liblttng-ust/lttng-context-provider.c b/liblttng-ust/lttng-context-provider.c index 50f73c62..fe831366 100644 --- a/liblttng-ust/lttng-context-provider.c +++ b/liblttng-ust/lttng-context-provider.c @@ -27,8 +27,10 @@ #include #include + #include "lttng-tracer-core.h" #include "jhash.h" +#include "context-provider-internal.h" #include #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(); diff --git a/liblttng-ust/lttng-events.c b/liblttng-ust/lttng-events.c index 0c508af7..98def9a5 100644 --- a/liblttng-ust/lttng-events.c +++ b/liblttng-ust/lttng-events.c @@ -22,10 +22,10 @@ #define _LGPL_SOURCE #include -#include -#include -#include +#include #include +#include +#include #include #include #include @@ -33,13 +33,15 @@ #include #include #include +#include #include -#include "clock.h" #include +#include #include +#include +#include #include -#include #include #include @@ -48,6 +50,7 @@ #include #include #include +#include #include #include #include "error.h" @@ -63,6 +66,7 @@ #include "wait.h" #include "../libringbuffer/shm.h" #include "jhash.h" +#include /* * 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(); + } +} diff --git a/liblttng-ust/lttng-filter.c b/liblttng-ust/lttng-filter.c index 5ee12d26..70e489fc 100644 --- a/liblttng-ust/lttng-filter.c +++ b/liblttng-ust/lttng-filter.c @@ -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); +} diff --git a/liblttng-ust/lttng-probes.c b/liblttng-ust/lttng-probes.c index 9b3bacc4..e328e2c0 100644 --- a/liblttng-ust/lttng-probes.c +++ b/liblttng-ust/lttng-probes.c @@ -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; } diff --git a/liblttng-ust/lttng-ust-abi.c b/liblttng-ust/lttng-ust-abi.c index fb124c2a..d5877c3d 100644 --- a/liblttng-ust/lttng-ust-abi.c +++ b/liblttng-ust/lttng-ust-abi.c @@ -38,6 +38,7 @@ */ #define _LGPL_SOURCE +#include #include #include @@ -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, + <tng_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, <tng_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) diff --git a/liblttng-ust/lttng-ust-comm.c b/liblttng-ust/lttng-ust-comm.c index 87657ef8..530102b5 100644 --- a/liblttng-ust/lttng-ust-comm.c +++ b/liblttng-ust/lttng-ust-comm.c @@ -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; diff --git a/liblttng-ust/ust-events-internal.h b/liblttng-ust/ust-events-internal.h index 649fd211..074a7b69 100644 --- a/liblttng-ust/ust-events-internal.h +++ b/liblttng-ust/ust-events-internal.h @@ -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 */ -- 2.34.1