#define LTTNG_EVENT_NOTIFIER_NOTIFICATION_PADDING 32
struct lttng_ust_event_notifier_notification {
uint64_t token;
+ uint16_t capture_buf_size;
char padding[LTTNG_EVENT_NOTIFIER_NOTIFICATION_PADDING];
} LTTNG_PACKED;
char data[0];
} LTTNG_PACKED;
+#define CAPTURE_BYTECODE_MAX_LEN 65536
+#define LTTNG_UST_CAPTURE_PADDING 32
+struct lttng_ust_capture_bytecode {
+ uint32_t len;
+ uint32_t reloc_offset;
+ uint64_t seqnum;
+ char padding[LTTNG_UST_CAPTURE_PADDING];
+ char data[0];
+} LTTNG_PACKED;
+
#define LTTNG_UST_EXCLUSION_PADDING 32
struct lttng_ust_event_exclusion {
uint32_t count;
/* Event notifier group commands */
#define LTTNG_UST_EVENT_NOTIFIER_CREATE \
_UST_CMDW(0xB0, struct lttng_ust_event_notifier)
+#define LTTNG_UST_CAPTURE _UST_CMD(0xB1)
#define LTTNG_UST_ROOT_HANDLE 0
struct lttng_ust_object_data **context_data);
int ustctl_set_filter(int sock, struct lttng_ust_filter_bytecode *bytecode,
struct lttng_ust_object_data *obj_data);
+int ustctl_set_capture(int sock, struct lttng_ust_capture_bytecode *bytecode,
+ struct lttng_ust_object_data *obj_data);
int ustctl_set_exclusion(int sock, struct lttng_ust_event_exclusion *exclusion,
struct lttng_ust_object_data *obj_data);
/* Other bits are kept for future use. */
};
+struct lttng_interpreter_output;
+
/*
* This structure is used in the probes. More specifically, the `filter` and
* `node` fields are explicity used in the probes. When modifying this
union {
uint64_t (*filter)(void *interpreter_data,
const char *interpreter_stack_data);
+ uint64_t (*capture)(void *interpreter_data,
+ const char *interpreter_stack_data,
+ struct lttng_interpreter_output *interpreter_output);
} interpreter_funcs;
int link_failed;
struct cds_list_head node; /* list of bytecode runtime in event */
uint64_t user_token;
int enabled;
int registered; /* has reg'd tracepoint probe */
+ size_t num_captures; /* Needed to allocate the msgpack array. */
struct cds_list_head filter_bytecode_runtime_head;
+ struct cds_list_head capture_bytecode_runtime_head;
int has_enablers_without_bytecode;
struct cds_list_head enablers_ref_head;
const struct lttng_event_desc *desc;
void lttng_session_destroy(struct lttng_session *session);
void lttng_event_notifier_notification_send(
- struct lttng_event_notifier *event_notifier);
+ struct lttng_event_notifier *event_notifier,
+ const char *stack_data);
struct lttng_channel *lttng_channel_create(struct lttng_session *session,
const char *transport_name,
#undef TRACEPOINT_EVENT_CLASS
#define TRACEPOINT_EVENT_CLASS(_provider, _name, _args, _fields) \
static inline \
-void __event_prepare_filter_stack__##_provider##___##_name(char *__stack_data,\
+void __event_prepare_interpreter_stack__##_provider##___##_name(char *__stack_data,\
_TP_ARGS_DATA_PROTO(_args)) \
{ \
_fields \
struct lttng_bytecode_runtime *__filter_bc_runtime; \
int __filter_record = __event->has_enablers_without_bytecode; \
\
- __event_prepare_filter_stack__##_provider##___##_name(__stackvar.__filter_stack_data, \
+ __event_prepare_interpreter_stack__##_provider##___##_name(__stackvar.__filter_stack_data, \
_TP_ARGS_DATA_VAR(_args)); \
tp_list_for_each_entry_rcu(__filter_bc_runtime, &__event->filter_bytecode_runtime_head, node) { \
if (caa_unlikely(__filter_bc_runtime->interpreter_funcs.filter(__filter_bc_runtime, \
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]; \
+ char __interpreter_stack_data[2 * sizeof(unsigned long) * __num_fields]; \
} __stackvar; \
if (caa_unlikely(!CMM_ACCESS_ONCE(__event_notifier->enabled))) \
return; \
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, \
+ __event_prepare_interpreter_stack__##_provider##___##_name(__stackvar.__interpreter_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->interpreter_funcs.filter(__filter_bc_runtime, \
- __stackvar.__filter_stack_data) & LTTNG_INTERPRETER_RECORD_FLAG)) \
+ __stackvar.__interpreter_stack_data) & LTTNG_INTERPRETER_RECORD_FLAG)) \
__filter_record = 1; \
} \
if (caa_likely(!__filter_record)) \
return; \
} \
+ if (caa_unlikely(!cds_list_empty(&__event_notifier->capture_bytecode_runtime_head))) \
+ __event_prepare_interpreter_stack__##_provider##___##_name(__stackvar.__interpreter_stack_data, \
+ _TP_ARGS_DATA_VAR(_args)); \
\
- lttng_event_notifier_notification_send(__event_notifier); \
+ lttng_event_notifier_notification_send(__event_notifier, \
+ __stackvar.__interpreter_stack_data); \
}
#include TRACEPOINT_INCLUDE
struct {
uint32_t count; /* how many names follow */
} LTTNG_PACKED exclusion;
+ struct {
+ uint32_t data_size; /* following capture data */
+ uint32_t reloc_offset;
+ uint64_t seqnum;
+ } LTTNG_PACKED capture;
char padding[USTCOMM_MSG_PADDING2];
} u;
} LTTNG_PACKED;
return ustcomm_recv_app_reply(sock, &lur, lum.handle, lum.cmd);
}
+int ustctl_set_capture(int sock, struct lttng_ust_capture_bytecode *bytecode,
+ struct lttng_ust_object_data *obj_data)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ int ret;
+
+ if (!obj_data)
+ return -EINVAL;
+
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = obj_data->handle;
+ lum.cmd = LTTNG_UST_CAPTURE;
+ lum.u.capture.data_size = bytecode->len;
+ lum.u.capture.reloc_offset = bytecode->reloc_offset;
+ lum.u.capture.seqnum = bytecode->seqnum;
+
+ ret = ustcomm_send_app_msg(sock, &lum);
+ if (ret)
+ return ret;
+ /* send var len bytecode */
+ ret = ustcomm_send_unix_sock(sock, bytecode->data,
+ bytecode->len);
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret != bytecode->len)
+ return -EINVAL;
+ return ustcomm_recv_app_reply(sock, &lur, lum.handle, lum.cmd);
+}
+
int ustctl_set_exclusion(int sock, struct lttng_ust_event_exclusion *exclusion,
struct lttng_ust_object_data *obj_data)
{
#include "lttng-bytecode.h"
#include "share.h"
-static
-void capture_enum(struct lttng_msgpack_writer *writer,
- struct lttng_interpreter_output *output) __attribute__ ((unused));
+/*
+ * 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)
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)
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 notification_send(struct lttng_event_notifier_notification *notif)
+{
+ ssize_t ret;
+ size_t content_len;
+ int iovec_count = 1;
+ struct lttng_ust_event_notifier_notification ust_notif;
+ struct iovec iov[2];
+
+ assert(notif);
+
+ ust_notif.token = notif->event_notifier_token;
+
/*
- * 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.
+ * 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.
*/
- struct lttng_ust_event_notifier_notification notif;
- ssize_t ret;
+ 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(event_notifier);
- assert(event_notifier->group);
- assert(sizeof(notif) <= PIPE_BUF);
+ assert(content_len > 0 && content_len <= CAPTURE_BUFFER_SIZE);
- notif.token = event_notifier->user_token;
+ iov[1].iov_base = notif->capture_buf;
+ iov[1].iov_len = content_len;
+
+ iovec_count++;
+ } else {
+ content_len = 0;
+ }
+
+ /*
+ * 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 = patient_writev(notif->notification_fd, iov, iovec_count);
if (ret == -1) {
if (errno == EAGAIN) {
DBG("Cannot send event notifier notification without blocking: %s",
}
}
}
+
+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);
+}
return LTTNG_INTERPRETER_DISCARD;
}
+uint64_t lttng_bytecode_capture_interpret_false(void *capture_data,
+ const char *capture_stack_data,
+ struct lttng_interpreter_output *output)
+{
+ return LTTNG_INTERPRETER_DISCARD;
+}
+
#ifdef INTERPRETER_USE_SWITCH
/*
}
/*
- * Return 0 (discard), or raise the 0x1 flag (log event).
- * Currently, other flags are kept for future extensions and have no
- * effect.
+ * For `output` equal to NULL:
+ * Return 0 (discard), or raise the 0x1 flag (log event).
+ * Currently, other flags are kept for future extensions and have no
+ * effect.
+ * For `output` not equal to NULL:
+ * Return 0 on success, negative error value on error.
*/
static
uint64_t bytecode_interpret(void *interpreter_data,
return bytecode_interpret(filter_data, filter_stack_data, NULL);
}
+uint64_t lttng_bytecode_capture_interpret(void *capture_data,
+ const char *capture_stack_data,
+ struct lttng_interpreter_output *output)
+{
+ return bytecode_interpret(capture_data, capture_stack_data,
+ (struct lttng_interpreter_output *) output);
+}
+
#undef START_OP
#undef OP
#undef PO
case LTTNG_UST_BYTECODE_NODE_TYPE_FILTER:
runtime->p.interpreter_funcs.filter = lttng_bytecode_filter_interpret;
break;
+ case LTTNG_UST_BYTECODE_NODE_TYPE_CAPTURE:
+ runtime->p.interpreter_funcs.capture = lttng_bytecode_capture_interpret;
+ break;
default:
abort();
}
case LTTNG_UST_BYTECODE_NODE_TYPE_FILTER:
runtime->p.interpreter_funcs.filter = lttng_bytecode_filter_interpret_false;
break;
+ case LTTNG_UST_BYTECODE_NODE_TYPE_CAPTURE:
+ runtime->p.interpreter_funcs.capture = lttng_bytecode_capture_interpret_false;
+ break;
default:
abort();
}
runtime->interpreter_funcs.filter = lttng_bytecode_filter_interpret;
}
+void lttng_bytecode_capture_sync_state(struct lttng_bytecode_runtime *runtime)
+{
+ struct lttng_ust_bytecode_node *bc = runtime->bc;
+
+ if (!bc->enabler->enabled || runtime->link_failed)
+ runtime->interpreter_funcs.capture = lttng_bytecode_capture_interpret_false;
+ else
+ runtime->interpreter_funcs.capture = lttng_bytecode_capture_interpret;
+}
+
/*
* Given the lists of bytecode programs of an instance (trigger or event) and
* of a matching enabler, try to link all the enabler's bytecode programs with
const char *print_op(enum bytecode_op op);
void lttng_bytecode_filter_sync_state(struct lttng_bytecode_runtime *runtime);
+void lttng_bytecode_capture_sync_state(struct lttng_bytecode_runtime *runtime);
int lttng_bytecode_validate(struct bytecode_runtime *bytecode);
int lttng_bytecode_specialize(const struct lttng_event_desc *event_desc,
uint64_t lttng_bytecode_filter_interpret(void *filter_data,
const char *filter_stack_data);
+uint64_t lttng_bytecode_capture_interpret_false(void *capture_data,
+ const char *capture_stack_data,
+ struct lttng_interpreter_output *output);
+uint64_t lttng_bytecode_capture_interpret(void *capture_data,
+ const char *capture_stack_data,
+ struct lttng_interpreter_output *output);
+
#endif /* _LTTNG_BYTECODE_H */
event_notifier->registered = 0;
CDS_INIT_LIST_HEAD(&event_notifier->filter_bytecode_runtime_head);
+ CDS_INIT_LIST_HEAD(&event_notifier->capture_bytecode_runtime_head);
CDS_INIT_LIST_HEAD(&event_notifier->enablers_ref_head);
event_notifier->desc = desc;
return NULL;
event_notifier_enabler->base.format_type = format_type;
CDS_INIT_LIST_HEAD(&event_notifier_enabler->base.filter_bytecode_head);
+ CDS_INIT_LIST_HEAD(&event_notifier_enabler->capture_bytecode_head);
CDS_INIT_LIST_HEAD(&event_notifier_enabler->base.excluder_head);
event_notifier_enabler->user_token = event_notifier_param->event.token;
+ event_notifier_enabler->num_captures = 0;
memcpy(&event_notifier_enabler->base.event_param.name,
event_notifier_param->event.name,
return 0;
}
+int lttng_event_notifier_enabler_attach_capture_bytecode(
+ struct lttng_event_notifier_enabler *event_notifier_enabler,
+ struct lttng_ust_bytecode_node *bytecode)
+{
+ bytecode->enabler = lttng_event_notifier_enabler_as_enabler(
+ event_notifier_enabler);
+ cds_list_add_tail(&bytecode->node,
+ &event_notifier_enabler->capture_bytecode_head);
+ event_notifier_enabler->num_captures++;
+
+ lttng_event_notifier_group_sync_enablers(event_notifier_enabler->group);
+ return 0;
+}
+
int lttng_event_notifier_enabler_attach_exclusion(
struct lttng_event_notifier_enabler *event_notifier_enabler,
struct lttng_ust_excluder_node *excluder)
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;
&event_notifier_group->ctx,
&event_notifier->filter_bytecode_runtime_head,
<tng_event_notifier_enabler_as_enabler(event_notifier_enabler)->filter_bytecode_head);
+
+ /*
+ * Link capture bytecodes if not linked yet.
+ */
+ lttng_enabler_link_bytecode(event_notifier->desc,
+ &event_notifier_group->ctx, &event_notifier->capture_bytecode_runtime_head,
+ &event_notifier_enabler->capture_bytecode_head);
+
+ event_notifier->num_captures = event_notifier_enabler->num_captures;
}
end:
return 0;
&event_notifier->filter_bytecode_runtime_head, node) {
lttng_bytecode_filter_sync_state(runtime);
}
+
+ /* Enable captures. */
+ cds_list_for_each_entry(runtime,
+ &event_notifier->capture_bytecode_runtime_head, node) {
+ lttng_bytecode_capture_sync_state(runtime);
+ }
}
__tracepoint_probe_prune_release_queue();
}
case LTTNG_UST_EXCLUSION:
return lttng_event_notifier_enabler_attach_exclusion(event_notifier_enabler,
(struct lttng_ust_excluder_node *) arg);
+ case LTTNG_UST_CAPTURE:
+ return lttng_event_notifier_enabler_attach_capture_bytecode(
+ event_notifier_enabler,
+ (struct lttng_ust_bytecode_node *) arg);
case LTTNG_UST_ENABLE:
return lttng_event_notifier_enabler_enable(event_notifier_enabler);
case LTTNG_UST_DISABLE:
const char *bytecode_type_str(uint32_t cmd)
{
switch (cmd) {
+ case LTTNG_UST_CAPTURE:
+ return "capture";
case LTTNG_UST_FILTER:
return "filter";
default:
reloc_offset = lum->u.filter.reloc_offset;
seqnum = lum->u.filter.seqnum;
break;
+ case LTTNG_UST_CAPTURE:
+ type = LTTNG_UST_BYTECODE_NODE_TYPE_CAPTURE;
+ data_size = lum->u.capture.data_size;
+ data_size_max = CAPTURE_BYTECODE_MAX_LEN;
+ reloc_offset = lum->u.capture.reloc_offset;
+ seqnum = lum->u.capture.seqnum;
+ break;
default:
abort();
}
else
ret = lttng_ust_objd_unref(lum->handle, 1);
break;
+ case LTTNG_UST_CAPTURE:
case LTTNG_UST_FILTER:
ret = handle_bytecode_recv(sock_info, sock, lum);
if (ret)
struct lttng_event_notifier_enabler {
struct lttng_enabler base;
- struct cds_list_head node; /* per-app list of event notifier enablers */
+ struct cds_list_head node; /* per-app list of event_notifier enablers */
+ struct cds_list_head capture_bytecode_head;
struct lttng_event_notifier_group *group; /* weak ref */
uint64_t user_token; /* User-provided token */
+ uint64_t num_captures;
};
enum lttng_ust_bytecode_node_type {
LTTNG_UST_BYTECODE_NODE_TYPE_FILTER,
+ LTTNG_UST_BYTECODE_NODE_TYPE_CAPTURE,
};
struct lttng_ust_bytecode_node {
struct lttng_ust_excluder_node *excluder);
/*
- * Synchronize bytecodes for the enabler and the instance (event or trigger).
+ * Synchronize bytecodes for the enabler and the instance (event or
+ * event_notifier).
*
* This function goes over all bytecode programs of the enabler (event or
- * trigger enabler) to ensure each is linked to the provided instance.
+ * event_notifier enabler) to ensure each is linked to the provided instance.
*/
LTTNG_HIDDEN
void lttng_enabler_link_bytecode(const struct lttng_event_desc *event_desc,
struct lttng_event_notifier_enabler *event_notifier_enabler,
struct lttng_ust_bytecode_node *bytecode);
+/*
+ * Attach capture bytecode program to `struct lttng_event_notifier_enabler` and
+ * all event_notifiers related to this enabler.
+ */
+LTTNG_HIDDEN
+int lttng_event_notifier_enabler_attach_capture_bytecode(
+ struct lttng_event_notifier_enabler *event_notifier_enabler,
+ struct lttng_ust_bytecode_node *bytecode);
+
/*
* Attach exclusion list to `struct lttng_event_notifier_enabler` and all
* event notifiers related to this enabler.