X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=liblttng-ust%2Fevent-notifier-notification.c;h=96b91a9ab0b84d2bd85b4531b28d18e9f1b11e33;hb=cd61d9bfa5fd334b086831dcbdec63da6c0d1a16;hp=844438f2378a329763a90d5f49e431062d89253b;hpb=b9f82a28cface9fa168149252d560a7bade056c9;p=lttng-ust.git diff --git a/liblttng-ust/event-notifier-notification.c b/liblttng-ust/event-notifier-notification.c index 844438f2..96b91a9a 100644 --- a/liblttng-ust/event-notifier-notification.c +++ b/liblttng-ust/event-notifier-notification.c @@ -1,38 +1,39 @@ /* - * event-notifier-notification.c + * SPDX-License-Identifier: LGPL-2.1-only * * 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 +#include #include #include "../libmsgpack/msgpack.h" #include "lttng-bytecode.h" -#include "share.h" +#include "ust-share.h" + +/* + * 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 less than `PIPE_BUF` bytes must be + * atomic, so we bound the capture buffer size to the `PIPE_BUF` minus the size + * of the notification struct we are sending alongside the capture buffer. + */ +#define CAPTURE_BUFFER_SIZE \ + (PIPE_BUF - sizeof(struct lttng_ust_event_notifier_notification) - 1) + +struct lttng_event_notifier_notification { + int notification_fd; + uint64_t event_notifier_token; + uint8_t capture_buf[CAPTURE_BUFFER_SIZE]; + struct lttng_msgpack_writer writer; + bool has_captures; +}; -static -void capture_enum(struct lttng_msgpack_writer *writer, - struct lttng_interpreter_output *output) __attribute__ ((unused)); static void capture_enum(struct lttng_msgpack_writer *writer, struct lttng_interpreter_output *output) @@ -155,9 +156,6 @@ uint64_t capture_sequence_element_unsigned(uint8_t *ptr, return value; } -static -void capture_sequence(struct lttng_msgpack_writer *writer, - struct lttng_interpreter_output *output) __attribute__ ((unused)); static void capture_sequence(struct lttng_msgpack_writer *writer, struct lttng_interpreter_output *output) @@ -210,30 +208,144 @@ void capture_sequence(struct lttng_msgpack_writer *writer, lttng_msgpack_end_array(writer); } -void lttng_event_notifier_notification_send( +static +void notification_init(struct lttng_event_notifier_notification *notif, struct lttng_event_notifier *event_notifier) { + struct lttng_msgpack_writer *writer = ¬if->writer; + + notif->event_notifier_token = event_notifier->user_token; + notif->notification_fd = event_notifier->group->notification_fd; + notif->has_captures = false; + + if (event_notifier->num_captures > 0) { + lttng_msgpack_writer_init(writer, notif->capture_buf, + CAPTURE_BUFFER_SIZE); + + lttng_msgpack_begin_array(writer, event_notifier->num_captures); + notif->has_captures = true; + } +} + +static +void notification_append_capture( + struct lttng_event_notifier_notification *notif, + struct lttng_interpreter_output *output) +{ + struct lttng_msgpack_writer *writer = ¬if->writer; + + switch (output->type) { + case LTTNG_INTERPRETER_TYPE_S64: + lttng_msgpack_write_signed_integer(writer, output->u.s); + break; + case LTTNG_INTERPRETER_TYPE_U64: + lttng_msgpack_write_unsigned_integer(writer, output->u.u); + break; + case LTTNG_INTERPRETER_TYPE_DOUBLE: + lttng_msgpack_write_double(writer, output->u.d); + break; + case LTTNG_INTERPRETER_TYPE_STRING: + lttng_msgpack_write_str(writer, output->u.str.str); + break; + case LTTNG_INTERPRETER_TYPE_SEQUENCE: + capture_sequence(writer, output); + break; + case LTTNG_INTERPRETER_TYPE_SIGNED_ENUM: + case LTTNG_INTERPRETER_TYPE_UNSIGNED_ENUM: + capture_enum(writer, output); + break; + default: + abort(); + } +} + +static +void notification_append_empty_capture( + struct lttng_event_notifier_notification *notif) +{ + lttng_msgpack_write_nil(¬if->writer); +} + +static void record_error(struct lttng_event_notifier *event_notifier) +{ + struct lttng_event_notifier_group *event_notifier_group = + event_notifier->group; + struct lttng_counter *error_counter; + size_t dimension_index[1]; + int ret; + + error_counter = CMM_LOAD_SHARED(event_notifier_group->error_counter); /* - * 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. + * load-acquire paired with store-release orders creation of the + * error counter and setting error_counter_len before the + * error_counter is used. + * Currently a full memory barrier is used, which could be + * turned into acquire-release barriers. */ - struct lttng_ust_event_notifier_notification notif; + cmm_smp_mb(); + /* This group may not have an error counter attached to it. */ + if (!error_counter) + return; + + dimension_index[0] = event_notifier->error_counter_index; + ret = event_notifier_group->error_counter->ops->counter_add( + error_counter->counter, dimension_index, 1); + if (ret) + WARN_ON_ONCE(1); +} + +static +void notification_send(struct lttng_event_notifier_notification *notif, + struct lttng_event_notifier *event_notifier) +{ ssize_t ret; + size_t content_len; + int iovec_count = 1; + struct lttng_ust_event_notifier_notification ust_notif = {0}; + struct iovec iov[2]; + + assert(notif); + + ust_notif.token = event_notifier->user_token; + + /* + * Prepare sending the notification from multiple buffers using an + * array of `struct iovec`. The first buffer of the vector is + * notification structure itself and is always present. + */ + iov[0].iov_base = &ust_notif; + iov[0].iov_len = sizeof(ust_notif); + + if (notif->has_captures) { + /* + * If captures were requested, the second buffer of the array + * is the capture buffer. + */ + assert(notif->writer.buffer); + content_len = notif->writer.write_pos - notif->writer.buffer; + + assert(content_len > 0 && content_len <= CAPTURE_BUFFER_SIZE); + + iov[1].iov_base = notif->capture_buf; + iov[1].iov_len = content_len; - assert(event_notifier); - assert(event_notifier->group); - assert(sizeof(notif) <= PIPE_BUF); + iovec_count++; + } else { + content_len = 0; + } - notif.token = event_notifier->user_token; + /* + * Update the capture buffer size so that receiver of the buffer will + * know how much to expect. + */ + ust_notif.capture_buf_size = content_len; - ret = patient_write(event_notifier->group->notification_fd, ¬if, - sizeof(notif)); + /* Send all the buffers. */ + ret = ust_patient_writev(notif->notification_fd, iov, iovec_count); if (ret == -1) { if (errno == EAGAIN) { - DBG("Cannot send event notifier notification without blocking: %s", + record_error(event_notifier); + DBG("Cannot send event_notifier notification without blocking: %s", strerror(errno)); } else { DBG("Error to sending event notifier notification: %s", @@ -242,3 +354,43 @@ void lttng_event_notifier_notification_send( } } } + +void lttng_event_notifier_notification_send( + struct lttng_event_notifier *event_notifier, + const char *stack_data) +{ + /* + * This function is called from the probe, we must do dynamic + * allocation in this context. + */ + struct lttng_event_notifier_notification notif = {0}; + + notification_init(¬if, event_notifier); + + if (caa_unlikely(!cds_list_empty(&event_notifier->capture_bytecode_runtime_head))) { + struct lttng_bytecode_runtime *capture_bc_runtime; + + /* + * Iterate over all the capture bytecodes. If the interpreter + * functions returns successfully, append the value of the + * `output` parameter to the capture buffer. If the interpreter + * fails, append an empty capture to the buffer. + */ + cds_list_for_each_entry(capture_bc_runtime, + &event_notifier->capture_bytecode_runtime_head, node) { + struct lttng_interpreter_output output; + + if (capture_bc_runtime->interpreter_funcs.capture(capture_bc_runtime, + stack_data, &output) & LTTNG_INTERPRETER_RECORD_FLAG) + notification_append_capture(¬if, &output); + else + notification_append_empty_capture(¬if); + } + } + + /* + * Send the notification (including the capture buffer) to the + * sessiond. + */ + notification_send(¬if, event_notifier); +}