From c1710c3bcf2e90d6f4cad986ee8d31f9b3c10f5d Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Tue, 9 Mar 2021 16:23:37 -0500 Subject: [PATCH 01/16] msgpack: silence uninitialized msg_pack_object warning MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Present in the upstream msgpack code. I couldn't find a problematic use, but this will silence Coverity and is cleaner. Nothing should access 'via' when the type of a msg_pack_object is NIL. Coverity report: 1448708 Uninitialized scalar variable The variable will contain an arbitrary value left from earlier computations. In template_callback_root: Use of an uninitialized variable (CWE-457) Reported-by: Coverity Scan Signed-off-by: Jérémie Galarneau Change-Id: I4fe63b0279ca7c3f4c34ee8762ec724a731894c5 --- src/vendor/msgpack/unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vendor/msgpack/unpack.c b/src/vendor/msgpack/unpack.c index b390f5868..6b10f05b1 100644 --- a/src/vendor/msgpack/unpack.c +++ b/src/vendor/msgpack/unpack.c @@ -50,7 +50,7 @@ static int template_execute( static inline msgpack_object template_callback_root(unpack_user* u) { - msgpack_object o; + msgpack_object o = {}; MSGPACK_UNUSED(u); o.type = MSGPACK_OBJECT_NIL; return o; -- 2.34.1 From a3088f1a14942fade23f14d778ca635305fb6e84 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Tue, 9 Mar 2021 16:39:11 -0500 Subject: [PATCH 02/16] Fix: event-expr-to-bytecode: use after free in logging on error MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 1448709 Use after free This could cause an immediate crash or incorrect values might be read subsequently resulting in incorrect computations. In event_expr_to_bytecode_recursive: A pointer to freed memory is dereferenced, used as a function argument, or otherwise used (CWE-416) Reported-by: Coverity Scan Signed-off-by: Jérémie Galarneau Change-Id: Ifde0e21dbf6bf08be07b38f9c38896bdce973132 --- src/common/event-expr-to-bytecode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/event-expr-to-bytecode.c b/src/common/event-expr-to-bytecode.c index abc713fb3..9fe3c0350 100644 --- a/src/common/event-expr-to-bytecode.c +++ b/src/common/event-expr-to-bytecode.c @@ -114,7 +114,8 @@ int event_expr_to_bytecode_recursive(const struct lttng_event_expr *expr, bytecode, bytecode_reloc, name); free(name); if (status) { - ERR("Failed to push 'get symbol %s' in bytecode", name); + ERR("Failed to push 'get symbol %s:%s' in bytecode", + provider_name, type_name); goto end; } -- 2.34.1 From 3363a4c34e8283a6c1fccfb21a6658443a415055 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 11 Mar 2021 15:26:01 -0500 Subject: [PATCH 03/16] Fix: lttng: add-trigger: payload capture fields specified as contexts MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit ir_op_load_expr_to_event_expr() re-uses the same `load_expr_op` variable to traverse the IR. A check for the type of the original load expression is performed after `load_expr_op->next` is assigned to load_expr_op which is expected to have remained the same. The original event_expr child type is sampled at the start of the function and re-used as needed. Signed-off-by: Jérémie Galarneau Change-Id: I14ebcb9088c5107f3133ff5b43c65c3fc6101077 --- src/bin/lttng/commands/add_trigger.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/bin/lttng/commands/add_trigger.c b/src/bin/lttng/commands/add_trigger.c index e4342cf85..b3b1dade1 100644 --- a/src/bin/lttng/commands/add_trigger.c +++ b/src/bin/lttng/commands/add_trigger.c @@ -301,13 +301,16 @@ end: static struct lttng_event_expr *ir_op_load_expr_to_event_expr( - const struct ir_load_expression *load_exp, const char *capture_str) + const struct ir_load_expression *load_expr, + const char *capture_str) { char *provider_name = NULL; struct lttng_event_expr *event_expr = NULL; - const struct ir_load_expression_op *load_expr_op = load_exp->child; + const struct ir_load_expression_op *load_expr_op = load_expr->child; + const enum ir_load_expression_type load_expr_child_type = + load_expr_op->type; - switch (load_expr_op->type) { + switch (load_expr_child_type) { case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT: case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT: { @@ -319,12 +322,12 @@ struct lttng_event_expr *ir_op_load_expr_to_event_expr( field_name = load_expr_op->u.symbol; assert(field_name); - event_expr = load_expr_op->type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ? + event_expr = load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ? lttng_event_expr_event_payload_field_create(field_name) : lttng_event_expr_channel_context_field_create(field_name); if (!event_expr) { ERR("Failed to create %s event expression: field name = `%s`.", - load_expr_op->type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ? + load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ? "payload field" : "channel context", field_name); goto error; -- 2.34.1 From 6fb7c69074b6884da56bb0758e92e1c7f7859859 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 29 Apr 2020 15:36:00 -0400 Subject: [PATCH 04/16] condition: implement lttng_condition_event_rule_get_internal_capture_descriptor_at_index MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The lttng_capture_descriptor object is only exposed internally. A getter is necessary to allow more control on the lttng_capture_descriptor object. This will be reused to set the bytecode later in the patchset. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I7fdfdc9d2ac1920bcf1a098352dfd105949e1062 Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- src/common/conditions/event-rule.c | 65 +++++++++++++++++++----------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/event-rule.c index a08b3eb41..8d3f1cd53 100644 --- a/src/common/conditions/event-rule.c +++ b/src/common/conditions/event-rule.c @@ -192,6 +192,39 @@ end: return ret; } +static +struct lttng_capture_descriptor * +lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + const struct lttng_condition *condition, unsigned int index) +{ + const struct lttng_condition_event_rule *event_rule_cond = + container_of(condition, + const struct lttng_condition_event_rule, + parent); + struct lttng_capture_descriptor *desc = NULL; + unsigned int count; + enum lttng_condition_status status; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition)) { + goto end; + } + + status = lttng_condition_event_rule_get_capture_descriptor_count( + condition, &count); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + if (index >= count) { + goto end; + } + + desc = lttng_dynamic_pointer_array_get_pointer( + &event_rule_cond->capture_descriptors, index); +end: + return desc; +} + static int lttng_condition_event_rule_serialize( const struct lttng_condition *condition, struct lttng_payload *payload) @@ -233,13 +266,13 @@ static int lttng_condition_event_rule_serialize( } for (i = 0; i < capture_descr_count; i++) { - const struct lttng_event_expr *expr = - lttng_condition_event_rule_get_capture_descriptor_at_index( + const struct lttng_capture_descriptor *desc = + lttng_condition_event_rule_get_internal_capture_descriptor_at_index( condition, i); DBG("Serializing event rule condition's capture descriptor %" PRIu32, i); - ret = serialize_event_expr(expr, payload); + ret = serialize_event_expr(desc->event_expression, payload); if (ret) { goto end; } @@ -586,9 +619,9 @@ ssize_t lttng_condition_event_rule_create_from_payload( /* Capture descriptors. */ for (i = 0; i < capture_descr_count; i++) { + enum lttng_condition_status status; struct lttng_event_expr *expr = event_expr_from_payload( view, &offset); - enum lttng_condition_status status; if (!expr) { goto error; @@ -724,30 +757,14 @@ const struct lttng_event_expr * lttng_condition_event_rule_get_capture_descriptor_at_index( const struct lttng_condition *condition, unsigned int index) { - const struct lttng_condition_event_rule *event_rule_cond = - container_of(condition, - const struct lttng_condition_event_rule, - parent); const struct lttng_event_expr *expr = NULL; - struct lttng_capture_descriptor *desc = NULL; - unsigned int count; - enum lttng_condition_status status; + const struct lttng_capture_descriptor *desc = NULL; - if (!condition || !IS_EVENT_RULE_CONDITION(condition)) { - goto end; - } - - status = lttng_condition_event_rule_get_capture_descriptor_count(condition, &count); - if (status != LTTNG_CONDITION_STATUS_OK) { - goto end; - } - - if (index >= count) { + desc = lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + condition, index); + if (desc == NULL) { goto end; } - - desc = lttng_dynamic_pointer_array_get_pointer( - &event_rule_cond->capture_descriptors, index); expr = desc->event_expression; end: -- 2.34.1 From 834966afb566191f13f7c6870902a3e4b00a9d27 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 8 Apr 2020 17:51:52 -0400 Subject: [PATCH 05/16] condition: implement lttng_condition_event_rule_generate_capture_descriptor_bytecode MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Generate the bytecode related to the tracer notification capture feature. The bytecode is stored alongside the lttng_event_expr in an internal lttng_capture_descriptor object. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I4cfbdd9ab91328a5540ad19049e056fe3ce7f5ba Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- include/lttng/condition/event-rule-internal.h | 6 ++- include/lttng/lttng-error.h | 1 + src/common/conditions/event-rule.c | 52 ++++++++++++++++++- src/common/error.c | 1 + 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/include/lttng/condition/event-rule-internal.h b/include/lttng/condition/event-rule-internal.h index 241ec571d..1f039c347 100644 --- a/include/lttng/condition/event-rule-internal.h +++ b/include/lttng/condition/event-rule-internal.h @@ -39,7 +39,6 @@ struct lttng_evaluation_event_rule_comm { char payload[]; } LTTNG_PACKED; - LTTNG_HIDDEN ssize_t lttng_condition_event_rule_create_from_payload( struct lttng_payload_view *view, @@ -60,4 +59,9 @@ ssize_t lttng_evaluation_event_rule_create_from_payload( struct lttng_payload_view *view, struct lttng_evaluation **_evaluation); +LTTNG_HIDDEN +enum lttng_error_code +lttng_condition_event_rule_generate_capture_descriptor_bytecode( + struct lttng_condition *condition); + #endif /* LTTNG_CONDITION_EVENT_RULE_INTERNAL_H */ diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index db0f5e0f7..606d8b0b2 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -175,6 +175,7 @@ enum lttng_error_code { LTTNG_ERR_UNSUPPORTED_DOMAIN = 162, /* Unsupported domain used. */ LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY = 163, /* Operation does not apply to the process attribute tracker's tracking policy */ LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD = 164, /* Error initializing event notifier group notification file descriptor */ + LTTNG_ERR_INVALID_CAPTURE_EXPRESSION = 165, /* Invalid capture expression. */ /* MUST be last element of the manually-assigned section of the enum */ LTTNG_ERR_NR, diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/event-rule.c index 8d3f1cd53..8959652dd 100644 --- a/src/common/conditions/event-rule.c +++ b/src/common/conditions/event-rule.c @@ -7,14 +7,16 @@ #include #include +#include #include #include #include +#include #include #include #include #include -#include +#include #include #include #include @@ -922,3 +924,51 @@ enum lttng_evaluation_status lttng_evaluation_event_rule_get_trigger_name( end: return status; } + +LTTNG_HIDDEN +enum lttng_error_code +lttng_condition_event_rule_generate_capture_descriptor_bytecode( + struct lttng_condition *condition) +{ + enum lttng_error_code ret; + enum lttng_condition_status status; + unsigned int capture_count, i; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition)) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + status = lttng_condition_event_rule_get_capture_descriptor_count( + condition, &capture_count); + if (status != LTTNG_CONDITION_STATUS_OK) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + for (i = 0; i < capture_count; i++) { + struct lttng_capture_descriptor *local_capture_desc = + lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + condition, i); + + if (local_capture_desc == NULL) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + /* Generate the bytecode. */ + status = lttng_event_expr_to_bytecode( + local_capture_desc->event_expression, + &local_capture_desc->bytecode); + if (status < 0 || local_capture_desc->bytecode == NULL) { + ret = LTTNG_ERR_INVALID_CAPTURE_EXPRESSION; + goto end; + } + } + + /* Everything went better than expected */ + ret = LTTNG_OK; + +end: + return ret; +} diff --git a/src/common/error.c b/src/common/error.c index b8dabdf6e..e03bee350 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -240,6 +240,7 @@ static const char *error_string_array[] = { [ ERROR_INDEX(LTTNG_ERR_UNSUPPORTED_DOMAIN) ] = "Unsupported domain used", [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) ] = "Operation does not apply to the process attribute tracker's tracking policy", [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD) ] = "Failed to create an event notifier group notification file descriptor", + [ ERROR_INDEX(LTTNG_ERR_INVALID_CAPTURE_EXPRESSION) ] = "Invalid capture expression", /* Last element */ [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code" -- 2.34.1 From 51dbe9857c314b9b0dc53b4f9c367f7db659a6b4 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Tue, 3 Nov 2020 18:04:34 -0500 Subject: [PATCH 06/16] Implement lttng_condition_event_rule_get_capture_bytecode_at_index MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I0478c3c1a5866f924fa8a5b49fdc61570a802989 --- include/lttng/condition/event-rule-internal.h | 5 +++ src/common/conditions/event-rule.c | 39 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/lttng/condition/event-rule-internal.h b/include/lttng/condition/event-rule-internal.h index 1f039c347..0f2f3fbf6 100644 --- a/include/lttng/condition/event-rule-internal.h +++ b/include/lttng/condition/event-rule-internal.h @@ -64,4 +64,9 @@ enum lttng_error_code lttng_condition_event_rule_generate_capture_descriptor_bytecode( struct lttng_condition *condition); +LTTNG_HIDDEN +const struct lttng_bytecode * +lttng_condition_event_rule_get_capture_bytecode_at_index( + const struct lttng_condition *condition, unsigned int index); + #endif /* LTTNG_CONDITION_EVENT_RULE_INTERNAL_H */ diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/event-rule.c index 8959652dd..dcc31ef86 100644 --- a/src/common/conditions/event-rule.c +++ b/src/common/conditions/event-rule.c @@ -972,3 +972,42 @@ lttng_condition_event_rule_generate_capture_descriptor_bytecode( end: return ret; } + +LTTNG_HIDDEN +const struct lttng_bytecode * +lttng_condition_event_rule_get_capture_bytecode_at_index( + const struct lttng_condition *condition, unsigned int index) +{ + const struct lttng_condition_event_rule *event_rule_cond = + container_of(condition, + const struct lttng_condition_event_rule, + parent); + struct lttng_capture_descriptor *desc = NULL; + struct lttng_bytecode *bytecode = NULL; + unsigned int count; + enum lttng_condition_status status; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition)) { + goto end; + } + + status = lttng_condition_event_rule_get_capture_descriptor_count( + condition, &count); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + if (index >= count) { + goto end; + } + + desc = lttng_dynamic_pointer_array_get_pointer( + &event_rule_cond->capture_descriptors, index); + if (desc == NULL) { + goto end; + } + + bytecode = desc->bytecode; +end: + return bytecode; +} -- 2.34.1 From f2e97f594f0c730f933b5f9840938d878b13e26b Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 8 Apr 2020 20:03:06 -0400 Subject: [PATCH 07/16] Generate capture bytecode on trigger reception MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: Ibf86096bd17be078cfc5e8bcd82347797389d448 Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- src/common/trigger.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/common/trigger.c b/src/common/trigger.c index 04b3e7f11..71162e79b 100644 --- a/src/common/trigger.c +++ b/src/common/trigger.c @@ -7,10 +7,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -955,12 +957,21 @@ enum lttng_error_code lttng_trigger_generate_bytecode( condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); + + /* Generate the filter bytecode. */ ret = lttng_event_rule_generate_filter_bytecode( event_rule, creds); if (ret != LTTNG_OK) { goto end; } + /* Generate the capture bytecode. */ + ret = lttng_condition_event_rule_generate_capture_descriptor_bytecode( + condition); + if (ret != LTTNG_OK) { + goto end; + } + ret = LTTNG_OK; break; } -- 2.34.1 From 7e397c55fae384e3218be22e13996d7989611f25 Mon Sep 17 00:00:00 2001 From: Francis Deslauriers Date: Thu, 11 Mar 2021 11:06:53 -0500 Subject: [PATCH 08/16] Fix: sessiond: return _OK on _SET_SESSION_SHM_PATH command success MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Issue ===== When running the `test_crash` tests, I witness the following warning: Warning: Command returned an invalid status code, returning unknown error: command type = 40, ret = 0 The `cmd_set_session_shm_path()` function is returning 0 on success instead of the expected LTTNG_OK which leads to this warning being printed. Fix === Return LTTNG_OK. Side note ========= I added the string version of the command name in the warning printing for easier debugging. Signed-off-by: Francis Deslauriers Signed-off-by: Jérémie Galarneau Change-Id: I79fe41afd49a1c701e760e415b72aabdc9b25f6c --- src/bin/lttng-sessiond/client.c | 5 ++++- src/bin/lttng-sessiond/cmd.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bin/lttng-sessiond/client.c b/src/bin/lttng-sessiond/client.c index d752fb94e..770a2ee44 100644 --- a/src/bin/lttng-sessiond/client.c +++ b/src/bin/lttng-sessiond/client.c @@ -2618,7 +2618,10 @@ static void *thread_manage_clients(void *data) } if (ret < LTTNG_OK || ret >= LTTNG_ERR_NR) { - WARN("Command returned an invalid status code, returning unknown error: command type = %d, ret = %d", cmd_ctx.lsm.cmd_type, ret); + WARN("Command returned an invalid status code, returning unknown error: " + "command type = %s (%d), ret = %d", + lttcomm_sessiond_command_str(cmd_ctx.lsm.cmd_type), + cmd_ctx.lsm.cmd_type, ret); ret = LTTNG_ERR_UNK; } diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 2d94204d3..a6f1a4db6 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -5124,7 +5124,7 @@ int cmd_set_session_shm_path(struct ltt_session *session, sizeof(session->shm_path)); session->shm_path[sizeof(session->shm_path) - 1] = '\0'; - return 0; + return LTTNG_OK; } /* -- 2.34.1 From 1136f41bfcb45da596fcb147b38b3181223f87e1 Mon Sep 17 00:00:00 2001 From: Francis Deslauriers Date: Thu, 11 Mar 2021 11:18:40 -0500 Subject: [PATCH 09/16] Cleanup: sessiond: fix comments to match with behavior MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Francis Deslauriers Signed-off-by: Jérémie Galarneau Change-Id: Ib8b7826d9b86df84eae048a7607ecc5a93aa5f56 --- src/bin/lttng-sessiond/cmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index a6f1a4db6..a2af8882f 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -4203,7 +4203,7 @@ end: * then regenerate the metadata. Live and per-pid sessions are not * supported and return an error. * - * Return 0 on success or else a LTTNG_ERR code. + * Return LTTNG_OK on success or else a LTTNG_ERR code. */ int cmd_regenerate_metadata(struct ltt_session *session) { @@ -4244,7 +4244,7 @@ end: * * Ask the tracer to regenerate a new statedump. * - * Return 0 on success or else a LTTNG_ERR code. + * Return LTTNG_OK on success or else a LTTNG_ERR code. */ int cmd_regenerate_statedump(struct ltt_session *session) { @@ -5477,7 +5477,7 @@ end: * 'activate' to false means deactivate the rotation schedule and validate that * 'new_value' has the same value as the currently active value. * - * Return 0 on success or else a positive LTTNG_ERR code. + * Return LTTNG_OK on success or else a positive LTTNG_ERR code. */ int cmd_rotation_set_schedule(struct ltt_session *session, bool activate, enum lttng_rotation_schedule_type schedule_type, -- 2.34.1 From d28fcdec589e6f0c98fed2f6c094defec58e2b24 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Fri, 1 May 2020 16:31:01 -0400 Subject: [PATCH 10/16] lttng-ctl: add event field value API MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This patch adds a new read-only event field value API to liblttng-ctl. It also adds the internal functions to create such objects. As of this patch, an event field value is one of: * An unsigned integer (64-bit). * A signed integer (64-bit). * An unsigned enumeration (64-bit). * A signed enumeration (64-bit). * A real number (double precision). * A string (UTF-8). * An array of zero or more event field values. The first purpose of this API will be to access the captured field values of a notification's trigger condition evaluation. An enumeration event field value conceptually is an integer event field value, therefore lttng_event_field_value_unsigned_int_get_value() works for an unsigned enumeration and lttng_event_field_value_signed_int_get_value() works for a signed enumeration. The compound event field values (array only, as of this patch) can return the `LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE` status when you access a contained event field value. This indicates that there is no field at this location. For the captured field value use case, it means the event did not have any field at that position considering the condition's capture descriptors. Internally, call lttng_event_field_value_array_append_unavailable() to append an unavailable event field value to an array field value. The lttng_event_field_value_array_get_element_at_index() function can also return an event field value of which the type, as returned by lttng_event_field_value_get_type(), is `LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN`. This is different from the `LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE` status: the event field value exists (which is why there's a valid `struct lttng_event_field_value *`), but its type is unknown by this version of the library. This makes it possible to add new event field value types in the future while remaining forward compatible. Note that enumeration labels will not visible to clients since the kernel tracer does not make their content accessible to the session daemon. Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau Change-Id: I04d327d9f50ff6c6e2ad1f5a1e56c3af25c92c15 Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- include/Makefile.am | 2 + include/lttng/event-field-value-internal.h | 190 +++++++ include/lttng/event-field-value.h | 229 ++++++++ include/lttng/lttng.h | 1 + src/common/Makefile.am | 1 + src/common/event-field-value.c | 592 +++++++++++++++++++++ 6 files changed, 1015 insertions(+) create mode 100644 include/lttng/event-field-value-internal.h create mode 100644 include/lttng/event-field-value.h create mode 100644 src/common/event-field-value.c diff --git a/include/Makefile.am b/include/Makefile.am index 54fec49a2..b75602b27 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -104,6 +104,7 @@ lttnginclude_HEADERS = \ lttng/domain.h \ lttng/event.h \ lttng/event-expr.h \ + lttng/event-field-value.h \ lttng/handle.h \ lttng/session.h \ lttng/lttng-error.h \ @@ -178,6 +179,7 @@ noinst_HEADERS = \ lttng/domain-internal.h \ lttng/event-internal.h \ lttng/event-expr-internal.h \ + lttng/event-field-value-internal.h \ lttng/rotate-internal.h \ lttng/ref-internal.h \ lttng/location-internal.h \ diff --git a/include/lttng/event-field-value-internal.h b/include/lttng/event-field-value-internal.h new file mode 100644 index 000000000..e0b67e94d --- /dev/null +++ b/include/lttng/event-field-value-internal.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_FIELD_VALUE_INTERNAL_H +#define LTTNG_EVENT_FIELD_VALUE_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_field_value { + enum lttng_event_field_value_type type; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT`. + */ +struct lttng_event_field_value_uint { + struct lttng_event_field_value parent; + uint64_t val; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT`. + */ +struct lttng_event_field_value_int { + struct lttng_event_field_value parent; + int64_t val; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` and + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM` (base). + */ +struct lttng_event_field_value_enum { + struct lttng_event_field_value parent; + + /* + * Array of `char *` (owned by this). + */ + struct lttng_dynamic_pointer_array labels; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM`. + */ +struct lttng_event_field_value_enum_uint { + struct lttng_event_field_value_enum parent; + uint64_t val; +}; + +/* + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`. + */ +struct lttng_event_field_value_enum_int { + struct lttng_event_field_value_enum parent; + int64_t val; +}; + +/* `LTTNG_EVENT_FIELD_VALUE_TYPE_REAL` */ +struct lttng_event_field_value_real { + struct lttng_event_field_value parent; + double val; +}; + +/* `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING` */ +struct lttng_event_field_value_string { + struct lttng_event_field_value parent; + + /* Owned by this */ + char *val; +}; + +/* `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING` */ +struct lttng_event_field_value_array { + struct lttng_event_field_value parent; + + /* + * Array of `struct lttng_event_field_value *` (owned by this). + * + * A `NULL` element means it's unavailable + * (`LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE` status). + */ + struct lttng_dynamic_pointer_array elems; +}; + +/* + * This is internal since the session daemon knows nothing about the + * enumeration fields produced by the kernel tracer. Indeed, the kernel tracer + * manages its own metadata which remains opaque to the rest of the toolchain. + * + * Enumerations could be supported for the user space tracer, but it is not the + * case right now. + */ + +/* + * Sets `*count` to the number of labels of the enumeration event field + * value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` or + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`. + * * `count` is `NULL`. + */ +LTTNG_HIDDEN +enum lttng_event_field_value_status +lttng_event_field_value_enum_get_label_count( + const struct lttng_event_field_value *field_val, + unsigned int *count); + +/* + * Returns the label at index `index` of the enumeration event field + * value `field_val`, or `NULL` if: + * + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` or + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`. + * * `index` is greater than or equal to the label count of `field_val`, + * as returned by lttng_event_field_value_enum_get_label_count(). + */ +LTTNG_HIDDEN +const char *lttng_event_field_value_enum_get_label_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_uint_create( + uint64_t val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_int_create( + int64_t val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_enum_uint_create( + uint64_t val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_enum_int_create( + int64_t val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_real_create(double val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_string_create( + const char *val); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_string_create_with_size( + const char *val, size_t size); + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_array_create(void); + +LTTNG_HIDDEN +int lttng_event_field_value_enum_append_label( + struct lttng_event_field_value *field_val, const char *label); + +LTTNG_HIDDEN +int lttng_event_field_value_enum_append_label_with_size( + struct lttng_event_field_value *field_val, const char *label, + size_t size); + +LTTNG_HIDDEN +int lttng_event_field_value_array_append( + struct lttng_event_field_value *array_field_val, + struct lttng_event_field_value *field_val); + +LTTNG_HIDDEN +int lttng_event_field_value_array_append_unavailable( + struct lttng_event_field_value *array_field_val); + +LTTNG_HIDDEN +void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val); + +#endif /* LTTNG_EVENT_FIELD_VALUE_INTERNAL_H */ diff --git a/include/lttng/event-field-value.h b/include/lttng/event-field-value.h new file mode 100644 index 000000000..db4a4fb3b --- /dev/null +++ b/include/lttng/event-field-value.h @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_FIELD_VALUE_H +#define LTTNG_EVENT_FIELD_VALUE_H + +#include + +struct lttng_event_field_value; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Types of a event field value expression. + */ +enum lttng_event_field_value_type { + /* + * Unknown. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN = -2, + + /* + * Returned by lttng_event_field_value_get_type() with an + * invalid parameter. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID = -1, + + /* + * Unsigned integer event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT = 0, + + /* + * Signed integer event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT = 1, + + /* + * Unsigned enumeration event field value. + * + * This type conceptually inherits + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT`. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM = 2, + + /* + * Signed enumeration event field value. + * + * This type conceptually inherits + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT`. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM = 3, + + /* + * Real event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_REAL = 4, + + /* + * String event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_STRING = 5, + + /* + * Array event field value. + */ + LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY = 6, +}; + +/* + * Event field value API status codes. + */ +enum lttng_event_field_value_status { + /* + * Event field value is not available. + */ + LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE = -2, + + /* + * Invalid parameter. + */ + LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID = -1, + + /* + * Success. + */ + LTTNG_EVENT_FIELD_VALUE_STATUS_OK = 0, +}; + +/* + * Returns the type of the event field value `field_val`, or: + * + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN`: + * The type of `field_val` is unknown as of this version of the + * LTTng control library. + * + * `LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID`: + * `field_val` is `NULL`. + */ +extern enum lttng_event_field_value_type lttng_event_field_value_get_type( + const struct lttng_event_field_value *field_val); + +/* + * Sets `*val` to the raw value of the unsigned integer/enumeration + * event field value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT` or + * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM`. + * * `val` is `NULL`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_unsigned_int_get_value( + const struct lttng_event_field_value *field_val, uint64_t *val); + +/* + * Sets `*val` to the raw value of the signed integer/enumeration event + * field value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT` or + * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`. + * * `val` is `NULL`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_signed_int_get_value( + const struct lttng_event_field_value *field_val, int64_t *val); + +/* + * Sets `*val` to the raw value of the real event field value + * `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_REAL`. + * * `val` is `NULL`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_real_get_value( + const struct lttng_event_field_value *field_val, double *val); + +/* + * Returns the raw value (an UTF-8 C string) of the string event field + * value `field_val`, or `NULL` if: + * + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING`. + */ +extern const char *lttng_event_field_value_string_get_value( + const struct lttng_event_field_value *field_val); + +/* + * Sets `*length` to the length (the number of contained elements) of + * the array event field value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY`. + * * `length` is `NULL`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_array_get_length( + const struct lttng_event_field_value *field_val, + unsigned int *length); + +/* + * Sets `*elem_field_val` to the event field value at index `index` in + * the array event field value `field_val`. + * + * Returns: + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`: + * * `field_val` is `NULL`. + * * The type of `field_val` is not + * `LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY`. + * * `index` is greater than or equal to the length of `field_val`, + * as returned by lttng_event_field_value_array_get_length(). + * + * `LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE`: + * * No event field value exists at index `index` within + * `field_val`. + */ +extern enum lttng_event_field_value_status +lttng_event_field_value_array_get_element_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index, + const struct lttng_event_field_value **elem_field_val); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_FIELD_VALUE_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index 2b8a56e37..c665d3c9b 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/src/common/Makefile.am b/src/common/Makefile.am index c7220f703..4b549aa08 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -60,6 +60,7 @@ libcommon_la_SOURCES = \ evaluation.c \ event.c \ event-expr-to-bytecode.c event-expr-to-bytecode.h \ + event-field-value.c \ event-rule/event-rule.c \ event-rule/kprobe.c \ event-rule/syscall.c \ diff --git a/src/common/event-field-value.c b/src/common/event-field-value.c new file mode 100644 index 000000000..d7687880f --- /dev/null +++ b/src/common/event-field-value.c @@ -0,0 +1,592 @@ +/* + * event-field-value.c + * + * Linux Trace Toolkit Control Library + * + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include + +#include +#include +#include + +static +struct lttng_event_field_value *create_empty_field_val( + enum lttng_event_field_value_type type, size_t size) +{ + struct lttng_event_field_value *field_val; + + field_val = zmalloc(size); + if (!field_val) { + goto end; + } + + field_val->type = type; + +end: + return field_val; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_uint_create( + uint64_t val) +{ + struct lttng_event_field_value_uint *field_val; + + field_val = container_of(create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT, + sizeof(*field_val)), + struct lttng_event_field_value_uint, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_int_create( + int64_t val) +{ + struct lttng_event_field_value_int *field_val; + + field_val = container_of(create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT, + sizeof(*field_val)), + struct lttng_event_field_value_int, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +static +struct lttng_event_field_value_enum *create_enum_field_val( + enum lttng_event_field_value_type type, size_t size) +{ + struct lttng_event_field_value_enum *field_val; + + field_val = container_of(create_empty_field_val(type, size), + struct lttng_event_field_value_enum, parent); + if (!field_val) { + goto error; + } + + lttng_dynamic_pointer_array_init(&field_val->labels, free); + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return field_val; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_enum_uint_create( + uint64_t val) +{ + struct lttng_event_field_value_enum_uint *field_val; + + field_val = container_of(create_enum_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM, + sizeof(*field_val)), + struct lttng_event_field_value_enum_uint, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent.parent); + +end: + return &field_val->parent.parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_enum_int_create( + int64_t val) +{ + struct lttng_event_field_value_enum_int *field_val; + + field_val = container_of(create_enum_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM, + sizeof(*field_val)), + struct lttng_event_field_value_enum_int, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent.parent); + +end: + return &field_val->parent.parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_real_create(double val) +{ + struct lttng_event_field_value_real *field_val = container_of( + create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_REAL, + sizeof(*field_val)), + struct lttng_event_field_value_real, parent); + + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_string_create_with_size( + const char *val, size_t size) +{ + struct lttng_event_field_value_string *field_val = container_of( + create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_STRING, + sizeof(*field_val)), + struct lttng_event_field_value_string, parent); + + if (!field_val) { + goto error; + } + + assert(val); + field_val->val = strndup(val, size); + if (!field_val->val) { + goto error; + } + + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_string_create( + const char *val) +{ + assert(val); + return lttng_event_field_value_string_create_with_size(val, + strlen(val)); +} + +static +void destroy_field_val(void *field_val) +{ + lttng_event_field_value_destroy(field_val); +} + +LTTNG_HIDDEN +struct lttng_event_field_value *lttng_event_field_value_array_create(void) +{ + struct lttng_event_field_value_array *field_val = container_of( + create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY, + sizeof(*field_val)), + struct lttng_event_field_value_array, parent); + + if (!field_val) { + goto error; + } + + lttng_dynamic_pointer_array_init(&field_val->elems, destroy_field_val); + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +LTTNG_HIDDEN +void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val) +{ + if (!field_val) { + goto end; + } + + switch (field_val->type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + { + struct lttng_event_field_value_enum *enum_field_val = + container_of(field_val, + struct lttng_event_field_value_enum, parent); + + lttng_dynamic_pointer_array_reset(&enum_field_val->labels); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: + { + struct lttng_event_field_value_string *str_field_val = + container_of(field_val, + struct lttng_event_field_value_string, parent); + + free(str_field_val->val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: + { + struct lttng_event_field_value_array *array_field_expr = + container_of(field_val, + struct lttng_event_field_value_array, + parent); + + lttng_dynamic_pointer_array_reset(&array_field_expr->elems); + break; + } + default: + break; + } + + free(field_val); + +end: + return; +} + +LTTNG_HIDDEN +int lttng_event_field_value_enum_append_label_with_size( + struct lttng_event_field_value *field_val, + const char *label, size_t size) +{ + int ret; + char *new_label; + + assert(field_val); + assert(label); + new_label = strndup(label, size); + if (!new_label) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &container_of(field_val, + struct lttng_event_field_value_enum, parent)->labels, + new_label); + if (ret == 0) { + new_label = NULL; + } + +end: + free(new_label); + return ret; +} + +LTTNG_HIDDEN +int lttng_event_field_value_enum_append_label( + struct lttng_event_field_value *field_val, + const char *label) +{ + assert(label); + return lttng_event_field_value_enum_append_label_with_size(field_val, + label, strlen(label)); +} + +LTTNG_HIDDEN +int lttng_event_field_value_array_append( + struct lttng_event_field_value *array_field_val, + struct lttng_event_field_value *field_val) +{ + assert(array_field_val); + assert(field_val); + return lttng_dynamic_pointer_array_add_pointer( + &container_of(array_field_val, + struct lttng_event_field_value_array, parent)->elems, + field_val); +} + +LTTNG_HIDDEN +int lttng_event_field_value_array_append_unavailable( + struct lttng_event_field_value *array_field_val) +{ + assert(array_field_val); + return lttng_dynamic_pointer_array_add_pointer( + &container_of(array_field_val, + struct lttng_event_field_value_array, parent)->elems, + NULL); +} + +enum lttng_event_field_value_type lttng_event_field_value_get_type( + const struct lttng_event_field_value *field_val) +{ + enum lttng_event_field_value_type type; + + if (!field_val) { + type = LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID; + goto end; + } + + type = field_val->type; + +end: + return type; +} + +enum lttng_event_field_value_status +lttng_event_field_value_unsigned_int_get_value( + const struct lttng_event_field_value *field_val, uint64_t *val) +{ + enum lttng_event_field_value_status status; + + if (!field_val || !val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + switch (field_val->type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: + *val = container_of(field_val, + const struct lttng_event_field_value_uint, + parent)->val; + break; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + *val = container_of( + container_of(field_val, + const struct lttng_event_field_value_enum, + parent), + const struct lttng_event_field_value_enum_uint, + parent)->val; + break; + default: + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status +lttng_event_field_value_signed_int_get_value( + const struct lttng_event_field_value *field_val, int64_t *val) +{ + enum lttng_event_field_value_status status; + + if (!field_val || !val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + switch (field_val->type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: + *val = container_of(field_val, + const struct lttng_event_field_value_int, + parent)->val; + break; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + *val = container_of( + container_of(field_val, + const struct lttng_event_field_value_enum, + parent), + const struct lttng_event_field_value_enum_int, + parent)->val; + break; + default: + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status +lttng_event_field_value_real_get_value( + const struct lttng_event_field_value *field_val, double *val) +{ + enum lttng_event_field_value_status status; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_REAL || + !val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *val = container_of(field_val, + const struct lttng_event_field_value_real, parent)->val; + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +static +bool is_enum_field_val(const struct lttng_event_field_value *field_val) +{ + return field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM || + field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM; +} + +enum lttng_event_field_value_status +lttng_event_field_value_enum_get_label_count( + const struct lttng_event_field_value *field_val, + unsigned int *count) +{ + enum lttng_event_field_value_status status; + + if (!field_val || !is_enum_field_val(field_val) || !count) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *count = (unsigned int) lttng_dynamic_pointer_array_get_count( + &container_of(field_val, + const struct lttng_event_field_value_enum, + parent)->labels); + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +const char *lttng_event_field_value_enum_get_label_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index) +{ + const char *ret; + const struct lttng_event_field_value_enum *enum_field_val; + + if (!field_val || !is_enum_field_val(field_val)) { + ret = NULL; + goto end; + } + + enum_field_val = container_of(field_val, + const struct lttng_event_field_value_enum, parent); + + if (index >= lttng_dynamic_pointer_array_get_count(&enum_field_val->labels)) { + ret = NULL; + goto end; + } + + ret = lttng_dynamic_pointer_array_get_pointer(&enum_field_val->labels, + index); + +end: + return ret; +} + +const char *lttng_event_field_value_string_get_value( + const struct lttng_event_field_value *field_val) +{ + const char *ret; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_STRING) { + ret = NULL; + goto end; + } + + ret = container_of(field_val, + const struct lttng_event_field_value_string, parent)->val; + +end: + return ret; +} + +enum lttng_event_field_value_status lttng_event_field_value_array_get_length( + const struct lttng_event_field_value *field_val, + unsigned int *length) +{ + enum lttng_event_field_value_status status; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY || + !length) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *length = (unsigned int) lttng_dynamic_pointer_array_get_count( + &container_of(field_val, + const struct lttng_event_field_value_array, + parent)->elems); + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status +lttng_event_field_value_array_get_element_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index, + const struct lttng_event_field_value **elem_field_val) +{ + enum lttng_event_field_value_status status; + const struct lttng_event_field_value_array *array_field_val; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY || + !elem_field_val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + array_field_val = container_of(field_val, + const struct lttng_event_field_value_array, parent); + + if (index >= lttng_dynamic_pointer_array_get_count(&array_field_val->elems)) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *elem_field_val = lttng_dynamic_pointer_array_get_pointer( + &array_field_val->elems, index); + if (*elem_field_val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + } else { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE; + } + +end: + return status; +} -- 2.34.1 From a17b133bc868e046f285146e26a5bbe2a569b892 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Fri, 12 Mar 2021 14:54:52 -0500 Subject: [PATCH 11/16] Fix: sessiond: off-by-one poll check when draining an event notifier MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit When an event source is removed (on the death of an application), the notification thread "drains" any remaining notifications from the event notifier pipe. In doing so, it creates a new poll set containing the event source to check if messages are left in the event notification pipe. The invocation of `LTTNG_POLL_GETEV(&events, 1)` means to check the events pending for the first (and only) fd in the poll set. This check is off by one since `0` should be used. For some reason, this worked everywhere except when using a 32-bit userland on a 64-bit kernel (on x86_64). Signed-off-by: Jérémie Galarneau Change-Id: I6f274fdd7c80d5676fd48ae20a14adb3cc010142 --- src/bin/lttng-sessiond/notification-thread-events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/lttng-sessiond/notification-thread-events.c b/src/bin/lttng-sessiond/notification-thread-events.c index 6bd799279..747902612 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.c +++ b/src/bin/lttng-sessiond/notification-thread-events.c @@ -2017,7 +2017,7 @@ int drain_event_notifier_notification_pipe( * the pipe is closed but empty. */ ret = lttng_poll_wait_interruptible(&events, 0); - if (ret == 0 || (LTTNG_POLL_GETEV(&events, 1) & LPOLLIN) == 0) { + if (ret == 0 || (LTTNG_POLL_GETEV(&events, 0) & LPOLLIN) == 0) { /* No more notification to be read on this pipe. */ ret = 0; goto end; -- 2.34.1 From 1a3d8cf37eaa418f7c4a7629cd9fe058db3d1054 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Thu, 14 May 2020 12:44:38 -0400 Subject: [PATCH 12/16] UST abi: add `struct lttng_ust_capture_bytecode` MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I512bb3c5e0232f70b860d33c72d61176303146bc Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- src/bin/lttng-sessiond/ust-abi-internal.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bin/lttng-sessiond/ust-abi-internal.h b/src/bin/lttng-sessiond/ust-abi-internal.h index 3892a7f5d..0f12be083 100644 --- a/src/bin/lttng-sessiond/ust-abi-internal.h +++ b/src/bin/lttng-sessiond/ust-abi-internal.h @@ -263,6 +263,16 @@ struct lttng_ust_filter_bytecode { 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; -- 2.34.1 From f2eafd2d6170ab8974bb8ceb1eec6d89ab969623 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Tue, 28 Apr 2020 10:37:55 -0400 Subject: [PATCH 13/16] sessiond: ust-app: add utils to add a capture bytecode to a ust object MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: If5b5954cadfafbf851e3fc3a9e37c76c3840a4a3 Depends-on: lttng-ust: I8423c510bf6af2f9bf85256e8d6f931d36f7054b --- src/bin/lttng-sessiond/ust-app.c | 87 ++++++++++++++++++++--- src/bin/lttng-sessiond/ust-ctl-internal.h | 2 + 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index f7d8c985b..01fa7fc93 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -1331,15 +1331,15 @@ error: * * Return allocated filter or NULL on error. */ -static struct lttng_ust_filter_bytecode *create_ust_bytecode_from_bytecode( - const struct lttng_bytecode *orig_f) +static struct lttng_ust_filter_bytecode * +create_ust_filter_bytecode_from_bytecode(const struct lttng_bytecode *orig_f) { struct lttng_ust_filter_bytecode *filter = NULL; - /* Copy filter bytecode */ + /* Copy filter bytecode. */ filter = zmalloc(sizeof(*filter) + orig_f->len); if (!filter) { - PERROR("zmalloc alloc ust filter bytecode"); + PERROR("Failed to allocate lttng_ust_filter_bytecode: bytecode len = %" PRIu32 " bytes", orig_f->len); goto error; } @@ -1350,6 +1350,30 @@ error: return filter; } +/* + * Create a liblttng-ust capture bytecode from given bytecode. + * + * Return allocated filter or NULL on error. + */ +static struct lttng_ust_capture_bytecode * +create_ust_capture_bytecode_from_bytecode(const struct lttng_bytecode *orig_f) +{ + struct lttng_ust_capture_bytecode *capture = NULL; + + /* Copy capture bytecode. */ + capture = zmalloc(sizeof(*capture) + orig_f->len); + if (!capture) { + PERROR("Failed to allocate lttng_ust_capture_bytecode: bytecode len = %" PRIu32 " bytes", orig_f->len); + goto error; + } + + assert(sizeof(struct lttng_bytecode) == + sizeof(struct lttng_ust_capture_bytecode)); + memcpy(capture, orig_f, sizeof(*capture) + orig_f->len); +error: + return capture; +} + /* * Find an ust_app using the sock and return it. RCU read side lock must be * held before calling this helper function. @@ -1519,7 +1543,7 @@ static int set_ust_object_filter(struct ust_app *app, health_code_update(); - ust_bytecode = create_ust_bytecode_from_bytecode(bytecode); + ust_bytecode = create_ust_filter_bytecode_from_bytecode(bytecode); if (!ust_bytecode) { ret = -LTTNG_ERR_NOMEM; goto error; @@ -1530,8 +1554,8 @@ static int set_ust_object_filter(struct ust_app *app, pthread_mutex_unlock(&app->sock_lock); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app set object filter failed for object %p of app (pid: %d) " - "with ret %d", ust_object, app->pid, ret); + ERR("UST app set object filter failed: object = %p of app pid = %d, ret = %d", + ust_object, app->pid, ret); } else { /* * This is normal behavior, an application can die during the @@ -1544,7 +1568,54 @@ static int set_ust_object_filter(struct ust_app *app, goto error; } - DBG2("UST filter successfully set for object %p", ust_object); + DBG2("UST filter successfully set: object = %p", ust_object); + +error: + health_code_update(); + free(ust_bytecode); + return ret; +} + +/* + * Set a capture bytecode for the passed object. + */ +static int set_ust_capture(struct ust_app *app, + const struct lttng_bytecode *bytecode, + struct lttng_ust_object_data *ust_object) +{ + int ret; + struct lttng_ust_capture_bytecode *ust_bytecode = NULL; + + health_code_update(); + + ust_bytecode = create_ust_capture_bytecode_from_bytecode(bytecode); + if (!ust_bytecode) { + ret = -LTTNG_ERR_NOMEM; + goto error; + } + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_set_capture(app->sock, ust_bytecode, + ust_object); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app set object capture failed: object = %p of app pid = %d, ret = %d", + ust_object, app->pid, ret); + } else { + /* + * This is normal behavior, an application can die during the + * creation process. Don't report an error so the execution can + * continue normally. + */ + ret = 0; + DBG3("Failed to set UST app object capture. Application is dead."); + } + + goto error; + } + + DBG2("UST capture successfully set: object = %p", ust_object); error: health_code_update(); diff --git a/src/bin/lttng-sessiond/ust-ctl-internal.h b/src/bin/lttng-sessiond/ust-ctl-internal.h index 8d7217f31..7a89dbc26 100644 --- a/src/bin/lttng-sessiond/ust-ctl-internal.h +++ b/src/bin/lttng-sessiond/ust-ctl-internal.h @@ -79,6 +79,8 @@ int ustctl_set_filter(int sock, struct lttng_ust_filter_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); +int ustctl_set_capture(int sock, struct lttng_ust_capture_bytecode *bytecode, + struct lttng_ust_object_data *obj_data); int ustctl_enable(int sock, struct lttng_ust_object_data *object); int ustctl_disable(int sock, struct lttng_ust_object_data *object); -- 2.34.1 From f83be61d63aaf5ea70b850737da436fe4ae80e51 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 8 Apr 2020 21:40:41 -0400 Subject: [PATCH 14/16] sessiond: ust-app: set capture bytecode on event notifier on creation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I82d2dd27da92c4dc1587b22628f8e1a8097e06c2 Depends-on: lttng-ust: I8423c510bf6af2f9bf85256e8d6f931d36f7054b --- src/bin/lttng-sessiond/ust-app.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index 01fa7fc93..cc027be99 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "buffer-registry.h" @@ -2077,6 +2078,8 @@ static int create_ust_event_notifier(struct ust_app *app, const struct lttng_condition *condition = NULL; struct lttng_ust_event_notifier event_notifier; const struct lttng_event_rule *event_rule = NULL; + unsigned int capture_bytecode_count = 0, i; + enum lttng_condition_status cond_status; health_code_update(); assert(app->event_notifier_group.object); @@ -2146,6 +2149,23 @@ static int create_ust_event_notifier(struct ust_app *app, } } + /* Set the capture bytecodes. */ + cond_status = lttng_condition_event_rule_get_capture_descriptor_count( + condition, &capture_bytecode_count); + assert(cond_status == LTTNG_CONDITION_STATUS_OK); + + for (i = 0; i < capture_bytecode_count; i++) { + const struct lttng_bytecode *capture_bytecode = + lttng_condition_event_rule_get_capture_bytecode_at_index( + condition, i); + + ret = set_ust_capture(app, capture_bytecode, + ua_event_notifier_rule->obj); + if (ret < 0) { + goto error; + } + } + /* * We now need to explicitly enable the event, since it * is disabled at creation. -- 2.34.1 From 11f6ce94d8fb73f017888681aaba5d7df55fc735 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Thu, 16 Apr 2020 16:31:16 -0400 Subject: [PATCH 15/16] Use bytecode seqnum to force the evaluation ordering of capture bytecode MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This ensure that the resulting payload is ordered as expected. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I74c87cdc7098030e05b060f287561b4177739cc1 Depends-on: lttng-ust: I8423c510bf6af2f9bf85256e8d6f931d36f7054b --- src/bin/lttng-sessiond/ust-app.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index cc027be99..43864bd0f 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -1579,9 +1579,12 @@ error: /* * Set a capture bytecode for the passed object. + * The sequence number enforces the ordering at runtime and on reception of + * the captured payloads. */ static int set_ust_capture(struct ust_app *app, const struct lttng_bytecode *bytecode, + unsigned int capture_seqnum, struct lttng_ust_object_data *ust_object) { int ret; @@ -1595,6 +1598,11 @@ static int set_ust_capture(struct ust_app *app, goto error; } + /* + * Set the sequence number to ensure the capture of fields is ordered. + */ + ust_bytecode->seqnum = capture_seqnum; + pthread_mutex_lock(&app->sock_lock); ret = ustctl_set_capture(app->sock, ust_bytecode, ust_object); @@ -2159,7 +2167,7 @@ static int create_ust_event_notifier(struct ust_app *app, lttng_condition_event_rule_get_capture_bytecode_at_index( condition, i); - ret = set_ust_capture(app, capture_bytecode, + ret = set_ust_capture(app, capture_bytecode, i, ua_event_notifier_rule->obj); if (ret < 0) { goto error; -- 2.34.1 From c3e68e71e0bca1c067bf24447163ac6850a9b09e Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Mon, 8 Mar 2021 15:59:01 -0500 Subject: [PATCH 16/16] SoW-2020-0002: Trace Hit Counters MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: Id52749b59969276b5390f4494df48940662d5d8c --- .gitignore | 1 + DO_NO_MERGE.txt | 3 + configure.ac | 6 + doc/man/Makefile.am | 6 +- doc/man/lttng-add-map.1.txt | 81 + doc/man/lttng-add-trigger.1.txt | 80 +- doc/man/lttng-disable-map.1.txt | 51 + doc/man/lttng-enable-map.1.txt | 48 + doc/man/lttng-list.1.txt | 11 +- doc/man/lttng-view-map.1.txt | 58 + include/Makefile.am | 98 +- include/lttng/action/action.h | 1 + include/lttng/action/group-internal.h | 5 + include/lttng/action/incr-value-internal.h | 42 + include/lttng/action/incr-value.h | 79 + include/lttng/condition/condition.h | 3 +- include/lttng/condition/evaluation-internal.h | 5 +- include/lttng/condition/event-rule-internal.h | 72 - include/lttng/condition/on-event-internal.h | 113 + .../condition/{event-rule.h => on-event.h} | 53 +- include/lttng/event-rule/event-rule.h | 6 +- .../event-rule/kernel-function-internal.h | 39 + include/lttng/event-rule/kernel-function.h | 75 + ...obe-internal.h => kernel-probe-internal.h} | 14 +- .../event-rule/{kprobe.h => kernel-probe.h} | 27 +- include/lttng/event-rule/syscall.h | 2 + .../lttng/event-rule/tracepoint-internal.h | 15 +- include/lttng/event-rule/tracepoint.h | 52 +- ...-internal.h => userspace-probe-internal.h} | 16 +- .../{uprobe.h => userspace-probe.h} | 27 +- include/lttng/kernel-function-internal.h | 106 + include/lttng/kernel-function.h | 87 + include/lttng/log-level-rule-internal.h | 64 + include/lttng/log-level-rule.h | 89 + include/lttng/lttng-error.h | 14 + include/lttng/lttng.h | 9 +- include/lttng/map-key-internal.h | 118 + include/lttng/map-key.h | 35 + include/lttng/map/map-internal.h | 228 ++ include/lttng/map/map-query-internal.h | 148 + include/lttng/map/map-query.h | 90 + include/lttng/map/map.h | 258 ++ include/lttng/trigger/trigger-internal.h | 9 + src/bin/lttng-sessiond/Makefile.am | 5 +- src/bin/lttng-sessiond/action-executor.c | 26 +- src/bin/lttng-sessiond/agent.c | 29 +- src/bin/lttng-sessiond/buffer-registry.c | 240 ++ src/bin/lttng-sessiond/buffer-registry.h | 47 + src/bin/lttng-sessiond/clear.c | 1 - src/bin/lttng-sessiond/client.c | 219 + src/bin/lttng-sessiond/cmd.c | 999 ++++- src/bin/lttng-sessiond/cmd.h | 16 + src/bin/lttng-sessiond/condition-internal.c | 45 +- src/bin/lttng-sessiond/condition-internal.h | 2 + src/bin/lttng-sessiond/dispatch.c | 3 + .../event-notifier-error-accounting.c | 820 ++++ .../event-notifier-error-accounting.h | 70 + src/bin/lttng-sessiond/event.c | 391 +- src/bin/lttng-sessiond/event.h | 42 + src/bin/lttng-sessiond/kernel.c | 579 ++- src/bin/lttng-sessiond/kernel.h | 20 + src/bin/lttng-sessiond/main.c | 50 +- src/bin/lttng-sessiond/map.c | 442 +++ src/bin/lttng-sessiond/map.h | 45 + src/bin/lttng-sessiond/modprobe.c | 31 +- .../notification-thread-commands.c | 44 +- .../notification-thread-commands.h | 13 + .../notification-thread-events.c | 640 ++- .../notification-thread-internal.h | 14 +- src/bin/lttng-sessiond/notification-thread.c | 20 + src/bin/lttng-sessiond/register.c | 2 +- src/bin/lttng-sessiond/save.c | 222 ++ src/bin/lttng-sessiond/session.h | 1 + src/bin/lttng-sessiond/sessiond-config.c | 1 + src/bin/lttng-sessiond/sessiond-config.h | 2 + src/bin/lttng-sessiond/testpoint.h | 2 + src/bin/lttng-sessiond/trace-kernel.c | 354 +- src/bin/lttng-sessiond/trace-kernel.h | 57 +- src/bin/lttng-sessiond/trace-ust.c | 259 +- src/bin/lttng-sessiond/trace-ust.h | 82 +- src/bin/lttng-sessiond/ust-abi-internal.h | 4 +- src/bin/lttng-sessiond/ust-app.c | 3526 +++++++++++++++-- src/bin/lttng-sessiond/ust-app.h | 139 +- src/bin/lttng-sessiond/ust-consumer.c | 8 +- src/bin/lttng-sessiond/ust-ctl-internal.h | 3 +- src/bin/lttng-sessiond/ust-registry.c | 637 ++- src/bin/lttng-sessiond/ust-registry.h | 123 +- src/bin/lttng/Makefile.am | 7 +- src/bin/lttng/command.h | 4 + src/bin/lttng/commands/add_map.c | 288 ++ src/bin/lttng/commands/add_trigger.c | 373 +- src/bin/lttng/commands/disable_map.c | 175 + src/bin/lttng/commands/enable_map.c | 175 + src/bin/lttng/commands/list.c | 127 + src/bin/lttng/commands/list_triggers.c | 243 +- src/bin/lttng/commands/view_map.c | 651 +++ src/bin/lttng/lttng.c | 8 + src/common/Makefile.am | 15 +- src/common/actions/action.c | 7 + src/common/actions/group.c | 8 + src/common/actions/incr-value.c | 508 +++ src/common/conditions/condition.c | 8 +- .../conditions/{event-rule.c => on-event.c} | 687 +++- src/common/config/config-session-abi.h | 11 + src/common/config/session-config.c | 383 +- src/common/config/session.xsd | 43 + src/common/error.c | 14 + src/common/evaluation.c | 14 +- src/common/event-rule/event-rule.c | 29 +- src/common/event-rule/kernel-function.c | 437 ++ .../event-rule/{kprobe.c => kernel-probe.c} | 188 +- src/common/event-rule/syscall.c | 9 + src/common/event-rule/tracepoint.c | 294 +- .../{uprobe.c => userspace-probe.c} | 189 +- src/common/hashtable/hashtable.c | 21 +- src/common/index-allocator.c | 121 + src/common/index-allocator.h | 36 + src/common/kernel-ctl/kernel-ctl.c | 77 + src/common/kernel-ctl/kernel-ctl.h | 16 + src/common/kernel-ctl/kernel-ioctl.h | 23 + src/common/kernel-function.c | 726 ++++ src/common/log-level-rule.c | 298 ++ src/common/lttng-kernel.h | 126 +- src/common/map-key/map-key.c | 561 +++ src/common/map-query.c | 674 ++++ src/common/map.c | 1239 ++++++ src/common/notification.c | 2 +- src/common/sessiond-comm/sessiond-comm.h | 31 + src/{bin/lttng-sessiond => common}/shm.c | 38 + src/{bin/lttng-sessiond => common}/shm.h | 2 + src/common/trigger.c | 110 +- src/common/ust-consumer/ust-consumer.c | 45 +- src/common/utils.c | 10 + src/common/utils.h | 1 + src/lib/lttng-ctl/lttng-ctl.c | 333 +- tests/regression/Makefile.am | 5 + tests/regression/kernel/Makefile.am | 19 +- tests/regression/kernel/test_kernel_function | 62 + tests/regression/tools/Makefile.am | 2 +- tests/regression/tools/map/Makefile.am | 30 + tests/regression/tools/map/map_base_test.sh | 578 +++ tests/regression/tools/map/test_map_kernel | 322 ++ tests/regression/tools/map/test_map_query.c | 125 + tests/regression/tools/map/test_map_ust | 416 ++ .../regression/tools/notification/Makefile.am | 19 +- .../tools/notification/base_client.c | 52 +- .../tools/notification/notification.c | 851 +++- .../tools/notification/sessiond_testpoints.c | 113 + .../test_notification_kernel_capture | 55 + .../notification/test_notification_multi_app | 11 +- ...test_notification_notifier_discarded_count | 254 ++ .../test_notification_ust_capture | 40 + tests/regression/tools/save-load/Makefile.am | 3 +- .../tools/save-load/load-42-maps.lttng | 67 + tests/regression/tools/save-load/test_load | 23 +- tests/regression/tools/save-load/test_save | 19 +- .../tools/trigger/start-stop/test_start_stop | 49 +- .../tools/trigger/test_add_trigger_cli | 10 +- .../tools/trigger/test_list_triggers_cli | 214 +- .../tools/trigger/test_remove_trigger_cli | 19 +- .../tools/trigger/utils/notification-client.c | 4 +- tests/unit/Makefile.am | 101 +- tests/unit/test_action.c | 120 + tests/unit/test_condition.c | 30 +- tests/unit/test_event_rule.c | 84 +- tests/unit/test_log_level_rule.c | 187 + tests/unit/test_map.c | 439 ++ tests/unit/test_map_key.c | 132 + tests/unit/test_map_query.c | 291 ++ tests/unit/test_ust_data.c | 9 +- .../gen-syscall-events/gen-syscall-events.c | 1 - tests/utils/utils.sh | 115 +- 172 files changed, 24982 insertions(+), 1893 deletions(-) create mode 100644 DO_NO_MERGE.txt create mode 100644 doc/man/lttng-add-map.1.txt create mode 100644 doc/man/lttng-disable-map.1.txt create mode 100644 doc/man/lttng-enable-map.1.txt create mode 100644 doc/man/lttng-view-map.1.txt create mode 100644 include/lttng/action/incr-value-internal.h create mode 100644 include/lttng/action/incr-value.h delete mode 100644 include/lttng/condition/event-rule-internal.h create mode 100644 include/lttng/condition/on-event-internal.h rename include/lttng/condition/{event-rule.h => on-event.h} (71%) create mode 100644 include/lttng/event-rule/kernel-function-internal.h create mode 100644 include/lttng/event-rule/kernel-function.h rename include/lttng/event-rule/{kprobe-internal.h => kernel-probe-internal.h} (66%) rename include/lttng/event-rule/{kprobe.h => kernel-probe.h} (74%) rename include/lttng/event-rule/{uprobe-internal.h => userspace-probe-internal.h} (65%) rename include/lttng/event-rule/{uprobe.h => userspace-probe.h} (74%) create mode 100644 include/lttng/kernel-function-internal.h create mode 100644 include/lttng/kernel-function.h create mode 100644 include/lttng/log-level-rule-internal.h create mode 100644 include/lttng/log-level-rule.h create mode 100644 include/lttng/map-key-internal.h create mode 100644 include/lttng/map-key.h create mode 100644 include/lttng/map/map-internal.h create mode 100644 include/lttng/map/map-query-internal.h create mode 100644 include/lttng/map/map-query.h create mode 100644 include/lttng/map/map.h create mode 100644 src/bin/lttng-sessiond/event-notifier-error-accounting.c create mode 100644 src/bin/lttng-sessiond/event-notifier-error-accounting.h create mode 100644 src/bin/lttng-sessiond/map.c create mode 100644 src/bin/lttng-sessiond/map.h create mode 100644 src/bin/lttng/commands/add_map.c create mode 100644 src/bin/lttng/commands/disable_map.c create mode 100644 src/bin/lttng/commands/enable_map.c create mode 100644 src/bin/lttng/commands/view_map.c create mode 100644 src/common/actions/incr-value.c rename src/common/conditions/{event-rule.c => on-event.c} (54%) create mode 100644 src/common/event-rule/kernel-function.c rename src/common/event-rule/{kprobe.c => kernel-probe.c} (66%) rename src/common/event-rule/{uprobe.c => userspace-probe.c} (63%) create mode 100644 src/common/index-allocator.c create mode 100644 src/common/index-allocator.h create mode 100644 src/common/kernel-function.c create mode 100644 src/common/log-level-rule.c create mode 100644 src/common/map-key/map-key.c create mode 100644 src/common/map-query.c create mode 100644 src/common/map.c rename src/{bin/lttng-sessiond => common}/shm.c (86%) rename src/{bin/lttng-sessiond => common}/shm.h (85%) create mode 100755 tests/regression/kernel/test_kernel_function create mode 100644 tests/regression/tools/map/Makefile.am create mode 100644 tests/regression/tools/map/map_base_test.sh create mode 100755 tests/regression/tools/map/test_map_kernel create mode 100644 tests/regression/tools/map/test_map_query.c create mode 100755 tests/regression/tools/map/test_map_ust create mode 100644 tests/regression/tools/notification/sessiond_testpoints.c create mode 100755 tests/regression/tools/notification/test_notification_kernel_capture create mode 100755 tests/regression/tools/notification/test_notification_notifier_discarded_count create mode 100755 tests/regression/tools/notification/test_notification_ust_capture create mode 100644 tests/regression/tools/save-load/load-42-maps.lttng create mode 100644 tests/unit/test_action.c create mode 100644 tests/unit/test_log_level_rule.c create mode 100644 tests/unit/test_map.c create mode 100644 tests/unit/test_map_key.c create mode 100644 tests/unit/test_map_query.c diff --git a/.gitignore b/.gitignore index 3c76b61f2..5e2b0a189 100644 --- a/.gitignore +++ b/.gitignore @@ -148,6 +148,7 @@ health_check /tests/unit/test_buffer_view /tests/unit/test_kernel_probe /tests/unit/test_event_expr_to_bytecode +/tests/unit/test_log_level_rule /tests/utils/testapp/gen-ust-nevents-str/gen-ust-nevents-str /tests/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary /tests/utils/testapp/userspace-probe-elf-cxx-binary/userspace-probe-elf-cxx-binary diff --git a/DO_NO_MERGE.txt b/DO_NO_MERGE.txt new file mode 100644 index 000000000..4b2b5fa0e --- /dev/null +++ b/DO_NO_MERGE.txt @@ -0,0 +1,3 @@ +captures +trigger error counter +map+thc diff --git a/configure.ac b/configure.ac index 9a40705af..1edbfadd0 100644 --- a/configure.ac +++ b/configure.ac @@ -247,6 +247,7 @@ LTTNG_PTHREAD_GETNAME_NP # Check if clock_gettime, timer_create, timer_settime, and timer_delete are available in lib rt, and if so, # add -lrt to LIBS AC_CHECK_LIB([rt], [clock_gettime, timer_create, timer_settime, timer_delete]) +AC_CHECK_LIB([m], [floor, log10]) # Checks for dl. AC_CHECK_LIB([dl], [dlopen], [ @@ -425,6 +426,7 @@ _AC_DEFINE_AND_SUBST([DEFAULT_ROTATE_PENDING_TIMER], [500000]) # Command short descriptions _AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_ADD_CONTEXT], [Add context fields to a channel]) +_AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_ADD_MAP], [Add map]) _AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_CREATE], [Create a tracing session]) _AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_CLEAR], [Clear a tracing session]) _AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_DESTROY], [Tear down tracing sessions]) @@ -1079,6 +1081,9 @@ AC_SUBST(lttngactionincludedir) lttngconditionincludedir="${includedir}/lttng/condition" AC_SUBST(lttngconditionincludedir) +lttngmapincludedir="${includedir}/lttng/map" +AC_SUBST(lttngmapincludedir) + lttngnotificationincludedir="${includedir}/lttng/notification" AC_SUBST(lttngnotificationincludedir) @@ -1154,6 +1159,7 @@ AC_CONFIG_FILES([ tests/regression/tools/notification/Makefile tests/regression/tools/rotation/Makefile tests/regression/tools/base-path/Makefile + tests/regression/tools/map/Makefile tests/regression/tools/metadata/Makefile tests/regression/tools/tracker/Makefile tests/regression/tools/working-directory/Makefile diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 5ae6ffbe3..5d5599c40 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -39,7 +39,11 @@ MAN1_NAMES = \ lttng-clear \ lttng-add-trigger \ lttng-remove-trigger \ - lttng-list-triggers + lttng-list-triggers \ + lttng-add-map \ + lttng-enable-map \ + lttng-disable-map \ + lttng-view-map MAN3_NAMES = MAN8_NAMES = lttng-sessiond lttng-relayd diff --git a/doc/man/lttng-add-map.1.txt b/doc/man/lttng-add-map.1.txt new file mode 100644 index 000000000..e2661d3b1 --- /dev/null +++ b/doc/man/lttng-add-map.1.txt @@ -0,0 +1,81 @@ +lttng-add-map(1) +================ +:revdate: 30 Mars 2021 + + +NAME +---- +lttng-add-map - Create LTTng map + + +SYNOPSIS +-------- + +Create a Linux kernel map: +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *add-map* option:--kernel + [option:--session='SESSION'] [--bitness='BITNESS'] + [option:--coalesce-hits] + [option:--may-key-count='MAX_KEY_COUNT'] + 'MAP' + +Create a user space map: +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *add-map* option:--userspace + [option:--session='SESSION'] [--bitness='BITNESS'] + [(option:--per-pid | option:--per-uid)] + [option:--coalesce-hits] + [option:--may-key-count='MAX_KEY_COUNT'] + 'MAP' + + +DESCRIPTION +----------- + +The `lttng add-map` command is used to create maps. + +A map is key-value data structure used to count events occurences. When +creating a map, many parameters related to this data structure can be +fine-tuned. + +When 'MAP' does not name an existing map, a map named +'MAP' is created. + +OPTIONS +------- + +option:-k, option:--kernel:: + Create map in the Linux kernel domain. + +option:-u, option:--userspace:: + Create map in the user space domain. + +option:-s 'SESSION', option:--session='SESSION':: + Create map in the tracing session named 'SESSION' + instead of the current tracing session. + +option:--bitness='BITNESS':: + Set the counter bucket size to BITNESS bits. + +option:--coalesce-hits:: + Coalesces hits from identical event rules. + +option:--per-pid:: + Keep one event counter per process. + +option:--per-uid:: + Keep one event counter for all processes of a single user. + +include::common-cmd-help-options.txt[] + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-enable-map(1), +man:lttng-disable-map(1), +man:lttng-view-map(1), +man:lttng-list(1), +man:lttng-add-trigger(1), +man:lttng(1) diff --git a/doc/man/lttng-add-trigger.1.txt b/doc/man/lttng-add-trigger.1.txt index 393e8c920..b06e865bf 100644 --- a/doc/man/lttng-add-trigger.1.txt +++ b/doc/man/lttng-add-trigger.1.txt @@ -1,6 +1,6 @@ lttng-add-trigger(1) ===================== -:revdate: 17 January 2020 +:revdate: 27 May 2020 NAME @@ -56,6 +56,11 @@ Event rule: `on-event [event rule arguments]`:: - It is not possible to use filter operands that use values from the context. ++ +Fields to capture can be specified with the option:--capture option, followed by +a capture expression. Zero or more captures can be configured. See the +<> section below for more information. + [[actions]] Actions ~~~~~~~ @@ -73,6 +78,11 @@ Notify: *notify*:: Some details about the condition evaluation are sent along with the notification. +Increment value: *incr-value* -s sess-name -m map-name --key KEY_FORMAT:: + This action causes the tracer to increment the value of a counter + in a map. If the map is absent or disabled when the condition is met, + nothing is done. + Start session: *start-session* session-name:: This action causes the LTTng session daemon to start tracing for the session with the given name. If no session with the given name exist @@ -96,6 +106,74 @@ Snapshot session: *snapshot-session* session-name:: the given name exist at the time the condition is met, nothing is done. + +[[capture-expr]] +Capture expression +~~~~~~~~~~~~~~~~~~ + +A capture expression can be specified with the option:--capture option when +creating a new on-event condition. If the capture expression corresponds with an +event's field when tracing, the runtime dynamic value corresponding to the +capture expression is captured. + +NOTE: Make sure to **single-quote** the capture expression when running +the command from a shell, as capture expressions typically include +characters having a special meaning for most shells. + +* Supported field types: + - integer, + - unsigned integer, + - floating point value, + - fixed-size array of integers, + - variable-size array of integers (sequence), + - enumeration, + - text string, + - element of any allowing previous type. + +* The dynamic value of an event field is captured by using its name as a C + identifier. ++ +The square bracket notation is available, like in the C +language, to access array/sequence field. +Only a constant, positive integer number can be used within square +brackets. If the index is out of bounds, the capture expression +evaluates to `unavailable`. ++ +An enumeration field's value is an integer. ++ +When the capture's field does not exist, the capture expression +evaluates to `unavailable`. ++ +Examples: `my_field`, `target_cpu`, `seq[7]` + +* The dynamic value of a statically-known context field is captured by + prefixing its name with `$ctx.`. See man:lttng-add-context(1) to get a list of + available contexts. ++ +When the expression's statically-known context field does not exist, +the capture expression evaluates to `unavailable`. ++ +Examples: `$ctx.prio`, `$ctx.preemptible`, +`$ctx.perf:cpu:stalled-cycles-frontend`. ++ +NOTE: The statically-known context field does NOT need to be added using the +man:lttng-add-context(1) command. The statically-known context fields are +always available in the context of triggers. + +* The dynamic value of an application-specific context field is captured by + prefixing its name with `$app.` (follows the format used to add such a context + field with the man:lttng-add-context(1) command). ++ +When the expression's application-specific context field does not exist, +the capture expression evaluates to `unavailable`. ++ +Example: `$app.server:cur_user`. ++ +NOTE: The application-specific context field does NOT need to be added using the +man:lttng-add-context(1) command. The application-specific context fields fields +are always available in the context of triggers. + + OPTIONS ------- diff --git a/doc/man/lttng-disable-map.1.txt b/doc/man/lttng-disable-map.1.txt new file mode 100644 index 000000000..a46a2c4a2 --- /dev/null +++ b/doc/man/lttng-disable-map.1.txt @@ -0,0 +1,51 @@ +lttng-disable-map(1) +==================== +:revdate: 30 Mars 2021 + + +NAME +---- +lttng-disable-map - Disable LTTng map + + +SYNOPSIS +-------- + +Disable an existing map: + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *disable-map* (option:--userspace | option:--kernel) + [option:--session='SESSION'] 'MAP' + + +DESCRIPTION +----------- + +The `lttng disable-map` command is used to disable existing maps. Disabling a +map prevents any further modification of the content of the map by the tracers +until it's enabled again using `lttng-enable-map(1)`. Even when disabled, the +content of the map is available via the `lttng-view-map(1)` command. + +OPTIONS +------- +option:-k, option:--kernel:: + Disable map in the Linux kernel domain. + +option:-u, option:--userspace:: + Disable map in the user space domain. + +option:-s 'SESSION', option:--session='SESSION':: + Disable map in the tracing session named 'SESSION' + instead of the current tracing session. + +include::common-cmd-help-options.txt[] + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-add-map(1), +man:lttng-enable-map(1), +man:lttng-view-map(1), +man:lttng(1) diff --git a/doc/man/lttng-enable-map.1.txt b/doc/man/lttng-enable-map.1.txt new file mode 100644 index 000000000..7b56911b8 --- /dev/null +++ b/doc/man/lttng-enable-map.1.txt @@ -0,0 +1,48 @@ +lttng-enable-map(1) +=================== +:revdate: 30 Mars 2021 + + +NAME +---- +lttng-enable-map - Enable LTTng map + + +SYNOPSIS +-------- + +Enable an existing map: + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *enable-map* (option:--userspace | option:--kernel) + [option:--session='SESSION'] 'MAP' + + +DESCRIPTION +----------- + +The `lttng enable-map` command is used to enable existing maps. + +OPTIONS +------- +option:-k, option:--kernel:: + Enable map in the Linux kernel domain. + +option:-u, option:--userspace:: + Enable map in the user space domain. + +option:-s 'SESSION', option:--session='SESSION':: + Enable map in the tracing session named 'SESSION' + instead of the current tracing session. + +include::common-cmd-help-options.txt[] + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-add-map(1), +man:lttng-disable-map(1), +man:lttng-view-map(1), +man:lttng(1) diff --git a/doc/man/lttng-list.1.txt b/doc/man/lttng-list.1.txt index 0cb19fe48..cbb25137f 100644 --- a/doc/man/lttng-list.1.txt +++ b/doc/man/lttng-list.1.txt @@ -5,7 +5,7 @@ lttng-list(1) NAME ---- -lttng-list - List LTTng tracing sessions, domains, channels, and events +lttng-list - List LTTng tracing sessions, domains, channels, maps, and events SYNOPSIS @@ -26,10 +26,10 @@ List tracing session's domains: [verse] *lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *list* option:--domain 'SESSION' -List tracing session's channels and event rules: +List tracing session's channels, maps, and event rules: [verse] -*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *list* [option:--channel='CHANNEL'] 'SESSION' +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *list* [option:--channel='CHANNEL'] [option:--map='MAP'] 'SESSION' DESCRIPTION @@ -52,7 +52,7 @@ listed event sources. Providing a tracing session name 'SESSION' targets a specific tracing session. If the option:--domain option is used, domains containing at least one channel in the selected tracing session are listed. Otherwise, -all the domains, channels, and event rules of the selected tracing +all the domains, channels, maps, and event rules of the selected tracing session are listed along with its details (trace path, for example), except when the option:--channel option is used to isolate a specific channel by name. @@ -84,6 +84,9 @@ Target option:-c 'CHANNEL', option:--channel='CHANNEL':: Only list the details of the channel named 'CHANNEL'. +option:-m 'MAP', option:--map='MAP':: + Only list the details of the map named 'MAP'. + Listing ~~~~~~~ diff --git a/doc/man/lttng-view-map.1.txt b/doc/man/lttng-view-map.1.txt new file mode 100644 index 000000000..de7e40ee0 --- /dev/null +++ b/doc/man/lttng-view-map.1.txt @@ -0,0 +1,58 @@ +lttng-view-map(1) +================= +:revdate: 30 Mars 2021 + + +NAME +---- +lttng-view-map - View LTTng map + + +SYNOPSIS +-------- + +View the content of a map: + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *view-map* [(option:--userspace | option:--kernel)] + [option:--session='SESSION'] [option:--key='KEY'] 'MAP' + + +DESCRIPTION +----------- + +The `lttng view-map` command is used to view the content of maps. +This command prints all the key value pairs contained in this map. + +On LTTng-UST maps created with the `--per-pid` option, the content of the map +of every running applications and the aggregated per-pid map. The aggregated +per-pid map contains the per-key-value pair sum of the maps of applications +that exited since the start of the session. + + +OPTIONS +------- +option:-k, option:--kernel:: + View map in the Linux kernel domain. + +option:-u, option:--userspace:: + View map in the user space domain. + +option:-s 'SESSION', option:--session='SESSION':: + View map in the tracing session named 'SESSION' + instead of the current tracing session. + +option:--key='KEY':: + Only show entries for KEY. + +include::common-cmd-help-options.txt[] + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-add-map(1), +man:lttng-enable-map(1), +man:lttng-disable-map(1), +man:lttng(1) diff --git a/include/Makefile.am b/include/Makefile.am index b75602b27..9405907c2 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -97,34 +97,40 @@ CLEANFILES = version.i.tmp DISTCLEANFILES = version.i lttnginclude_HEADERS = \ - lttng/health.h \ - lttng/lttng.h \ - lttng/constant.h \ lttng/channel.h \ + lttng/clear-handle.h \ + lttng/clear.h \ + lttng/constant.h \ + lttng/destruction-handle.h \ lttng/domain.h \ - lttng/event.h \ + lttng/endpoint.h \ lttng/event-expr.h \ lttng/event-field-value.h \ + lttng/event.h \ lttng/handle.h \ - lttng/session.h \ - lttng/lttng-error.h \ - lttng/snapshot.h \ - lttng/save.h \ + lttng/health.h \ + lttng/kernel-function.h \ + lttng/kernel-probe.h \ lttng/load.h \ - lttng/endpoint.h \ - lttng/rotation.h \ lttng/location.h \ - lttng/userspace-probe.h \ + lttng/log-level-rule.h \ + lttng/map/map.h \ + lttng/map/map-query.h \ + lttng/map-key.h \ + lttng/lttng-error.h \ + lttng/lttng.h \ + lttng/rotation.h \ + lttng/save.h \ lttng/session-descriptor.h \ - lttng/destruction-handle.h \ - lttng/clear.h \ - lttng/clear-handle.h \ + lttng/session.h \ + lttng/snapshot.h \ lttng/tracker.h \ - lttng/kernel-probe.h + lttng/userspace-probe.h lttngactioninclude_HEADERS= \ lttng/action/action.h \ lttng/action/group.h \ + lttng/action/incr-value.h \ lttng/action/notify.h \ lttng/action/rotate-session.h \ lttng/action/snapshot-session.h \ @@ -134,7 +140,7 @@ lttngactioninclude_HEADERS= \ lttngconditioninclude_HEADERS= \ lttng/condition/condition.h \ lttng/condition/buffer-usage.h \ - lttng/condition/event-rule.h \ + lttng/condition/on-event.h \ lttng/condition/session-consumed-size.h \ lttng/condition/session-rotation.h \ lttng/condition/evaluation.h @@ -143,54 +149,66 @@ lttngnotificationinclude_HEADERS= \ lttng/notification/channel.h \ lttng/notification/notification.h +lttngmapinclude_HEADERS= \ + lttng/map/map.h \ + lttng/map/map-query.h + lttngtriggerinclude_HEADERS= \ lttng/trigger/trigger.h lttngeventruleinclude_HEADERS= \ lttng/event-rule/event-rule.h \ - lttng/event-rule/kprobe.h \ + lttng/event-rule/kernel-function.h \ + lttng/event-rule/kernel-probe.h \ lttng/event-rule/syscall.h \ - lttng/event-rule/uprobe.h \ + lttng/event-rule/userspace-probe.h \ lttng/event-rule/tracepoint.h noinst_HEADERS = \ - lttng/snapshot-internal.h \ - lttng/health-internal.h \ - lttng/save-internal.h \ - lttng/load-internal.h \ lttng/action/action-internal.h \ lttng/action/group-internal.h \ + lttng/action/incr-value-internal.h \ lttng/action/notify-internal.h \ lttng/action/rotate-session-internal.h \ lttng/action/snapshot-session-internal.h \ lttng/action/start-session-internal.h \ lttng/action/stop-session-internal.h \ - lttng/condition/condition-internal.h \ + lttng/channel-internal.h \ lttng/condition/buffer-usage-internal.h \ - lttng/condition/event-rule-internal.h \ - lttng/condition/session-consumed-size-internal.h \ + lttng/condition/condition-internal.h \ lttng/condition/evaluation-internal.h \ + lttng/condition/on-event-internal.h \ + lttng/condition/session-consumed-size-internal.h \ lttng/condition/session-rotation-internal.h \ - lttng/notification/notification-internal.h \ - lttng/trigger/trigger-internal.h \ - lttng/endpoint-internal.h \ - lttng/notification/channel-internal.h \ - lttng/channel-internal.h \ lttng/domain-internal.h \ - lttng/event-internal.h \ + lttng/endpoint-internal.h \ lttng/event-expr-internal.h \ lttng/event-field-value-internal.h \ - lttng/rotate-internal.h \ - lttng/ref-internal.h \ - lttng/location-internal.h \ - lttng/userspace-probe-internal.h \ - lttng/session-internal.h \ - lttng/session-descriptor-internal.h \ - lttng/kernel-probe-internal.h \ + lttng/event-internal.h \ lttng/event-rule/event-rule-internal.h \ - lttng/event-rule/kprobe-internal.h \ + lttng/event-rule/kernel-function-internal.h \ + lttng/event-rule/kernel-probe-internal.h \ lttng/event-rule/syscall-internal.h \ - lttng/event-rule/uprobe-internal.h \ lttng/event-rule/tracepoint-internal.h \ + lttng/event-rule/userspace-probe-internal.h \ + lttng/health-internal.h \ + lttng/kernel-function-internal.h \ + lttng/kernel-probe-internal.h \ + lttng/load-internal.h \ + lttng/location-internal.h \ + lttng/log-level-rule-internal.h \ + lttng/map/map-internal.h \ + lttng/map/map-query-internal.h \ + lttng/map-key-internal.h \ + lttng/notification/channel-internal.h \ + lttng/notification/notification-internal.h \ + lttng/ref-internal.h \ + lttng/rotate-internal.h \ + lttng/save-internal.h \ + lttng/session-descriptor-internal.h \ + lttng/session-internal.h \ + lttng/snapshot-internal.h \ + lttng/trigger/trigger-internal.h \ + lttng/userspace-probe-internal.h \ version.h \ version.i diff --git a/include/lttng/action/action.h b/include/lttng/action/action.h index be7e397d0..ad451f219 100644 --- a/include/lttng/action/action.h +++ b/include/lttng/action/action.h @@ -22,6 +22,7 @@ enum lttng_action_type { LTTNG_ACTION_TYPE_ROTATE_SESSION = 3, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION = 4, LTTNG_ACTION_TYPE_GROUP = 5, + LTTNG_ACTION_TYPE_INCREMENT_VALUE = 6, }; enum lttng_action_status { diff --git a/include/lttng/action/group-internal.h b/include/lttng/action/group-internal.h index cddee55ed..d587ef8c9 100644 --- a/include/lttng/action/group-internal.h +++ b/include/lttng/action/group-internal.h @@ -26,4 +26,9 @@ extern ssize_t lttng_action_group_create_from_payload( struct lttng_payload_view *view, struct lttng_action **group); +LTTNG_HIDDEN +extern struct lttng_action *lttng_action_group_get_mutable_at_index( + struct lttng_action *group, + unsigned int index); + #endif /* LTTNG_ACTION_GROUP_INTERNAL_H */ diff --git a/include/lttng/action/incr-value-internal.h b/include/lttng/action/incr-value-internal.h new file mode 100644 index 000000000..c98105eb0 --- /dev/null +++ b/include/lttng/action/incr-value-internal.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_ACTION_INCR_VALUE_INTERNAL_H +#define LTTNG_ACTION_INCR_VALUE_INTERNAL_H + +#include + +#include + +struct lttng_action; +struct lttng_payload_view; +struct lttng_map_key; + +/* + * Create a "incr-value" action from a payload view. + * + * On success, return the number of bytes consumed from `view`, and the created + * action in `*action`. On failure, return -1. + */ +LTTNG_HIDDEN +extern ssize_t lttng_action_incr_value_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **action); + +LTTNG_HIDDEN +enum lttng_action_status lttng_action_incr_value_borrow_key_mutable( + const struct lttng_action *action, struct lttng_map_key **key); + +LTTNG_HIDDEN +enum lttng_action_status lttng_action_incr_value_set_tracer_token( + struct lttng_action *action, uint64_t token); + +LTTNG_HIDDEN +uint64_t lttng_action_incr_value_get_tracer_token( + const struct lttng_action *action); + +#endif /* LTTNG_ACTION_INCR_VALUE_INTERNAL_H */ diff --git a/include/lttng/action/incr-value.h b/include/lttng/action/incr-value.h new file mode 100644 index 000000000..40f1e719b --- /dev/null +++ b/include/lttng/action/incr-value.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_ACTION_INCR_VALUE_H +#define LTTNG_ACTION_INCR_VALUE_H + +#include +#include + +struct lttng_action; +struct lttng_map_key; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Create a newly allocated incr-value action object. + * + * Returns a new action on success, NULL on failure. This action must be + * destroyed using lttng_action_destroy(). + */ +extern struct lttng_action *lttng_action_incr_value_create(void); + +/* + * Set the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + */ +extern enum lttng_action_status lttng_action_incr_value_set_session_name( + struct lttng_action *action, const char *session_name); + +/* + * Get the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + */ +extern enum lttng_action_status lttng_action_incr_value_get_session_name( + const struct lttng_action *action, const char **session_name); + +/* + * Set the map name of an lttng_action object of type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + */ +extern enum lttng_action_status lttng_action_incr_value_set_map_name( + struct lttng_action *action, const char *map_name); + +/* + * Get the map name of an lttng_action object of type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + */ +extern enum lttng_action_status lttng_action_incr_value_get_map_name( + const struct lttng_action *action, const char **map_name); + +/* + * Set key allocation policy for an lttng_action object type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + * + * The caller retains ownership of the passed lttng_map_key. + */ +extern enum lttng_action_status +lttng_action_incr_value_set_key(struct lttng_action *action, + struct lttng_map_key *key); + +/* + * Get key allocation policy for an lttng_action object type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + */ +extern enum lttng_action_status +lttng_action_incr_value_get_key(const struct lttng_action *action, + const struct lttng_map_key **key); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_INCR_VALUE_H */ diff --git a/include/lttng/condition/condition.h b/include/lttng/condition/condition.h index 78a206df3..97be5923d 100644 --- a/include/lttng/condition/condition.h +++ b/include/lttng/condition/condition.h @@ -21,7 +21,7 @@ enum lttng_condition_type { LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW = 102, LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING = 103, LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED = 104, - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT = 105, + LTTNG_CONDITION_TYPE_ON_EVENT = 105, }; enum lttng_condition_status { @@ -30,6 +30,7 @@ enum lttng_condition_status { LTTNG_CONDITION_STATUS_UNKNOWN = -2, LTTNG_CONDITION_STATUS_INVALID = -3, LTTNG_CONDITION_STATUS_UNSET = -4, + LTTNG_CONDITION_STATUS_UNSUPPORTED = -4, }; /* diff --git a/include/lttng/condition/evaluation-internal.h b/include/lttng/condition/evaluation-internal.h index 15ae4af4d..eaef721fb 100644 --- a/include/lttng/condition/evaluation-internal.h +++ b/include/lttng/condition/evaluation-internal.h @@ -9,6 +9,7 @@ #define LTTNG_EVALUATION_INTERNAL_H #include +#include #include #include #include @@ -38,7 +39,9 @@ void lttng_evaluation_init(struct lttng_evaluation *evaluation, enum lttng_condition_type type); LTTNG_HIDDEN -ssize_t lttng_evaluation_create_from_payload(struct lttng_payload_view *view, +ssize_t lttng_evaluation_create_from_payload( + const struct lttng_condition *condition, + struct lttng_payload_view *view, struct lttng_evaluation **evaluation); LTTNG_HIDDEN diff --git a/include/lttng/condition/event-rule-internal.h b/include/lttng/condition/event-rule-internal.h deleted file mode 100644 index 0f2f3fbf6..000000000 --- a/include/lttng/condition/event-rule-internal.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#ifndef LTTNG_CONDITION_EVENT_RULE_INTERNAL_H -#define LTTNG_CONDITION_EVENT_RULE_INTERNAL_H - -#include -#include -#include -#include -#include - -struct lttng_capture_descriptor { - struct lttng_event_expr *event_expression; - struct lttng_bytecode *bytecode; -}; - -struct lttng_condition_event_rule { - struct lttng_condition parent; - struct lttng_event_rule *rule; - - /* Array of `struct lttng_capture_descriptor *`. */ - struct lttng_dynamic_pointer_array capture_descriptors; -}; - -struct lttng_evaluation_event_rule { - struct lttng_evaluation parent; - char *name; -}; - -struct lttng_evaluation_event_rule_comm { - /* Includes the null terminator. */ - uint32_t trigger_name_length; - /* Trigger name. */ - char payload[]; -} LTTNG_PACKED; - -LTTNG_HIDDEN -ssize_t lttng_condition_event_rule_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **condition); - -LTTNG_HIDDEN -enum lttng_condition_status -lttng_condition_event_rule_borrow_rule_mutable( - const struct lttng_condition *condition, - struct lttng_event_rule **rule); - -LTTNG_HIDDEN -struct lttng_evaluation *lttng_evaluation_event_rule_create( - const char* trigger_name); - -LTTNG_HIDDEN -ssize_t lttng_evaluation_event_rule_create_from_payload( - struct lttng_payload_view *view, - struct lttng_evaluation **_evaluation); - -LTTNG_HIDDEN -enum lttng_error_code -lttng_condition_event_rule_generate_capture_descriptor_bytecode( - struct lttng_condition *condition); - -LTTNG_HIDDEN -const struct lttng_bytecode * -lttng_condition_event_rule_get_capture_bytecode_at_index( - const struct lttng_condition *condition, unsigned int index); - -#endif /* LTTNG_CONDITION_EVENT_RULE_INTERNAL_H */ diff --git a/include/lttng/condition/on-event-internal.h b/include/lttng/condition/on-event-internal.h new file mode 100644 index 000000000..e19b50276 --- /dev/null +++ b/include/lttng/condition/on-event-internal.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_CONDITION_ON_EVENT_INTERNAL_H +#define LTTNG_CONDITION_ON_EVENT_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include + +struct lttng_capture_descriptor { + struct lttng_event_expr *event_expression; + struct lttng_bytecode *bytecode; +}; + +struct lttng_condition_on_event { + struct lttng_condition parent; + struct lttng_event_rule *rule; + + LTTNG_OPTIONAL(uint64_t) error_count; + /* + * Internal use only. + * Error accounting counter index. + */ + LTTNG_OPTIONAL(uint64_t) error_counter_index; + + /* Array of `struct lttng_capture_descriptor *`. */ + struct lttng_dynamic_pointer_array capture_descriptors; +}; + +struct lttng_evaluation_event_rule { + struct lttng_evaluation parent; + char *name; + + /* MessagePack-encoded captured event field values. */ + struct lttng_dynamic_buffer capture_payload; + + /* + * The content of this array event field value is the decoded + * version of `capture_payload` above. + * + * This is a cache: it's not serialized/deserialized in + * communications from/to the library and the session daemon. + */ + struct lttng_event_field_value *captured_values; +}; + +struct lttng_evaluation_event_rule_comm { + /* Includes the null terminator. */ + uint32_t trigger_name_length; + /* Trigger name. */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_condition_on_event_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **condition); + +LTTNG_HIDDEN +enum lttng_condition_status +lttng_condition_on_event_borrow_rule_mutable( + const struct lttng_condition *condition, + struct lttng_event_rule **rule); + +LTTNG_HIDDEN +void lttng_condition_on_event_set_error_counter_index( + struct lttng_condition *condition, uint64_t error_counter_index); + +LTTNG_HIDDEN +uint64_t lttng_condition_on_event_get_error_counter_index( + const struct lttng_condition *condition); + +LTTNG_HIDDEN +uint64_t lttng_condition_on_event_get_error_count( + const struct lttng_condition *condition); + +LTTNG_HIDDEN +void lttng_condition_on_event_set_error_count(struct lttng_condition *condition, + uint64_t error_count); + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_event_rule_create( + const struct lttng_condition_on_event *condition, + const char* trigger_name, + const char *capture_payload, size_t capture_payload_size, + bool decode_capture_payload); + +LTTNG_HIDDEN +ssize_t lttng_evaluation_event_rule_create_from_payload( + const struct lttng_condition_on_event *condition, + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation); + +LTTNG_HIDDEN +enum lttng_error_code +lttng_condition_on_event_generate_capture_descriptor_bytecode( + struct lttng_condition *condition); + +LTTNG_HIDDEN +const struct lttng_bytecode * +lttng_condition_on_event_get_capture_bytecode_at_index( + const struct lttng_condition *condition, unsigned int index); + +#endif /* LTTNG_CONDITION_ON_EVENT_INTERNAL_H */ diff --git a/include/lttng/condition/event-rule.h b/include/lttng/condition/on-event.h similarity index 71% rename from include/lttng/condition/event-rule.h rename to include/lttng/condition/on-event.h index 91fce32d6..ca10781ed 100644 --- a/include/lttng/condition/event-rule.h +++ b/include/lttng/condition/on-event.h @@ -5,8 +5,8 @@ * */ -#ifndef LTTNG_CONDITION_EVENT_RULE_H -#define LTTNG_CONDITION_EVENT_RULE_H +#ifndef LTTNG_CONDITION_ON_EVENT_H +#define LTTNG_CONDITION_ON_EVENT_H #include #include @@ -17,6 +17,7 @@ extern "C" { #endif struct lttng_event_expr; +struct lttng_event_field_value; /** * Event rule conditions allows an action to be taken whenever an event matching @@ -35,7 +36,7 @@ struct lttng_event_expr; * Returns a new condition on success, NULL on failure. This condition must be * destroyed using lttng_condition_destroy(). */ -extern struct lttng_condition *lttng_condition_event_rule_create( +extern struct lttng_condition *lttng_condition_on_event_create( struct lttng_event_rule *rule); /* @@ -48,8 +49,8 @@ extern struct lttng_condition *lttng_condition_event_rule_create( * Returns LTTNG_CONDITION_STATUS_OK and a pointer to the condition's rule * on success, LTTNG_CONDITION_STATUS_INVALID if an invalid * parameter is passed. */ -extern enum lttng_condition_status lttng_condition_event_rule_get_rule( - const struct lttng_condition *condition, +extern enum lttng_condition_status lttng_condition_on_event_get_rule( + const struct lttng_condition *condition, const struct lttng_event_rule **rule); /** @@ -74,6 +75,30 @@ lttng_evaluation_event_rule_get_trigger_name( const struct lttng_evaluation *evaluation, const char **name); +/* + * Sets `*field_val` to the array event field value of the event rule + * condition evaluation `evaluation` which contains its captured values. + * + * Returns: + * + * `LTTNG_EVALUATION_STATUS_OK`: + * Success. + * + * `*field_val` is an array event field value with a length of at + * least one. + * + * `LTTNG_EVALUATION_STATUS_INVALID`: + * * `evaluation` is `NULL`. + * * The type of the condition of `evaluation` is not + * `LTTNG_CONDITION_TYPE_ON_EVENT`. + * * The condition of `evaluation` has no capture descriptors. + * * `field_val` is `NULL`. + */ +extern enum lttng_evaluation_status +lttng_evaluation_get_captured_values( + const struct lttng_evaluation *evaluation, + const struct lttng_event_field_value **field_val); + /* * Appends (transfering the ownership) the capture descriptor `expr` to * the event rule condition `condition`. @@ -89,7 +114,7 @@ lttng_evaluation_event_rule_get_trigger_name( * `LTTNG_CONDITION_STATUS_INVALID`: * * `condition` is `NULL`. * * The type of `condition` is not - * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * `LTTNG_CONDITION_TYPE_ON_EVENT`. * * `expr` is `NULL`. * * `expr` is not a locator expression, that is, its type is not * one of: @@ -98,9 +123,11 @@ lttng_evaluation_event_rule_get_trigger_name( * * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD` * * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD` * * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT` + * `LTTNG_CONDITION_STATUS_UNSUPPORTED`: + * * The associated event-rule does not support runtime capture. */ extern enum lttng_condition_status -lttng_condition_event_rule_append_capture_descriptor( +lttng_condition_on_event_append_capture_descriptor( struct lttng_condition *condition, struct lttng_event_expr *expr); @@ -116,11 +143,11 @@ lttng_condition_event_rule_append_capture_descriptor( * `LTTNG_CONDITION_STATUS_INVALID`: * * `condition` is `NULL`. * * The type of `condition` is not - * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * `LTTNG_CONDITION_TYPE_ON_EVENT`. * * `count` is `NULL`. */ extern enum lttng_condition_status -lttng_condition_event_rule_get_capture_descriptor_count( +lttng_condition_on_event_get_capture_descriptor_count( const struct lttng_condition *condition, unsigned int *count); /* @@ -129,17 +156,17 @@ lttng_condition_event_rule_get_capture_descriptor_count( * * * `condition` is `NULL`. * * The type of `condition` is not - * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * `LTTNG_CONDITION_TYPE_ON_EVENT`. * * `index` is greater than or equal to the number of capture * descriptors in `condition` (as returned by - * lttng_condition_event_rule_get_capture_descriptor_count()). + * lttng_condition_on_event_get_capture_descriptor_count()). */ extern const struct lttng_event_expr * -lttng_condition_event_rule_get_capture_descriptor_at_index( +lttng_condition_on_event_get_capture_descriptor_at_index( const struct lttng_condition *condition, unsigned int index); #ifdef __cplusplus } #endif -#endif /* LTTNG_CONDITION_EVENT_RULE_H */ +#endif /* LTTNG_CONDITION_ON_EVENT_H */ diff --git a/include/lttng/event-rule/event-rule.h b/include/lttng/event-rule/event-rule.h index e097dd488..eec673e00 100644 --- a/include/lttng/event-rule/event-rule.h +++ b/include/lttng/event-rule/event-rule.h @@ -18,9 +18,9 @@ enum lttng_event_rule_type { LTTNG_EVENT_RULE_TYPE_UNKNOWN = -1, LTTNG_EVENT_RULE_TYPE_TRACEPOINT = 0, LTTNG_EVENT_RULE_TYPE_SYSCALL = 1, - LTTNG_EVENT_RULE_TYPE_KPROBE = 2, - LTTNG_EVENT_RULE_TYPE_KRETPROBE = 3, - LTTNG_EVENT_RULE_TYPE_UPROBE = 4, + LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE = 2, + LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION = 3, + LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE = 4, }; enum lttng_event_rule_status { diff --git a/include/lttng/event-rule/kernel-function-internal.h b/include/lttng/event-rule/kernel-function-internal.h new file mode 100644 index 000000000..e7a3c380e --- /dev/null +++ b/include/lttng/event-rule/kernel-function-internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_KERNEL_FUNCTION_INTERNAL_H +#define LTTNG_EVENT_RULE_KERNEL_FUNCTION_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_rule_kernel_function { + struct lttng_event_rule parent; + char *name; + struct lttng_kernel_function_location *location; +}; + +struct lttng_event_rule_kernel_function_comm { + /* Includes terminator `\0`. */ + uint32_t name_len; + uint32_t location_len; + /* + * Payload is composed of, in that order: + * - name (null terminated), + * - kernel function location object. + */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kernel_function_create_from_payload( + struct lttng_payload_view *payload, + struct lttng_event_rule **rule); + +#endif /* LTTNG_EVENT_RULE_KERNEL_FUNCTION_INTERNAL_H */ diff --git a/include/lttng/event-rule/kernel-function.h b/include/lttng/event-rule/kernel-function.h new file mode 100644 index 000000000..aaa3f755d --- /dev/null +++ b/include/lttng/event-rule/kernel-function.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_KERNEL_FUNCTION_H +#define LTTNG_EVENT_RULE_KERNEL_FUNCTION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_kernel_function_location; + +/* + * Create a newly allocated kernel function event rule. + * + * The location is copied internally. + * + * Returns a new event rule on success, NULL on failure. The returned event rule + * must be destroyed using lttng_event_rule_destroy(). + */ +extern struct lttng_event_rule *lttng_event_rule_kernel_function_create( + const struct lttng_kernel_function_location *location); + +/* + * Get the kernel function location of a kernel function event rule. + * + * The caller does not assume the ownership of the returned location. + * The location shall only be used for the duration of the event + * rule's lifetime, or before a different location is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's location + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is + * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a location was not set prior to + * this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_kernel_function_get_location( + const struct lttng_event_rule *rule, + const struct lttng_kernel_function_location **location); + +/* + * Set the name of a kernel function event rule. + * + * The name is copied internally. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_kernel_function_set_event_name( + struct lttng_event_rule *rule, const char *name); + +/* + * Get the name of a kernel function event rule. + * + * The caller does not assume the ownership of the returned name. + * The name shall only only be used for the duration of the event + * rule's lifetime, or before a different name is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on + * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed, + * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_kernel_function_get_event_name( + const struct lttng_event_rule *rule, const char **name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_KERNEL_FUNCTION_H */ diff --git a/include/lttng/event-rule/kprobe-internal.h b/include/lttng/event-rule/kernel-probe-internal.h similarity index 66% rename from include/lttng/event-rule/kprobe-internal.h rename to include/lttng/event-rule/kernel-probe-internal.h index 7d2ecc8cc..50c02d851 100644 --- a/include/lttng/event-rule/kprobe-internal.h +++ b/include/lttng/event-rule/kernel-probe-internal.h @@ -5,21 +5,21 @@ * */ -#ifndef LTTNG_EVENT_RULE_KPROBE_INTERNAL_H -#define LTTNG_EVENT_RULE_KPROBE_INTERNAL_H +#ifndef LTTNG_EVENT_RULE_KERNEL_PROBE_INTERNAL_H +#define LTTNG_EVENT_RULE_KERNEL_PROBE_INTERNAL_H #include #include #include -#include +#include -struct lttng_event_rule_kprobe { +struct lttng_event_rule_kernel_probe { struct lttng_event_rule parent; char *name; struct lttng_kernel_probe_location *location; }; -struct lttng_event_rule_kprobe_comm { +struct lttng_event_rule_kernel_probe_comm { /* Includes terminator `\0`. */ uint32_t name_len; uint32_t location_len; @@ -32,8 +32,8 @@ struct lttng_event_rule_kprobe_comm { } LTTNG_PACKED; LTTNG_HIDDEN -ssize_t lttng_event_rule_kprobe_create_from_payload( +ssize_t lttng_event_rule_kernel_probe_create_from_payload( struct lttng_payload_view *payload, struct lttng_event_rule **rule); -#endif /* LTTNG_EVENT_RULE_KPROBE_INTERNAL_H */ +#endif /* LTTNG_EVENT_RULE_KERNEL_PROBE_INTERNAL_H */ diff --git a/include/lttng/event-rule/kprobe.h b/include/lttng/event-rule/kernel-probe.h similarity index 74% rename from include/lttng/event-rule/kprobe.h rename to include/lttng/event-rule/kernel-probe.h index e3d7563a4..4efd4d394 100644 --- a/include/lttng/event-rule/kprobe.h +++ b/include/lttng/event-rule/kernel-probe.h @@ -5,8 +5,8 @@ * */ -#ifndef LTTNG_EVENT_RULE_KPROBE_H -#define LTTNG_EVENT_RULE_KPROBE_H +#ifndef LTTNG_EVENT_RULE_KERNEL_PROBE_H +#define LTTNG_EVENT_RULE_KERNEL_PROBE_H #include @@ -19,21 +19,12 @@ struct lttng_kernel_probe_location; /* * Create a newly allocated kprobe event rule. * - * Returns a new event rule on success, NULL on failure. The returned event rule - * must be destroyed using lttng_event_rule_destroy(). - */ -extern struct lttng_event_rule *lttng_event_rule_kprobe_create(void); - -/* - * Set the kernel probe location of a kprobe event rule. - * * The location is copied internally. * - * Returns LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID - * if invalid parameters are passed. + * Returns a new event rule on success, NULL on failure. The returned event rule + * must be destroyed using lttng_event_rule_destroy(). */ -extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_location( - struct lttng_event_rule *rule, +extern struct lttng_event_rule *lttng_event_rule_kernel_probe_create( const struct lttng_kernel_probe_location *location); /* @@ -48,7 +39,7 @@ extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_location( * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a location was not set prior to * this call. */ -extern enum lttng_event_rule_status lttng_event_rule_kprobe_get_location( +extern enum lttng_event_rule_status lttng_event_rule_kernel_probe_get_location( const struct lttng_event_rule *rule, const struct lttng_kernel_probe_location **location); @@ -60,7 +51,7 @@ extern enum lttng_event_rule_status lttng_event_rule_kprobe_get_location( * Returns LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID * if invalid parameters are passed. */ -extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( +extern enum lttng_event_rule_status lttng_event_rule_kernel_probe_set_event_name( struct lttng_event_rule *rule, const char *name); /* @@ -74,11 +65,11 @@ extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed, * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call. */ -extern enum lttng_event_rule_status lttng_event_rule_kprobe_get_name( +extern enum lttng_event_rule_status lttng_event_rule_kernel_probe_get_event_name( const struct lttng_event_rule *rule, const char **name); #ifdef __cplusplus } #endif -#endif /* LTTNG_EVENT_RULE_KPROBE_H */ +#endif /* LTTNG_EVENT_RULE_KERNEL_PROBE_H */ diff --git a/include/lttng/event-rule/syscall.h b/include/lttng/event-rule/syscall.h index baf8432c1..c581b809c 100644 --- a/include/lttng/event-rule/syscall.h +++ b/include/lttng/event-rule/syscall.h @@ -17,6 +17,8 @@ extern "C" { /* * Create a newly allocated syscall event rule. * + * The default pattern is '*'. + * * Returns a new event rule on success, NULL on failure. This event rule must be * destroyed using lttng_event_rule_destroy(). */ diff --git a/include/lttng/event-rule/tracepoint-internal.h b/include/lttng/event-rule/tracepoint-internal.h index 227fe6de9..6c4d438d5 100644 --- a/include/lttng/event-rule/tracepoint-internal.h +++ b/include/lttng/event-rule/tracepoint-internal.h @@ -10,10 +10,12 @@ #include #include +#include #include #include #include #include +#include struct lttng_event_rule_tracepoint { struct lttng_event_rule parent; @@ -27,11 +29,8 @@ struct lttng_event_rule_tracepoint { /* Filter. */ char *filter_expression; - /* Loglevel. */ - struct { - enum lttng_loglevel_type type; - int value; - } loglevel; + /* Log level. */ + struct lttng_log_level_rule *log_level_rule; /* Exclusions. */ struct lttng_dynamic_pointer_array exclusions; @@ -46,19 +45,19 @@ struct lttng_event_rule_tracepoint { struct lttng_event_rule_tracepoint_comm { /* enum lttng_domain_type. */ int8_t domain_type; - /* enum lttng_event_logleven_type. */ - int8_t loglevel_type; - int32_t loglevel_value; /* Includes terminator `\0`. */ uint32_t pattern_len; /* Includes terminator `\0`. */ uint32_t filter_expression_len; + /* enum lttng_log_level_rule_comm + payload if any */ + uint32_t log_level_rule_len; uint32_t exclusions_count; uint32_t exclusions_len; /* * Payload is composed of, in that order: * - pattern (null terminated), * - filter expression (null terminated), + * - log level rule serialized object, * - exclusions (32 bit length + null terminated string). */ char payload[]; diff --git a/include/lttng/event-rule/tracepoint.h b/include/lttng/event-rule/tracepoint.h index e24b0fbe1..62c1db38d 100644 --- a/include/lttng/event-rule/tracepoint.h +++ b/include/lttng/event-rule/tracepoint.h @@ -10,6 +10,7 @@ #include #include +#include #include #ifdef __cplusplus @@ -19,6 +20,8 @@ extern "C" { /* * Create a newly allocated tracepoint event rule. * + * The default pattern is '*'. + * * Returns a new event rule on success, NULL on failure. This event rule must be * destroyed using lttng_event_rule_destroy(). */ @@ -90,58 +93,31 @@ extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter( const struct lttng_event_rule *rule, const char **expression); /* - * Set the single log level of a tracepoint event rule. - * - * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID - * if invalid parameters are passed. - */ -extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level( - struct lttng_event_rule *rule, int level); - -/* - * Set the log level range lower bound of a tracepoint event rule. + * Set the log level rule of a tracepoint event rule. * * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID * if invalid parameters are passed. */ extern enum lttng_event_rule_status -lttng_event_rule_tracepoint_set_log_level_range_lower_bound( - struct lttng_event_rule *rule, int level); +lttng_event_rule_tracepoint_set_log_level_rule(struct lttng_event_rule *rule, + const struct lttng_log_level_rule *log_level_rule); /* - * Set the log level to all of a tracepoint event rule. + * Get the log level rule of a tracepoint event rule. * - * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID - * if invalid parameters are passed. - */ -extern enum lttng_event_rule_status -lttng_event_rule_tracepoint_set_log_level_all(struct lttng_event_rule *rule); - -/* - * Get the log level type of a tracepoint event rule. + * The caller does not assume the ownership of the returned log level rule. The + * log level rule shall only only be used for the duration of the event rule's + * lifetime, or before a different log level rule is set. * - * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the log level type output + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the log level rule output * parameter on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter - * is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a log level was not set prior + * is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a log level rule was not set prior * to this call. */ extern enum lttng_event_rule_status -lttng_event_rule_tracepoint_get_log_level_type( +lttng_event_rule_tracepoint_get_log_level_rule( const struct lttng_event_rule *rule, - enum lttng_loglevel_type *type); - -/* - * Get the log level of a tracepoint event rule. - * - * For range log level , the lower bound log level is returned. - * - * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the log level output parameter - * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is - * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a log level was not set prior to - * this call. - */ -extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_log_level( - const struct lttng_event_rule *rule, int *level); + const struct lttng_log_level_rule **log_level_rule); /* * Add an exclusion to the set of exclusion of an event rule. diff --git a/include/lttng/event-rule/uprobe-internal.h b/include/lttng/event-rule/userspace-probe-internal.h similarity index 65% rename from include/lttng/event-rule/uprobe-internal.h rename to include/lttng/event-rule/userspace-probe-internal.h index 6d4b10661..8ba5b2bf2 100644 --- a/include/lttng/event-rule/uprobe-internal.h +++ b/include/lttng/event-rule/userspace-probe-internal.h @@ -5,21 +5,21 @@ * */ -#ifndef LTTNG_EVENT_RULE_UPROBE_INTERNAL_H -#define LTTNG_EVENT_RULE_UPROBE_INTERNAL_H +#ifndef LTTNG_EVENT_RULE_USERSPACE_PROBE_INTERNAL_H +#define LTTNG_EVENT_RULE_USERSPACE_PROBE_INTERNAL_H #include #include #include -#include +#include -struct lttng_event_rule_uprobe { +struct lttng_event_rule_userspace_probe { struct lttng_event_rule parent; char *name; struct lttng_userspace_probe_location *location; }; -struct lttng_event_rule_uprobe_comm { +struct lttng_event_rule_userspace_probe_comm { /* Includes terminator `\0`. */ uint32_t name_len; /* Includes terminator `\0`. */ @@ -33,13 +33,13 @@ struct lttng_event_rule_uprobe_comm { } LTTNG_PACKED; LTTNG_HIDDEN -ssize_t lttng_event_rule_uprobe_create_from_payload( +ssize_t lttng_event_rule_userspace_probe_create_from_payload( struct lttng_payload_view *view, struct lttng_event_rule **rule); LTTNG_HIDDEN struct lttng_userspace_probe_location * -lttng_event_rule_uprobe_get_location_mutable( +lttng_event_rule_userspace_probe_get_location_mutable( const struct lttng_event_rule *rule); -#endif /* LTTNG_EVENT_RULE_UPROBE_INTERNAL_H */ +#endif /* LTTNG_EVENT_RULE_USERSPACE_PROBE_INTERNAL_H */ diff --git a/include/lttng/event-rule/uprobe.h b/include/lttng/event-rule/userspace-probe.h similarity index 74% rename from include/lttng/event-rule/uprobe.h rename to include/lttng/event-rule/userspace-probe.h index 12aa01318..88bc2f43a 100644 --- a/include/lttng/event-rule/uprobe.h +++ b/include/lttng/event-rule/userspace-probe.h @@ -5,8 +5,8 @@ * */ -#ifndef LTTNG_EVENT_RULE_UPROBE_H -#define LTTNG_EVENT_RULE_UPROBE_H +#ifndef LTTNG_EVENT_RULE_USERSPACE_PROBE_H +#define LTTNG_EVENT_RULE_USERSPACE_PROBE_H #include #include @@ -18,21 +18,12 @@ extern "C" { /* * Create a newly allocated uprobe event rule. * - * Returns a new event rule on success, NULL on failure. This event rule must be - * destroyed using lttng_event_rule_destroy(). - */ -extern struct lttng_event_rule *lttng_event_rule_uprobe_create(void); - -/* - * Set the location of a uprobe event rule. - * * The location is copied internally. * - * Returns LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID - * if invalid parameters are passed. + * Returns a new event rule on success, NULL on failure. This event rule must be + * destroyed using lttng_event_rule_destroy(). */ -extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_location( - struct lttng_event_rule *rule, +extern struct lttng_event_rule *lttng_event_rule_userspace_probe_create( const struct lttng_userspace_probe_location *location); /* @@ -47,7 +38,7 @@ extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_location( * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a location was not set prior to * this call. */ -extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( +extern enum lttng_event_rule_status lttng_event_rule_userspace_probe_get_location( const struct lttng_event_rule *rule, const struct lttng_userspace_probe_location **location); @@ -59,7 +50,7 @@ extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID * if invalid parameters are passed. */ -extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( +extern enum lttng_event_rule_status lttng_event_rule_userspace_probe_set_event_name( struct lttng_event_rule *rule, const char *name); /* @@ -73,11 +64,11 @@ extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed, * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call. */ -extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_name( +extern enum lttng_event_rule_status lttng_event_rule_userspace_probe_get_event_name( const struct lttng_event_rule *rule, const char **name); #ifdef __cplusplus } #endif -#endif /* LTTNG_EVENT_RULE_UPROBE_H */ +#endif /* LTTNG_EVENT_RULE_USERSPACE_PROBE_H */ diff --git a/include/lttng/kernel-function-internal.h b/include/lttng/kernel-function-internal.h new file mode 100644 index 000000000..f0b412a59 --- /dev/null +++ b/include/lttng/kernel-function-internal.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_KERNEL_FUNCTION_INTERNAL_H +#define LTTNG_KERNEL_FUNCTION_INTERNAL_H + +#include +#include +#include + +#include +#include +#include + +struct lttng_payload; +struct lttng_payload_view; +struct lttng_dynamic_buffer; + +typedef bool (*kernel_function_location_equal_cb)( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); +typedef int (*kernel_function_location_serialize_cb)( + const struct lttng_kernel_function_location *kernel_function_location, + struct lttng_payload *payload); +typedef bool (*kernel_function_location_equal_cb)( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); +typedef ssize_t (*kernel_function_location_create_from_payload_cb)( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **kernel_function_location); +typedef unsigned long (*kernel_function_location_hash_cb)( + const struct lttng_kernel_function_location *location); + +struct lttng_kernel_function_location_comm { + /* enum lttng_kernel_function_location_type */ + int8_t type; + /* + * Payload is composed of, in that order, + * - type-specific payload + */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_kernel_function_location_symbol_comm { + /* Includes the trailing \0. */ + uint32_t symbol_len; + /* The offset from the symbol. */ + uint64_t offset; + /* + * Payload is composed of, in that order, + * - symbol name (with trailing \0). + */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_kernel_function_location_address_comm { + uint64_t address; +} LTTNG_PACKED; + +/* Common ancestor of all kernel function locations. */ +struct lttng_kernel_function_location { + enum lttng_kernel_function_location_type type; + kernel_function_location_equal_cb equal; + kernel_function_location_serialize_cb serialize; + kernel_function_location_hash_cb hash; +}; + +struct lttng_kernel_function_location_symbol { + struct lttng_kernel_function_location parent; + char *symbol_name; + uint64_t offset; +}; + +struct lttng_kernel_function_location_address { + struct lttng_kernel_function_location parent; + uint64_t address; +}; + +LTTNG_HIDDEN +int lttng_kernel_function_location_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload); + +LTTNG_HIDDEN +ssize_t lttng_kernel_function_location_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **function_location); + +LTTNG_HIDDEN +bool lttng_kernel_function_location_is_equal( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); + +LTTNG_HIDDEN +struct lttng_kernel_function_location *lttng_kernel_function_location_copy( + const struct lttng_kernel_function_location *location); + +LTTNG_HIDDEN +unsigned long lttng_kernel_function_location_hash( + const struct lttng_kernel_function_location *location); + +#endif /* LTTNG_KERNEL_FUNCTION_INTERNAL_H */ diff --git a/include/lttng/kernel-function.h b/include/lttng/kernel-function.h new file mode 100644 index 000000000..f05083f0b --- /dev/null +++ b/include/lttng/kernel-function.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_KERNEL_FUNCTION_H +#define LTTNG_KERNEL_FUNCTION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_kernel_function_location; + +enum lttng_kernel_function_location_status { + LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK = 0, + /* Invalid parameters provided. */ + LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_INVALID = -1, +}; + +enum lttng_kernel_function_location_type { + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_UNKNOWN = -1, + /* Location derived from a symbol and an offset. */ + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET = 0, + /* Location derived from an address. */ + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS = 1, +}; + +/* + * Get the type of the kernel function location. + */ +extern enum lttng_kernel_function_location_type +lttng_kernel_function_location_get_type( + const struct lttng_kernel_function_location *location); + +/* + * Destroy the kernel function location. + */ +extern void lttng_kernel_function_location_destroy( + struct lttng_kernel_function_location *location); + +/* + * Create a symbol derived function location. + * On failure, NULL is returned. + */ +extern struct lttng_kernel_function_location * +lttng_kernel_function_location_symbol_create(const char *symbol_name, + uint64_t offset); + +/* + * Get the symbol name of a symbol derived function location. + */ +extern const char *lttng_kernel_function_location_symbol_get_name( + const struct lttng_kernel_function_location *location); + +/* + * Get the offset of a symbol derived location. + */ +extern enum lttng_kernel_function_location_status +lttng_kernel_function_location_symbol_get_offset( + const struct lttng_kernel_function_location *location, + uint64_t *offset); + +/* + * Create an address derived function location. + * On failure, NULL is returned. + */ +extern struct lttng_kernel_function_location * +lttng_kernel_function_location_address_create(uint64_t address); + +/* + * Get the address of an address derived function location. + */ +extern enum lttng_kernel_function_location_status +lttng_kernel_function_location_address_get_address( + const struct lttng_kernel_function_location *location, + uint64_t *offset); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_KERNEL_FUNCTION_H */ diff --git a/include/lttng/log-level-rule-internal.h b/include/lttng/log-level-rule-internal.h new file mode 100644 index 000000000..9c5d1f250 --- /dev/null +++ b/include/lttng/log-level-rule-internal.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_LOG_LEVEL_RULE_INTERNAL_H +#define LTTNG_LOG_LEVEL_RULE_INTERNAL_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * For now only a single backing struct is used for both type of log level + * rule (exactly, as_severe) since both only have a "level" as property. + */ +struct lttng_log_level_rule { + enum lttng_log_level_rule_type type; + + /* Property */ + int level; +}; + +struct lttng_log_level_rule_comm { + /* enum lttng_log_level_rule_type */ + int8_t type; + int32_t level; +}; + +LTTNG_HIDDEN +ssize_t lttng_log_level_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_log_level_rule **rule); + +LTTNG_HIDDEN +int lttng_log_level_rule_serialize(const struct lttng_log_level_rule *rule, + struct lttng_payload *payload); + +LTTNG_HIDDEN +bool lttng_log_level_rule_is_equal(const struct lttng_log_level_rule *a, + const struct lttng_log_level_rule *b); + +LTTNG_HIDDEN +struct lttng_log_level_rule *lttng_log_level_rule_copy( + const struct lttng_log_level_rule *source); + +LTTNG_HIDDEN +void lttng_log_level_rule_to_loglevel( + const struct lttng_log_level_rule *log_level_rule, + enum lttng_loglevel_type *loglevel_type, + int *loglevel_value); +LTTNG_HIDDEN +unsigned long lttng_log_level_rule_hash( + const struct lttng_log_level_rule *log_level_rule); + +#endif /* LTTNG_LOG_LEVEL_RULE_INTERNAL_H */ diff --git a/include/lttng/log-level-rule.h b/include/lttng/log-level-rule.h new file mode 100644 index 000000000..d993163e9 --- /dev/null +++ b/include/lttng/log-level-rule.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_LOG_LEVEL_RULE_H +#define LTTNG_LOG_LEVEL_RULE_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_log_level_rule; + +enum lttng_log_level_rule_type { + LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN = -1, + LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY = 0, + LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS = 1, +}; + +enum lttng_log_level_rule_status { + LTTNG_LOG_LEVEL_RULE_STATUS_OK = 0, + LTTNG_LOG_LEVEL_RULE_STATUS_ERROR = -1, + LTTNG_LOG_LEVEL_RULE_STATUS_INVALID = -3, +}; + +/* + * Get the type of a log level rule. + * + * Returns the type of a log level rule on success, + * LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN on error. + */ +extern enum lttng_log_level_rule_type lttng_log_level_rule_get_type( + const struct lttng_log_level_rule *rule); + +/* + * Create a newly allocated log level rule where a log level must match exactly + * the rule to be considered. + * + * Returns a new log level rule on success, NULL on failure. This log level rule must be + * destroyed using lttng_log_level_rule_destroy(). + */ +extern struct lttng_log_level_rule *lttng_log_level_rule_exactly_create( + int level); + +/* + * Get the level property of a log level exactly rule. + * + * Returns LTTNG_LOG_LEVEL_RULE_STATUS and set the passed level pointer value + * on success, LTTNG_LOG_LEVEL_RULE_STATUS if an invalid + * parameter is passed. + */ +extern enum lttng_log_level_rule_status lttng_log_level_rule_exactly_get_level( + const struct lttng_log_level_rule *rule, int *level); + +/* + * Create a newly allocated log level rule where a log level must be at least as + * severe as the rule to be considered. + * + * Returns a new log level rule on success, NULL on failure. This log level rule + * must be destroyed using lttng_log_level_rule_destroy(). + */ +extern struct lttng_log_level_rule * +lttng_log_level_rule_at_least_as_severe_as_create(int level); + +/* + * Get the level property of a log level at least as severe rule. + * + * Returns LTTNG_LOG_LEVEL_RULE_STATUS and set the passed level pointer value + * on success, LTTNG_LOG_LEVEL_RULE_STATUS if an invalid + * parameter is passed. + */ +extern enum lttng_log_level_rule_status +lttng_log_level_rule_at_least_as_severe_as_get_level( + const struct lttng_log_level_rule *rule, int *level); + +/* + * Destroy (release) a log level rule object. + */ +extern void lttng_log_level_rule_destroy(struct lttng_log_level_rule *log_level_rule); + + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_LOG_LEVEL_RULE_H */ diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index 606d8b0b2..38b03550f 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -176,6 +176,20 @@ enum lttng_error_code { LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY = 163, /* Operation does not apply to the process attribute tracker's tracking policy */ LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD = 164, /* Error initializing event notifier group notification file descriptor */ LTTNG_ERR_INVALID_CAPTURE_EXPRESSION = 165, /* Invalid capture expression. */ + LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION = 166, /* Error registering event notifier to the tracer. */ + LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING = 167, /* Error initializing event notifier error accounting. */ + LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL = 168, /* Error event notifier error accounting full. */ + LTTNG_ERR_INVALID_MAP = 169, /* Invalid map provided. */ + LTTNG_ERR_MAP_NOT_FOUND = 170, /* Map by name not found. */ + LTTNG_ERR_UST_MAP_ENABLE_FAIL = 171, /* UST enable map failed */ + LTTNG_ERR_UST_MAP_DISABLE_FAIL = 172, /* UST disable map failed */ + LTTNG_ERR_UST_MAP_NOT_FOUND = 173, /* UST map not found */ + LTTNG_ERR_UST_MAP_EXIST = 174, /* UST map already exist */ + LTTNG_ERR_KERNEL_MAP_ENABLE_FAIL = 175, /* Kernel enable map failed */ + LTTNG_ERR_KERNEL_MAP_DISABLE_FAIL = 176, /* Kernel disable map failed */ + LTTNG_ERR_KERNEL_MAP_NOT_FOUND = 177, /* Kernel map not found */ + LTTNG_ERR_KERNEL_MAP_EXIST = 178, /* Kernel map already exist */ + LTTNG_ERR_MAP_VALUES_LIST_FAIL = 179, /* Listing map values failed */ /* MUST be last element of the manually-assigned section of the enum */ LTTNG_ERR_NR, diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index c665d3c9b..86db9945a 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -18,6 +18,7 @@ /* Include every LTTng ABI/API available. */ #include #include +#include #include #include #include @@ -29,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -40,16 +41,18 @@ #include #include #include -#include +#include #include #include -#include +#include #include #include #include #include #include +#include #include +#include #include #include #include diff --git a/include/lttng/map-key-internal.h b/include/lttng/map-key-internal.h new file mode 100644 index 000000000..84c25805d --- /dev/null +++ b/include/lttng/map-key-internal.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ +#ifndef LTTNG_MAP_KEY_INTERNAL_H +#define LTTNG_MAP_KEY_INTERNAL_H + +#include +#include +#include +#include + +#include + +struct lttng_payload; +struct lttng_payload_view; +struct lttng_map_key_token; + +typedef bool (*map_key_token_equal_cb)(const struct lttng_map_key_token *a, + const struct lttng_map_key_token *b); + +enum lttng_map_key_token_type { + LTTNG_MAP_KEY_TOKEN_TYPE_STRING, + LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, +}; + +struct lttng_map_key_token { + enum lttng_map_key_token_type type; + map_key_token_equal_cb equal; +}; + +struct lttng_map_key_token_comm { + uint8_t type; +}; + +struct lttng_map_key_token_string { + struct lttng_map_key_token parent; + char *string; +}; + +struct lttng_map_key_token_string_comm { + uint8_t parent_type; + + /* Includes null terminator. */ + uint32_t string_len; + + char payload[]; +}; + +struct lttng_map_key_token_variable { + struct lttng_map_key_token parent; + enum lttng_map_key_token_variable_type type; +}; + +struct lttng_map_key_token_variable_comm { + uint8_t parent_type; + uint8_t var_type; +}; + +struct lttng_map_key { + /* Reference counting is only exposed to internal users*/ + struct urcu_ref ref; + /* Array of `struct lttng_map_key_token` */ + struct lttng_dynamic_pointer_array tokens; +}; + +struct lttng_map_key_comm { + uint32_t token_count; + /* Array of `struct lttng_map_key_token` */ + char payload[]; +}; + +LTTNG_HIDDEN +void lttng_map_key_get(struct lttng_map_key *key); + +LTTNG_HIDDEN +void lttng_map_key_put(struct lttng_map_key *key); + +LTTNG_HIDDEN +ssize_t lttng_map_key_create_from_payload(struct lttng_payload_view *view, + struct lttng_map_key **key); + +LTTNG_HIDDEN +int lttng_map_key_serialize(const struct lttng_map_key *key, + struct lttng_payload *payload); + +LTTNG_HIDDEN +enum lttng_map_key_status lttng_map_key_get_token_count( + const struct lttng_map_key *key, unsigned int *count); + +LTTNG_HIDDEN +const struct lttng_map_key_token *lttng_map_key_get_token_at_index( + const struct lttng_map_key *key, unsigned int index); + +LTTNG_HIDDEN +enum lttng_map_key_token_variable_type lttng_map_key_token_variable_get_type( + const struct lttng_map_key_token_variable *token); + +LTTNG_HIDDEN +const char *lttng_map_key_token_string_get_string( + const struct lttng_map_key_token_string *token); + +LTTNG_HIDDEN +bool lttng_map_key_is_equal( + const struct lttng_map_key *a, const struct lttng_map_key *b); + +LTTNG_HIDDEN +void lttng_map_key_get(struct lttng_map_key *key); + +LTTNG_HIDDEN +void lttng_map_key_put(struct lttng_map_key *key); + +LTTNG_HIDDEN +struct lttng_map_key *lttng_map_key_parse_from_string(const char *key_str); + +#endif /* LTTNG_MAP_KEY_INTERNAL_H */ diff --git a/include/lttng/map-key.h b/include/lttng/map-key.h new file mode 100644 index 000000000..be5694889 --- /dev/null +++ b/include/lttng/map-key.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_KEY_H +#define LTTNG_MAP_KEY_H + +struct lttng_map_key; + +enum lttng_map_key_status { + LTTNG_MAP_KEY_STATUS_ERROR = -2, + LTTNG_MAP_KEY_STATUS_INVALID = -1, + LTTNG_MAP_KEY_STATUS_OK = 0, +}; + +enum lttng_map_key_token_variable_type { + LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, + LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME, +}; + +struct lttng_map_key *lttng_map_key_create(void); + +enum lttng_map_key_status lttng_map_key_append_token_variable( + struct lttng_map_key *key, + enum lttng_map_key_token_variable_type var_type); + +enum lttng_map_key_status lttng_map_key_append_token_string( + struct lttng_map_key *key, const char *string); + +void lttng_map_key_destroy(struct lttng_map_key *key); + +#endif /* LTTNG_MAP_KEY_H */ diff --git a/include/lttng/map/map-internal.h b/include/lttng/map/map-internal.h new file mode 100644 index 000000000..45aae76fa --- /dev/null +++ b/include/lttng/map/map-internal.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_INTERNAL_H +#define LTTNG_MAP_INTERNAL_H + +#include +#include +#include +#include +#include + +#include "map.h" + +struct lttng_payload; +struct lttng_payload_view; + +struct lttng_map { + /* Reference counting is only exposed to internal users. */ + struct urcu_ref ref; + + char *name; + LTTNG_OPTIONAL(bool) is_enabled; + enum lttng_map_bitness bitness; + enum lttng_map_boundary_policy boundary_policy; + enum lttng_domain_type domain; + enum lttng_buffer_type buffer_type; + bool coalesce_hits; + unsigned int dimension_count; + uint64_t *dimension_sizes; +}; + +struct lttng_map_list { + struct lttng_dynamic_pointer_array array; +}; + +struct lttng_map_key_value_pair { + char *key; + int64_t value; + bool has_overflowed; + bool has_underflowed; +}; + +struct lttng_map_key_value_pair_list { + enum lttng_map_key_value_pair_list_type type; + uint64_t id; /* pid_t or uid_t */ + uint64_t cpu; + bool summed_all_cpus; + struct lttng_dynamic_pointer_array array; +}; + +struct lttng_map_content { + enum lttng_buffer_type type; + struct lttng_dynamic_pointer_array array; +}; + +struct lttng_map_comm { + uint32_t name_length /* Includes '\0' */; + uint32_t length; + uint8_t is_enabled; + uint8_t bitness; + uint8_t boundary_policy; + uint8_t domain; + uint8_t buffer_type; + uint8_t coalesce_hits;; + uint64_t dimension_count; + + /* length excludes its own length. */ + /* A name and dimension sizes follow. */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_map_list_comm { + uint32_t count; + /* Count * lttng_map_comm structure */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_map_key_value_pair_comm { + uint32_t key_length /* Includes '\0' */; + int64_t value; + uint8_t has_overflowed; + uint8_t has_underflowed; +} LTTNG_PACKED; + +struct lttng_map_key_value_pair_list_comm { + uint32_t count; + uint8_t type; /* enum lttng_map_key_value_pair_list_type */ + uint64_t id; /* pid_t or uid_t */ + uint64_t cpu; + uint8_t summed_all_cpus; + /* Count * lttng_map_key_value_pair_comm structure */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_map_content_comm { + uint32_t count; + uint8_t type; /* enum lttng_buffer_type */ + /* Count * lttng_map_key_value_pair_list structure */ + char payload[]; +}; + +LTTNG_HIDDEN +ssize_t lttng_map_create_from_payload(struct lttng_payload_view *view, + struct lttng_map **map); + +LTTNG_HIDDEN +int lttng_map_serialize(const struct lttng_map *map, + struct lttng_payload *payload); + +LTTNG_HIDDEN +void lttng_map_get(struct lttng_map *map); + +LTTNG_HIDDEN +void lttng_map_put(struct lttng_map *map); + +LTTNG_HIDDEN +void lttng_map_set_is_enabled(struct lttng_map *map, bool enabled); + +/* + * Allocate a new list of maps. + * The returned object must be freed via lttng_map_list_destroy. + */ +LTTNG_HIDDEN +struct lttng_map_list *lttng_map_list_create(void); + +/* + * Add a map to the maps set. + * + * A reference to the added map is acquired on behalf of the map set + * on success. + */ +LTTNG_HIDDEN +enum lttng_map_status lttng_map_list_add(struct lttng_map_list *map_list, + struct lttng_map *map); + +LTTNG_HIDDEN +ssize_t lttng_map_list_create_from_payload(struct lttng_payload_view *view, + struct lttng_map_list **map_list); + +/* + * Serialize a map list to an lttng_payload object. + * Return LTTNG_OK on success, negative lttng error code on error. + */ +LTTNG_HIDDEN +int lttng_map_list_serialize(const struct lttng_map_list *map_list, + struct lttng_payload *payload); + +LTTNG_HIDDEN +struct lttng_map_key_value_pair *lttng_map_key_value_pair_create( + const char *key, int64_t value); + +LTTNG_HIDDEN +void lttng_map_key_value_pair_set_has_overflowed( + struct lttng_map_key_value_pair *key_value); + +LTTNG_HIDDEN +void lttng_map_key_value_pair_set_has_underflowed( + struct lttng_map_key_value_pair *key_value); + +LTTNG_HIDDEN +ssize_t lttng_map_key_value_pair_create_from_payload( + struct lttng_payload_view *view, + struct lttng_map_key_value_pair **key_value); + +LTTNG_HIDDEN +int lttng_map_key_value_pair_serialize( + const struct lttng_map_key_value_pair *key_value, + struct lttng_payload *payload); + +LTTNG_HIDDEN +void lttng_map_key_value_pair_destroy( + struct lttng_map_key_value_pair *key_value); + +LTTNG_HIDDEN +struct lttng_map_key_value_pair_list *lttng_map_key_value_pair_list_create( + enum lttng_map_key_value_pair_list_type type, + bool summed_all_cpus); + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_set_identifier( + struct lttng_map_key_value_pair_list *kv_pair_list, + uint64_t identifier); + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_set_cpu( + struct lttng_map_key_value_pair_list *kv_pair_list, + uint64_t cpu); + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_append_key_value( + struct lttng_map_key_value_pair_list *key_values, + struct lttng_map_key_value_pair *key_value); + +LTTNG_HIDDEN +ssize_t lttng_map_key_value_pair_list_create_from_payload( + struct lttng_payload_view *view, + struct lttng_map_key_value_pair_list **key_values); + +LTTNG_HIDDEN +int lttng_map_key_value_pair_list_serialize( + const struct lttng_map_key_value_pair_list *key_values, + struct lttng_payload *payload); + +LTTNG_HIDDEN +struct lttng_map_content *lttng_map_content_create( + enum lttng_buffer_type type); + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_content_append_key_value_list( + struct lttng_map_content *map_content, + struct lttng_map_key_value_pair_list *kv_list); + +LTTNG_HIDDEN +ssize_t lttng_map_content_create_from_payload( + struct lttng_payload_view *view, + struct lttng_map_content **map_content); + +LTTNG_HIDDEN +int lttng_map_content_serialize( + const struct lttng_map_content *map_content, + struct lttng_payload *payload); + +#endif /* LTTNG_MAP_INTERNAL_H */ diff --git a/include/lttng/map/map-query-internal.h b/include/lttng/map/map-query-internal.h new file mode 100644 index 000000000..47c7e5ff4 --- /dev/null +++ b/include/lttng/map/map-query-internal.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_QUERY_INTERNAL_H +#define LTTNG_MAP_QUERY_INTERNAL_H + +#include + +#include +#include + +#include +#include + +struct lttng_map_query { + enum lttng_map_query_config_cpu config_cpu; + enum lttng_map_query_config_buffer config_buffer; + enum lttng_map_query_config_app_bitness config_bitness; + + /* + * Aggregate the values of all selected CPUs in a single table. + */ + bool sum_by_cpu; + + /* + * Aggregate the values of all selected bitness in a single table. + */ + bool sum_by_app_bitness; + + /* + * Aggregate the values of all selected uid or pid in a single table. + */ + bool sum_by_uid; + bool sum_by_pid; + + char *key_filter; + struct lttng_dynamic_array cpu_array; + struct lttng_dynamic_array uid_array; + struct lttng_dynamic_array pid_array; +}; + +struct lttng_map_query_comm { + uint32_t key_filter_length; /* Include '\0' */ + + uint8_t config_cpu; + uint8_t config_buffer; + uint8_t config_app_bitness; + + uint8_t sum_by_cpu; + uint8_t sum_by_app_bitness; + uint8_t sum_by_uid; + uint8_t sum_by_pid; + + uint32_t cpu_count; + uint32_t uid_count; + uint32_t pid_count; + /* + * key_filter + + * (cpu_count * int) + (uid_count * uid_t) + (pid_count * pid_t) + */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +enum lttng_map_query_config_cpu lttng_map_query_get_config_cpu( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +enum lttng_map_query_config_buffer lttng_map_query_get_config_buffer( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +enum lttng_map_query_config_app_bitness lttng_map_query_get_config_app_bitness( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +bool lttng_map_query_get_config_sum_by_cpu( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +bool lttng_map_query_get_config_sum_by_pid( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +bool lttng_map_query_get_config_sum_by_uid( + const struct lttng_map_query *query); + +// Not supported yet. +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_set_sum_by_app_bitness( + struct lttng_map_query *query, bool sum_by_app_bitness); + +// Not supported yet. +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_set_sum_by_uid( + struct lttng_map_query *query, bool sum_by_uid); + +LTTNG_HIDDEN +bool lttng_map_query_get_config_sum_by_app_bitness( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_cpu_count( + const struct lttng_map_query *query, unsigned int *count); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_uid_count( + const struct lttng_map_query *query, unsigned int *count); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_pid_count( + const struct lttng_map_query *query, unsigned int *count); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_cpu_at_index( + const struct lttng_map_query *query, unsigned int index, + int *cpu); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_uid_at_index( + const struct lttng_map_query *query, unsigned int index, + uid_t *uid); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_pid_at_index( + const struct lttng_map_query *query, unsigned int index, + pid_t *pid); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_key_filter( + const struct lttng_map_query *query, const char **key_filter); + +LTTNG_HIDDEN +ssize_t lttng_map_query_create_from_payload(struct lttng_payload_view *view, + struct lttng_map_query **query); + +LTTNG_HIDDEN +int lttng_map_query_serialize(const struct lttng_map_query *query, + struct lttng_payload *payload); + +LTTNG_HIDDEN +void lttng_map_query_destroy(struct lttng_map_query *query); + +#endif /* LTTNG_MAP_QUERY_INTERNAL_H */ diff --git a/include/lttng/map/map-query.h b/include/lttng/map/map-query.h new file mode 100644 index 000000000..8f80844d5 --- /dev/null +++ b/include/lttng/map/map-query.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_QUERY_H +#define LTTNG_MAP_QUERY_H + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum lttng_map_query_status { + LTTNG_MAP_QUERY_STATUS_OK = 0, + LTTNG_MAP_QUERY_STATUS_ERROR = -1, + LTTNG_MAP_QUERY_STATUS_INVALID = -2, + LTTNG_MAP_QUERY_STATUS_NONE = -3, +}; + +/* + * Query the values of all CPUs or just some. + */ +enum lttng_map_query_config_cpu { + LTTNG_MAP_QUERY_CONFIG_CPU_ALL, + LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET, +}; + +/* + * Query the values of all uid (or pid) or just some. + */ +enum lttng_map_query_config_buffer { + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL, + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET, + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL, + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET, + LTTNG_MAP_QUERY_CONFIG_BUFFER_KERNEL_GLOBAL, +}; + +/* + * Query the values of all bitness or just some. + */ +enum lttng_map_query_config_app_bitness { + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_32, /*Not supported yet*/ + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_64, /*Not supported yet*/ + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL, + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_KERNEL, +}; + +struct lttng_map_query; + +/* + * + */ +extern struct lttng_map_query *lttng_map_query_create( + enum lttng_map_query_config_cpu cpu, + enum lttng_map_query_config_buffer buffer, + enum lttng_map_query_config_app_bitness bitness); + +extern enum lttng_map_query_status lttng_map_query_set_sum_by_cpu( + struct lttng_map_query *query, bool sum_by_cpu); + +extern enum lttng_map_query_status lttng_map_query_set_sum_by_pid( + struct lttng_map_query *query, bool sum_by_pid); + +extern enum lttng_map_query_status lttng_map_query_add_cpu( + struct lttng_map_query *query, int cpu_id); + +extern enum lttng_map_query_status lttng_map_query_add_uid( + struct lttng_map_query *query, uid_t uid); + +extern enum lttng_map_query_status lttng_map_query_add_pid( + struct lttng_map_query *query, pid_t pid); + +extern enum lttng_map_query_status lttng_map_query_add_key_filter( + struct lttng_map_query *query, const char *key_filter); + + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_MAP_QUERY_H */ diff --git a/include/lttng/map/map.h b/include/lttng/map/map.h new file mode 100644 index 000000000..292e39e62 --- /dev/null +++ b/include/lttng/map/map.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_H +#define LTTNG_MAP_H + +#include +#include + +#include +#include + +#include + +struct lttng_map; +struct lttng_map_list; + +struct lttng_map_key_value_pair; +/* A list of key value pair. */ +struct lttng_map_key_value_pair_list; +/* A list of key value pair list. */ +struct lttng_map_content; + +#ifdef __cplusplus +extern "C" { +#endif + +enum lttng_map_status { + LTTNG_MAP_STATUS_OK = 0, + LTTNG_MAP_STATUS_ERROR = -1, + LTTNG_MAP_STATUS_INVALID = -2, + LTTNG_MAP_STATUS_UNSET = -3, +}; + +enum lttng_map_bitness { + LTTNG_MAP_BITNESS_32BITS = 32, + LTTNG_MAP_BITNESS_64BITS = 64, +}; + +enum lttng_map_boundary_policy { + LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW, +}; + +enum lttng_map_key_value_pair_list_type { + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED, +}; + +/* + * + * Return LTTNG_MAP_STATUS_OK on success, LTTNG_MAP_STATUS_INVALID if invalid + * parameters are passed. + */ +extern enum lttng_map_status lttng_map_create(const char *name, + unsigned int dimension_count, + uint64_t *dimension_sizes, + enum lttng_domain_type domain, + enum lttng_buffer_type buffer_type, + enum lttng_map_bitness bitness, + enum lttng_map_boundary_policy boundary_policy, + bool coalesce_hits, + struct lttng_map **map); + +extern enum lttng_map_status lttng_map_get_name( + const struct lttng_map *map, const char **name); + +extern enum lttng_map_status lttng_map_set_name( + struct lttng_map *map, const char *name); + +/* + * Get the number of dimensions. + * + */ +extern unsigned int lttng_map_get_dimension_count( + const struct lttng_map *map); + +/* + * Get the number of elements for the provided dimension. + * + * Return LTTNG_MAP_STATUS_OK on success, LTTNG_MAP_STATUS_INVALID if invalid + * parameters are passed. + * + */ +extern enum lttng_map_status lttng_map_get_dimension_length( + const struct lttng_map *map, unsigned int dimension, + uint64_t *dimension_length); + +extern int lttng_map_get_is_enabled(const struct lttng_map *map); + +extern enum lttng_map_bitness lttng_map_get_bitness( + const struct lttng_map *map); + +extern enum lttng_domain_type lttng_map_get_domain( + const struct lttng_map *map); + +extern enum lttng_buffer_type lttng_map_get_buffer_type( + const struct lttng_map *map); + +extern enum lttng_map_boundary_policy lttng_map_get_boundary_policy( + const struct lttng_map *map); + +extern bool lttng_map_get_coalesce_hits( + const struct lttng_map *map); + +extern void lttng_map_destroy(struct lttng_map *map); + +extern enum lttng_error_code lttng_add_map(struct lttng_handle *handle, + struct lttng_map *map); + +extern enum lttng_error_code lttng_enable_map(struct lttng_handle *handle, + const char *map_name); + +extern enum lttng_error_code lttng_disable_map(struct lttng_handle *handle, + const char *map_name); + + +/* + * Get a map from the list at a given index. + * + * Note that the map list maintains the ownership of the returned map. + * It must not be destroyed by the user, nor should a reference to it be held + * beyond the lifetime of the map list. + * + * Returns a map, or NULL on error. + */ +extern const struct lttng_map *lttng_map_list_get_at_index( + const struct lttng_map_list *map_list, unsigned int index); + +/* + * Get the number of map in a map list. + */ + +extern enum lttng_map_status lttng_map_list_get_count( + const struct lttng_map_list *map_list, unsigned int *count); + +/* + * Destroy a map list. + */ +extern void lttng_map_list_destroy(struct lttng_map_list *map_list); + +extern enum lttng_error_code lttng_list_maps(struct lttng_handle *handle, + struct lttng_map_list **map_list); + +/* + * FIXME: frdeso proper explanation + * lttng_map_content 1 to N lttng_map_key_value_pair_list + * lttng_map_key_value_pair_list 1 to N lttng_map_key_value_pair + */ + +/* + * Get the key of a key-value. + * + * The caller does not assume the ownership of the returned key. + * The key shall only be used for the duration of the key-value's lifetime. + * + * Returns LTTNG_MAP_STATUS_OK and a pointer to the key-value's key on success, + * LTTNG_MAP_STATUS_INVALID if an invalid parameter is passed, or + */ +extern enum lttng_map_status lttng_map_key_value_pair_get_key( + const struct lttng_map_key_value_pair *kv_pair, const char **key); + +extern bool lttng_map_key_value_pair_get_has_overflowed( + const struct lttng_map_key_value_pair *key_value); + +extern bool lttng_map_key_value_pair_get_has_underflowed( + const struct lttng_map_key_value_pair *key_value); +/* + * Get the value of a key-value. + * + * The caller does not assume the ownership of the returned value. + * The value shall only be used for the duration of the key-value's lifetime. + * + * Returns LTTNG_MAP_STATUS_OK and a pointer to the key-value's value on success, + * LTTNG_MAP_STATUS_INVALID if an invalid parameter is passed. + */ +extern enum lttng_map_status lttng_map_key_value_pair_get_value( + const struct lttng_map_key_value_pair *kv_pair, int64_t *value); + +extern enum lttng_map_status lttng_map_content_get_count( + const struct lttng_map_content *map_content, + unsigned int *count); + +extern const struct lttng_map_key_value_pair_list *lttng_map_content_get_at_index( + const struct lttng_map_content *map_content, + unsigned int index); +/* + * List all key-value pairs for the given session and map. + * + * On success, a newly-allocated key-value list is returned. + * + * The key-value list must be destroyed by the caller (see + * lttng_map_key_value_pair_list_destroy()). + * + * Returns LTTNG_OK on success, else a suitable LTTng error code. + */ +extern enum lttng_error_code lttng_list_map_content( + struct lttng_handle *handle, const struct lttng_map *map, + const struct lttng_map_query *map_query, + struct lttng_map_content **map_content); + +extern enum lttng_buffer_type lttng_map_content_get_buffer_type( + const struct lttng_map_content *map_content); + +extern void lttng_map_content_destroy( + struct lttng_map_content *map_content); +/* + * Get a key-value from the list at a given index. + * + * Note that the key value list maintains the ownership of the returned key + * value. + * It must not be destroyed by the user, nor should a reference to it be held + * beyond the lifetime of the key value list. + * + * Returns a key-value, or NULL on error. + */ +extern const struct lttng_map_key_value_pair *lttng_map_key_value_pair_list_get_at_index( + const struct lttng_map_key_value_pair_list *kv_pair_list, + unsigned int index); + +/* + * Get the number of key value pair in a key-value list. + * + * Return LTTNG_MAP_STATUS_OK on success, + * LTTNG_MAP_STATUS_INVALID when invalid parameters are passed. + */ +extern enum lttng_map_status lttng_map_key_value_pair_list_get_count( + const struct lttng_map_key_value_pair_list *kv_pair_list, + unsigned int *count); + +extern enum lttng_map_key_value_pair_list_type lttng_map_key_value_pair_list_get_type( + const struct lttng_map_key_value_pair_list *kv_pair_list); + +extern uint64_t lttng_map_key_value_pair_list_get_identifer( + const struct lttng_map_key_value_pair_list *kv_pair_list); + +extern uint64_t lttng_map_key_value_pair_list_get_cpu( + const struct lttng_map_key_value_pair_list *kv_pair_list); + +extern bool lttng_map_key_value_pair_list_get_summed_all_cpu( + const struct lttng_map_key_value_pair_list *kv_pair_list); + +/* + * Destroy a map_key_value set. + */ +extern void lttng_map_key_value_pair_list_destroy( + struct lttng_map_key_value_pair_list *kv_pair_list); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_MAP_H */ diff --git a/include/lttng/trigger/trigger-internal.h b/include/lttng/trigger/trigger-internal.h index 21c269bef..59fe771aa 100644 --- a/include/lttng/trigger/trigger-internal.h +++ b/include/lttng/trigger/trigger-internal.h @@ -196,4 +196,13 @@ enum lttng_error_code lttng_trigger_generate_bytecode( LTTNG_HIDDEN struct lttng_trigger *lttng_trigger_copy(const struct lttng_trigger *trigger); +/* + * A given trigger needs a tracer notifier if + * it has an event-rule condition, + * AND + * it has one or more sessiond-execution action. + */ +LTTNG_HIDDEN +bool lttng_trigger_needs_tracer_notifier(const struct lttng_trigger *trigger); + #endif /* LTTNG_TRIGGER_INTERNAL_H */ diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am index 1d3741c61..67b96b1dc 100644 --- a/src/bin/lttng-sessiond/Makefile.am +++ b/src/bin/lttng-sessiond/Makefile.am @@ -20,7 +20,7 @@ lttng_sessiond_SOURCES = utils.c utils.h \ context.c context.h \ channel.c channel.h \ event.c event.h \ - shm.c shm.h \ + map.c map.h \ consumer.c consumer.h \ session.c session.h \ modprobe.c modprobe.h kern-modules.h \ @@ -56,8 +56,11 @@ lttng_sessiond_SOURCES = utils.c utils.h \ manage-consumer.c manage-consumer.h \ clear.c clear.h \ tracker.c tracker.h \ + event-notifier-error-accounting.c event-notifier-error-accounting.h \ action-executor.c action-executor.h +lttng_sessiond_LDFLAGS = -rdynamic + if HAVE_LIBLTTNG_UST_CTL lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \ ust-consumer.c ust-consumer.h notify-apps.c \ diff --git a/src/bin/lttng-sessiond/action-executor.c b/src/bin/lttng-sessiond/action-executor.c index 8f8bae406..4e6de8907 100644 --- a/src/bin/lttng-sessiond/action-executor.c +++ b/src/bin/lttng-sessiond/action-executor.c @@ -16,13 +16,14 @@ #include #include #include +#include #include #include #include #include #include #include -#include +#include #include #include #include @@ -66,6 +67,9 @@ typedef int (*action_executor_handler)(struct action_executor *executor, static int action_executor_notify_handler(struct action_executor *executor, const struct action_work_item *, const struct lttng_action *); +static int action_executor_incr_value_handler(struct action_executor *executor, + const struct action_work_item *, + const struct lttng_action *); static int action_executor_start_session_handler(struct action_executor *executor, const struct action_work_item *, const struct lttng_action *); @@ -87,6 +91,7 @@ static int action_executor_generic_handler(struct action_executor *executor, static const action_executor_handler action_executors[] = { [LTTNG_ACTION_TYPE_NOTIFY] = action_executor_notify_handler, + [LTTNG_ACTION_TYPE_INCREMENT_VALUE] = action_executor_incr_value_handler, [LTTNG_ACTION_TYPE_START_SESSION] = action_executor_start_session_handler, [LTTNG_ACTION_TYPE_STOP_SESSION] = action_executor_stop_session_handler, [LTTNG_ACTION_TYPE_ROTATE_SESSION] = action_executor_rotate_session_handler, @@ -94,14 +99,6 @@ static const action_executor_handler action_executors[] = { [LTTNG_ACTION_TYPE_GROUP] = action_executor_group_handler, }; -static const char *action_type_names[] = { - [LTTNG_ACTION_TYPE_NOTIFY] = "Notify", - [LTTNG_ACTION_TYPE_START_SESSION] = "Start session", - [LTTNG_ACTION_TYPE_STOP_SESSION] = "Stop session", - [LTTNG_ACTION_TYPE_ROTATE_SESSION] = "Rotate session", - [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = "Snapshot session", - [LTTNG_ACTION_TYPE_GROUP] = "Group", -}; static const char *get_action_name(const struct lttng_action *action) { @@ -109,7 +106,7 @@ static const char *get_action_name(const struct lttng_action *action) assert(action_type != LTTNG_ACTION_TYPE_UNKNOWN); - return action_type_names[action_type]; + return lttng_action_type_string(action_type); } /* Check if this trigger allowed to interect with a given session. */ @@ -205,6 +202,14 @@ static int action_executor_notify_handler(struct action_executor *executor, client_handle_transmission_status, executor); } +static int action_executor_incr_value_handler(struct action_executor *executor, + const struct action_work_item *work_item, + const struct lttng_action *action) +{ + /* This action is executed by the tracer. */ + return 0; +} + static int action_executor_start_session_handler(struct action_executor *executor, const struct action_work_item *work_item, const struct lttng_action *action) @@ -237,7 +242,6 @@ static int action_executor_start_session_handler(struct action_executor *executo if (!is_trigger_allowed_for_session(work_item->trigger, session)) { goto error_dispose_session; } - cmd_ret = cmd_start_trace(session); switch (cmd_ret) { case LTTNG_OK: diff --git a/src/bin/lttng-sessiond/agent.c b/src/bin/lttng-sessiond/agent.c index 283d5b035..cbd81c576 100644 --- a/src/bin/lttng-sessiond/agent.c +++ b/src/bin/lttng-sessiond/agent.c @@ -15,8 +15,9 @@ #include #include #include -#include +#include #include +#include #include #include @@ -1243,6 +1244,7 @@ struct agent_event *agent_find_event_by_trigger( const struct lttng_event_rule *rule; const char *name; const char *filter_expression; + const struct lttng_log_level_rule *log_level_rule; /* Unused when loglevel_type is 'ALL'. */ int loglevel_value = 0; enum lttng_loglevel_type loglevel_type; @@ -1253,9 +1255,9 @@ struct agent_event *agent_find_event_by_trigger( condition = lttng_trigger_get_const_condition(trigger); assert(lttng_condition_get_type(condition) == - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + LTTNG_CONDITION_TYPE_ON_EVENT); - c_status = lttng_condition_event_rule_get_rule(condition, &rule); + c_status = lttng_condition_on_event_get_rule(condition, &rule); assert(c_status == LTTNG_CONDITION_STATUS_OK); assert(lttng_event_rule_get_type(rule) == @@ -1272,14 +1274,19 @@ struct agent_event *agent_find_event_by_trigger( /* Get the internal filter expression. */ filter_expression = lttng_event_rule_get_filter(rule); - er_status = lttng_event_rule_tracepoint_get_log_level_type( - rule, &loglevel_type); - assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); - - if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { - er_status = lttng_event_rule_tracepoint_get_log_level( - rule, &loglevel_value); - assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + /* Map log_level_rule to loglevel. + * TODO: There is a possibility of extracting this to the log_level_rule + * internal api since multiple callsite do the same. + */ + er_status = lttng_event_rule_tracepoint_get_log_level_rule( + rule, &log_level_rule); + if (er_status == LTTNG_EVENT_RULE_STATUS_UNSET) { + loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + loglevel_value = 0; + } else if (er_status == LTTNG_EVENT_RULE_STATUS_OK) { + lttng_log_level_rule_to_loglevel(log_level_rule, &loglevel_type, &loglevel_value); + } else { + abort(); } return agent_find_event(name, loglevel_type, loglevel_value, diff --git a/src/bin/lttng-sessiond/buffer-registry.c b/src/bin/lttng-sessiond/buffer-registry.c index 32fcbfb5f..34574e391 100644 --- a/src/bin/lttng-sessiond/buffer-registry.c +++ b/src/bin/lttng-sessiond/buffer-registry.c @@ -136,6 +136,14 @@ int buffer_reg_uid_create(uint64_t session_id, uint32_t bits_per_long, uid_t uid goto error_session; } + reg->registry->maps = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!reg->registry->maps) { + lttng_ht_destroy(reg->registry->channels); + ret = -ENOMEM; + goto error_session; + } + + cds_lfht_node_init(®->node.node); *regp = reg; @@ -262,6 +270,13 @@ int buffer_reg_pid_create(uint64_t session_id, struct buffer_reg_pid **regp, goto error_session; } + reg->registry->maps = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!reg->registry->maps) { + lttng_ht_destroy(reg->registry->channels); + ret = -ENOMEM; + goto error_session; + } + lttng_ht_node_init_u64(®->node, reg->session_id); *regp = reg; @@ -385,6 +400,36 @@ int buffer_reg_channel_create(uint64_t key, struct buffer_reg_channel **regp) return 0; } +/* + * Allocate and initialize a buffer registry map with the given key. Set + * regp with the object pointer. + * + * Return 0 on success or else a negative value keeping regp untouched. + */ +int buffer_reg_map_create(uint64_t key, struct buffer_reg_map **regp) +{ + struct buffer_reg_map *reg; + + assert(regp); + + DBG3("Buffer registry map create with key: %" PRIu64, key); + + reg = zmalloc(sizeof(*reg)); + if (!reg) { + PERROR("zmalloc buffer registry map"); + return -ENOMEM; + } + + reg->key = key; + CDS_INIT_LIST_HEAD(®->counters); + pthread_mutex_init(®->counter_list_lock, NULL); + + lttng_ht_node_init_u64(®->node, key); + *regp = reg; + + return 0; +} + /* * Allocate and initialize a buffer registry stream. Set regp with the object * pointer. @@ -410,6 +455,31 @@ int buffer_reg_stream_create(struct buffer_reg_stream **regp) return 0; } +/* + * Allocate and initialize a buffer registry map_counter. Set regp with the object + * pointer. + * + * Return 0 on success or else a negative value keeping regp untouched. + */ +int buffer_reg_map_counter_create(struct buffer_reg_map_counter **regp) +{ + struct buffer_reg_map_counter *reg; + + assert(regp); + + DBG3("Buffer registry creating map_counter"); + + reg = zmalloc(sizeof(*reg)); + if (!reg) { + PERROR("zmalloc buffer registry map_counter"); + return -ENOMEM; + } + + *regp = reg; + + return 0; +} + /* * Add stream to the list in the channel. */ @@ -425,6 +495,21 @@ void buffer_reg_stream_add(struct buffer_reg_stream *stream, pthread_mutex_unlock(&channel->stream_list_lock); } +/* + * Add map_counter to the list in the map. + */ +void buffer_reg_map_counter_add(struct buffer_reg_map_counter *map_counter, + struct buffer_reg_map *map) +{ + assert(map_counter); + assert(map); + + pthread_mutex_lock(&map->counter_list_lock); + cds_list_add_tail(&map_counter->lnode, &map->counters); + map->counter_count++; + pthread_mutex_unlock(&map->counter_list_lock); +} + /* * Add a buffer registry channel object to the given session. */ @@ -439,6 +524,20 @@ void buffer_reg_channel_add(struct buffer_reg_session *session, rcu_read_unlock(); } +/* + * Add a buffer registry map object to the given session. + */ +void buffer_reg_map_add(struct buffer_reg_session *session, + struct buffer_reg_map *map) +{ + assert(session); + assert(map); + + rcu_read_lock(); + lttng_ht_add_unique_u64(session->maps, &map->node); + rcu_read_unlock(); +} + /* * Find a buffer registry channel object with the given key. RCU read side lock * MUST be acquired and hold on until the object reference is not needed @@ -476,6 +575,43 @@ end: return chan; } +/* + * Find a buffer registry map object with the given key. RCU read side lock + * MUST be acquired and hold on until the object reference is not needed + * anymore. + * + * Return the object pointer or NULL on error. + */ +struct buffer_reg_map *buffer_reg_map_find(uint64_t key, + struct buffer_reg_uid *reg) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + struct buffer_reg_map *map = NULL; + struct lttng_ht *ht; + + assert(reg); + + switch (reg->domain) { + case LTTNG_DOMAIN_UST: + ht = reg->registry->maps; + break; + default: + assert(0); + goto end; + } + + lttng_ht_lookup(ht, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (!node) { + goto end; + } + map = caa_container_of(node, struct buffer_reg_map, node); + +end: + return map; +} + /* * Destroy a buffer registry stream with the given domain. */ @@ -511,6 +647,41 @@ void buffer_reg_stream_destroy(struct buffer_reg_stream *regp, return; } +/* + * Destroy a buffer registry map_counter with the given domain. + */ +void buffer_reg_map_counter_destroy(struct buffer_reg_map_counter *regp, + enum lttng_domain_type domain) +{ + if (!regp) { + return; + } + + DBG3("Buffer registry map counter destroy with handle %d", + regp->obj.ust->handle); + + switch (domain) { + case LTTNG_DOMAIN_UST: + { + int ret; + + ret = ust_app_release_object(NULL, regp->obj.ust); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Buffer reg map counter release obj handle %d failed with ret %d", + regp->obj.ust->handle, ret); + } + free(regp->obj.ust); + lttng_fd_put(LTTNG_FD_APPS, 2); + break; + } + default: + assert(0); + } + + free(regp); + return; +} + /* * Remove buffer registry channel object from the session hash table. RCU read * side lock MUST be acquired before calling this. @@ -529,6 +700,24 @@ void buffer_reg_channel_remove(struct buffer_reg_session *session, assert(!ret); } +/* + * Remove buffer registry map object from the session hash table. RCU read + * side lock MUST be acquired before calling this. + */ +void buffer_reg_map_remove(struct buffer_reg_session *session, + struct buffer_reg_map *regp) +{ + int ret; + struct lttng_ht_iter iter; + + assert(session); + assert(regp); + + iter.iter.node = ®p->node.node; + ret = lttng_ht_del(session->maps, &iter); + assert(!ret); +} + /* * Destroy a buffer registry channel with the given domain. */ @@ -572,6 +761,49 @@ void buffer_reg_channel_destroy(struct buffer_reg_channel *regp, return; } +/* + * Destroy a buffer registry map with the given domain. + */ +void buffer_reg_map_destroy(struct buffer_reg_map *regp, + enum lttng_domain_type domain) +{ + if (!regp) { + return; + } + + DBG3("Buffer registry map destroy with key %" PRIu32, regp->key); + + switch (domain) { + case LTTNG_DOMAIN_UST: + { + int ret; + struct buffer_reg_map_counter *map_counter_reg, *tmp; + /* Wipe counter */ + cds_list_for_each_entry_safe(map_counter_reg, tmp, ®p->counters, lnode) { + cds_list_del(&map_counter_reg->lnode); + regp->counter_count--; + buffer_reg_map_counter_destroy(map_counter_reg, domain); + } + + if (regp->obj.ust) { + ret = ust_app_release_object(NULL, regp->obj.ust); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Buffer reg map release obj handle %d failed with ret %d", + regp->obj.ust->handle, ret); + } + free(regp->obj.ust); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + break; + } + default: + assert(0); + } + + free(regp); + return; +} + /* * Destroy a buffer registry session with the given domain. * @@ -583,6 +815,7 @@ static void buffer_reg_session_destroy(struct buffer_reg_session *regp, int ret; struct lttng_ht_iter iter; struct buffer_reg_channel *reg_chan; + struct buffer_reg_map *reg_map; DBG3("Buffer registry session destroy"); @@ -594,9 +827,16 @@ static void buffer_reg_session_destroy(struct buffer_reg_session *regp, assert(!ret); buffer_reg_channel_destroy(reg_chan, domain); } + cds_lfht_for_each_entry(regp->maps->ht, &iter.iter, reg_map, + node.node) { + ret = lttng_ht_del(regp->maps, &iter); + assert(!ret); + buffer_reg_map_destroy(reg_map, domain); + } rcu_read_unlock(); ht_cleanup_push(regp->channels); + ht_cleanup_push(regp->maps); switch (domain) { case LTTNG_DOMAIN_UST: diff --git a/src/bin/lttng-sessiond/buffer-registry.h b/src/bin/lttng-sessiond/buffer-registry.h index 0812414e5..00be7b65e 100644 --- a/src/bin/lttng-sessiond/buffer-registry.h +++ b/src/bin/lttng-sessiond/buffer-registry.h @@ -26,6 +26,14 @@ struct buffer_reg_stream { } obj; }; +struct buffer_reg_map_counter { + struct cds_list_head lnode; + union { + /* Original object data that MUST be copied over. */ + struct lttng_ust_object_data *ust; + } obj; +}; + struct buffer_reg_channel { /* This key is the same as a tracing channel key. */ uint32_t key; @@ -49,6 +57,25 @@ struct buffer_reg_channel { } obj; }; +struct buffer_reg_map { + /* This key is the same as a tracing map key. */ + uint32_t key; + /* Per cpu counter registry object of this map registry. */ + struct cds_list_head counters; + /* Total number of stream in the list. */ + uint64_t counter_count; + /* Used to ensure mutual exclusion to the counter's list. */ + pthread_mutex_t counter_list_lock; + /* Node for hash table usage. */ + struct lttng_ht_node_u64 node; + union { + /* Original object data that MUST be copied over. */ + struct lttng_ust_object_data *ust; + } obj; + + struct ustctl_daemon_counter *daemon_counter; +}; + struct buffer_reg_session { /* Registry per domain. */ union { @@ -57,6 +84,8 @@ struct buffer_reg_session { /* Contains buffer registry channel indexed by tracing channel key. */ struct lttng_ht *channels; + /* Contains buffer registry map indexed by tracing map key. */ + struct lttng_ht *maps; }; /* @@ -130,6 +159,17 @@ void buffer_reg_channel_remove(struct buffer_reg_session *session, void buffer_reg_channel_destroy(struct buffer_reg_channel *regp, enum lttng_domain_type domain); +/* Map */ +int buffer_reg_map_create(uint64_t key, struct buffer_reg_map **regp); +void buffer_reg_map_add(struct buffer_reg_session *session, + struct buffer_reg_map *map); +struct buffer_reg_map *buffer_reg_map_find(uint64_t key, + struct buffer_reg_uid *reg); +void buffer_reg_map_remove(struct buffer_reg_session *session, + struct buffer_reg_map *regp); +void buffer_reg_map_destroy(struct buffer_reg_map *regp, + enum lttng_domain_type domain); + /* Stream */ int buffer_reg_stream_create(struct buffer_reg_stream **regp); void buffer_reg_stream_add(struct buffer_reg_stream *stream, @@ -137,6 +177,13 @@ void buffer_reg_stream_add(struct buffer_reg_stream *stream, void buffer_reg_stream_destroy(struct buffer_reg_stream *regp, enum lttng_domain_type domain); +/* Map counter */ +int buffer_reg_map_counter_create(struct buffer_reg_map_counter **regp); +void buffer_reg_map_counter_add(struct buffer_reg_map_counter *map_counter, + struct buffer_reg_map *map); +void buffer_reg_map_counter_destroy(struct buffer_reg_map_counter *regp, + enum lttng_domain_type domain); + /* Global registry. */ void buffer_reg_destroy_registries(void); diff --git a/src/bin/lttng-sessiond/clear.c b/src/bin/lttng-sessiond/clear.c index 3ae70ea2f..3a08a119a 100644 --- a/src/bin/lttng-sessiond/clear.c +++ b/src/bin/lttng-sessiond/clear.c @@ -188,7 +188,6 @@ int cmd_clear_session(struct ltt_session *session, int *sock_fd) /* Flag session that trace should start automatically */ if (usess) { int int_ret = ust_app_start_trace_all(usess); - if (int_ret < 0) { ret = LTTNG_ERR_UST_START_FAIL; goto end; diff --git a/src/bin/lttng-sessiond/client.c b/src/bin/lttng-sessiond/client.c index 770a2ee44..ba6d08cbd 100644 --- a/src/bin/lttng-sessiond/client.c +++ b/src/bin/lttng-sessiond/client.c @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -767,6 +770,106 @@ end: return ret_code; } +static enum lttng_error_code receive_lttng_map(int sock, + int *sock_error, + uint32_t map_len, + struct lttng_map **_map) +{ + int ret; + ssize_t sock_recv_len; + enum lttng_error_code ret_code; + struct lttng_payload map_payload; + struct lttng_map *map = NULL; + + lttng_payload_init(&map_payload); + ret = lttng_dynamic_buffer_set_size( + &map_payload.buffer, map_len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + sock_recv_len = lttcomm_recv_unix_sock( + sock, map_payload.buffer.data, map_len); + if (sock_recv_len < 0 || sock_recv_len != map_len) { + ERR("Failed to receive map in command payload"); + *sock_error = 1; + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto end; + } + + /* Deserialize map. */ + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &map_payload, 0, -1); + + if (lttng_map_create_from_payload(&view, &map) != + map_len) { + ERR("Invalid map received as part of command payload"); + ret_code = LTTNG_ERR_INVALID_TRIGGER; + lttng_map_put(map); + goto end; + } + } + + *_map = map; + ret_code = LTTNG_OK; + +end: + return ret_code; +} + +static enum lttng_error_code receive_lttng_map_query(int sock, + int *sock_error, + uint32_t map_query_len, + struct lttng_map_query **_map_query) +{ + int ret; + ssize_t sock_recv_len; + enum lttng_error_code ret_code; + struct lttng_payload map_query_payload; + struct lttng_map_query *map_query = NULL; + + lttng_payload_init(&map_query_payload); + ret = lttng_dynamic_buffer_set_size( + &map_query_payload.buffer, map_query_len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + sock_recv_len = lttcomm_recv_unix_sock( + sock, map_query_payload.buffer.data, map_query_len); + if (sock_recv_len < 0 || sock_recv_len != map_query_len) { + ERR("Failed to receive map query in command payload"); + *sock_error = 1; + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto end; + } + + /* Deserialize map query. */ + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &map_query_payload, 0, -1); + + if (lttng_map_query_create_from_payload(&view, &map_query) != + map_query_len) { + ERR("Invalid map query received as part of command payload"); + ret_code = LTTNG_ERR_INVALID_TRIGGER; + lttng_map_query_destroy(map_query); + goto end; + } + } + + *_map_query = map_query; + ret_code = LTTNG_OK; + +end: + return ret_code; +} + /* * Version of setup_lttng_msg() without command header. */ @@ -876,6 +979,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: case LTTNG_CLEAR_SESSION: case LTTNG_LIST_TRIGGERS: + case LTTNG_LIST_MAP_VALUES: need_domain = false; break; default: @@ -886,6 +990,10 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, switch (cmd_ctx->lsm.cmd_type) { case LTTNG_REGISTER_TRIGGER: case LTTNG_UNREGISTER_TRIGGER: + case LTTNG_ADD_MAP: + case LTTNG_ENABLE_MAP: + case LTTNG_DISABLE_MAP: + case LTTNG_LIST_MAP_VALUES: need_consumerd = false; break; default: @@ -986,6 +1094,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, switch (cmd_ctx->lsm.cmd_type) { case LTTNG_DISABLE_CHANNEL: case LTTNG_DISABLE_EVENT: + case LTTNG_DISABLE_MAP: switch (cmd_ctx->lsm.domain.type) { case LTTNG_DOMAIN_KERNEL: if (!cmd_ctx->session->kernel_session) { @@ -1354,6 +1463,23 @@ error_add_context: kernel_poll_pipe[1]); break; } + case LTTNG_ADD_MAP: + { + ret = cmd_add_map(cmd_ctx, *sock); + + break; + } + case LTTNG_ENABLE_MAP: + ret = cmd_enable_map(cmd_ctx->session, cmd_ctx->lsm.domain.type, + cmd_ctx->lsm.u.enable_map.map_name); + break; + case LTTNG_DISABLE_MAP: + { + ret = cmd_disable_map(cmd_ctx->session, cmd_ctx->lsm.domain.type, + cmd_ctx->lsm.u.disable_map.map_name); + + break; + } case LTTNG_PROCESS_ATTR_TRACKER_ADD_INCLUDE_VALUE: case LTTNG_PROCESS_ATTR_TRACKER_REMOVE_INCLUDE_VALUE: { @@ -1892,6 +2018,45 @@ error_add_context: ret = LTTNG_OK; break; } + case LTTNG_LIST_MAPS: + { + struct lttng_map_list *return_map_list = NULL; + size_t original_payload_size; + size_t payload_size; + + ret = setup_empty_lttng_msg(cmd_ctx); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto setup_error; + } + + original_payload_size = cmd_ctx->reply_payload.buffer.size; + + ret = cmd_list_maps(cmd_ctx->lsm.domain.type, cmd_ctx->session, + &return_map_list); + if (ret != LTTNG_OK) { + goto error; + } + + assert(return_map_list); + ret = lttng_map_list_serialize(return_map_list, + &cmd_ctx->reply_payload); + lttng_map_list_destroy(return_map_list); + if (ret) { + ERR("Failed to serialize map_list in reply to `%s` command", + lttcomm_sessiond_command_str(cmd_ctx->lsm.cmd_type)); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + payload_size = cmd_ctx->reply_payload.buffer.size - + original_payload_size; + + update_lttng_msg(cmd_ctx, 0, payload_size); + + ret = LTTNG_OK; + break; + } case LTTNG_LIST_EVENTS: { ssize_t list_ret; @@ -2343,6 +2508,60 @@ error_add_context: ret = LTTNG_OK; break; } + case LTTNG_LIST_MAP_VALUES: + { + struct lttng_map_content *return_map_content = NULL; + struct lttng_map *payload_map = NULL; + struct lttng_map_query *payload_map_query = NULL; + size_t original_reply_payload_size; + size_t reply_payload_size; + + ret = setup_empty_lttng_msg(cmd_ctx); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto setup_error; + } + + original_reply_payload_size = cmd_ctx->reply_payload.buffer.size; + + ret = receive_lttng_map(*sock, sock_error, + cmd_ctx->lsm.u.list_map_values.map_length, + &payload_map); + if (ret != LTTNG_OK) { + goto error; + } + + ret = receive_lttng_map_query(*sock, sock_error, + cmd_ctx->lsm.u.list_map_values.query_length, + &payload_map_query); + if (ret != LTTNG_OK) { + goto error; + } + + ret = cmd_list_map_values(cmd_ctx->lsm.session.name, + payload_map, payload_map_query, + &return_map_content); + if (ret != LTTNG_OK) { + goto error; + } + assert(return_map_content); + ret = lttng_map_content_serialize(return_map_content, + &cmd_ctx->reply_payload); + lttng_map_content_destroy(return_map_content); + if (ret) { + ERR("Failed to serialize key-value pair list in reply to `list map values` command"); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + reply_payload_size = cmd_ctx->reply_payload.buffer.size - + original_reply_payload_size; + + update_lttng_msg(cmd_ctx, 0, reply_payload_size); + + ret = LTTNG_OK; + break; + } default: ret = LTTNG_ERR_UND; break; diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index a2af8882f..67ac9fd7b 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -30,13 +31,19 @@ #include #include #include -#include -#include +#include +#include #include #include #include +#include +#include +#include #include #include +#include "lttng/domain.h" +#include "lttng/map/map.h" +#include #include #include #include @@ -57,12 +64,14 @@ #include "lttng-syscall.h" #include "agent.h" #include "buffer-registry.h" +#include "map.h" #include "notification-thread.h" #include "notification-thread-commands.h" #include "rotate.h" #include "rotation-thread.h" #include "timer.h" #include "agent-thread.h" +#include "session.h" #include "tracker.h" #include "cmd.h" @@ -288,6 +297,340 @@ end: return ret; } +enum tracer_executed_action_state { + TRACER_EXECUTED_ACTION_STATE_REGISTER, + TRACER_EXECUTED_ACTION_STATE_UNREGISTER +}; + +static +enum lttng_error_code sync_incr_value_action_ust( + struct ltt_session *session, + const struct lttng_condition *condition, + const char *map_name, + uint64_t tracer_token, + struct lttng_map_key *key, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret_code; + const struct lttng_event_rule *event_rule; + enum lttng_condition_status cond_status; + enum lttng_event_rule_status er_status; + struct ltt_ust_session *usess = session->ust_session; + const char *pattern; + struct ltt_ust_map *map; + char *filter_expression; + struct lttng_bytecode *filter; + struct lttng_event_exclusion *exclusion; + enum lttng_event_rule_generate_exclusions_status + generate_exclusion_status; + + DBG("Syncing UST incr-value action for session '%s', map '%s'", + session->name, map_name); + if (!usess) { + DBG("No UST session"); + ret_code = LTTNG_OK; + goto end; + } + + assert(usess->domain_global.maps); + + map = trace_ust_find_map_by_name(usess->domain_global.maps, map_name); + if (!map) { + DBG("UST map \"%s\" not found", map_name); + ret_code = LTTNG_OK; + goto end; + } + + cond_status = lttng_condition_on_event_get_rule(condition, &event_rule); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Error getting on-event condition event-rule"); + ret_code = LTTNG_ERR_INVALID_MAP; + goto end; + } + + er_status = lttng_event_rule_tracepoint_get_pattern(event_rule, &pattern); + if (er_status != LTTNG_EVENT_RULE_STATUS_OK) { + /* At this point, this is a fatal error. */ + abort(); + } + + /* + * FIXME: frdeso, reuse the event notifier functions and approach to + * create the event. + */ + filter_expression = (char *) lttng_event_rule_get_filter(event_rule); + filter = (struct lttng_bytecode *)lttng_event_rule_get_filter_bytecode( + event_rule); + generate_exclusion_status = lttng_event_rule_generate_exclusions( + event_rule, &exclusion); + if (generate_exclusion_status == LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_ERROR) { + ERR("Error generating the exclusion"); + ret_code = LTTNG_ERR_EXCLUSION_INVAL; + goto end; + } + + switch (state) { + case TRACER_EXECUTED_ACTION_STATE_REGISTER: + ret_code = map_event_ust_enable_tracepoint(usess, + map, tracer_token, (char *) pattern, key, + LTTNG_EVENT_TRACEPOINT, LTTNG_EVENT_LOGLEVEL_ALL, + 0, filter_expression, filter, exclusion, false); + if (ret_code == LTTNG_ERR_UST_EVENT_ENABLED) { + ret_code = LTTNG_OK; + } else if (ret_code != LTTNG_OK) { + ERR("Enabling UST map event"); + goto end; + } + break; + case TRACER_EXECUTED_ACTION_STATE_UNREGISTER: + ret_code = map_event_ust_disable_tracepoint(usess, + map, tracer_token, (char *) pattern, key, + LTTNG_EVENT_TRACEPOINT, LTTNG_EVENT_LOGLEVEL_ALL, + 0, filter_expression, filter, exclusion, false); + if (ret_code == LTTNG_ERR_UST_EVENT_ENABLED) { + ret_code = LTTNG_OK; + } else if (ret_code != LTTNG_OK) { + ERR("Enabling UST map event"); + goto end; + } + break; + default: + abort(); + } + + ret_code = LTTNG_OK; + +end: + return ret_code; +} + +static +enum lttng_error_code sync_incr_value_action_kernel( + struct ltt_session *session, + const struct lttng_credentials *creds, + const struct lttng_condition *condition, + const char *map_name, + uint64_t tracer_token, + struct lttng_map_key *key, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret_code; + const struct lttng_event_rule *event_rule; + enum lttng_condition_status cond_status; + struct ltt_kernel_map *kmap; + struct ltt_kernel_session *ksess = session->kernel_session; + + DBG("Syncing kernel incr-value action for session '%s', map '%s'", + session->name, map_name); + + if (!ksess) { + DBG("No kernel session"); + ret_code = LTTNG_OK; + goto end; + } + + kmap = trace_kernel_get_map_by_name(map_name, ksess); + if (!kmap) { + DBG("Kernel map \"%s\" not found", map_name); + ret_code = LTTNG_OK; + goto end; + } + + cond_status = lttng_condition_on_event_get_rule(condition, &event_rule); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + ret_code = LTTNG_ERR_INVALID_MAP; + ERR("Error getting on-event condition event-rule"); + goto end; + } + + switch (state) { + case TRACER_EXECUTED_ACTION_STATE_REGISTER: + ret_code = map_event_kernel_enable_event(kmap, creds, tracer_token, + event_rule, key); + if(ret_code != LTTNG_OK) { + ERR("Error enabling event counter to the kernel tracer"); + goto end; + } + break; + case TRACER_EXECUTED_ACTION_STATE_UNREGISTER: + ret_code = map_event_kernel_disable_event(kmap, tracer_token); + if(ret_code != LTTNG_OK) { + ERR("Error disabling event counter to the kernel tracer"); + goto end; + } + break; + } + +end: + return ret_code; +} + +static +enum lttng_error_code sync_incr_value_action( + const struct lttng_credentials *creds, + const struct lttng_condition *condition, + const struct lttng_action *action, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret; + const char *session_name, *map_name; + enum lttng_action_status action_status; + enum lttng_condition_status cond_status; + const struct lttng_event_rule *event_rule; + struct ltt_session *session = NULL; + struct lttng_map_key *key; + uint64_t action_tracer_token; + + action_status = lttng_action_incr_value_get_map_name(action, + &map_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Map name not set for incr-value action"); + ret = LTTNG_ERR_INVALID_MAP; + goto end; + } + + action_status = lttng_action_incr_value_get_session_name(action, + &session_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Session name not set for incr-value action"); + ret = LTTNG_ERR_INVALID_MAP; + goto end; + } + + action_status = lttng_action_incr_value_borrow_key_mutable(action, &key); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Key not set for incr-value action"); + ret = LTTNG_ERR_INVALID_MAP; + goto end; + } + + /* Returns a refcounted reference */ + session = session_find_by_name(session_name); + if(!session) { + DBG("Session not found for incr-value action: session-name=%s", + session_name); + ret = LTTNG_OK; + goto end; + } + + cond_status = lttng_condition_on_event_get_rule(condition, &event_rule); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Error getting on-event condition event-rule"); + ret = LTTNG_ERR_INVALID_MAP; + session_put(session); + goto end; + } + + action_tracer_token = lttng_action_incr_value_get_tracer_token(action); + + switch (lttng_event_rule_get_domain_type(event_rule)) { + case LTTNG_DOMAIN_UST: + ret = sync_incr_value_action_ust(session, condition, + map_name, action_tracer_token, key, state); + if (ret == LTTNG_ERR_UST_EVENT_EXIST) { + DBG("Incr-value action already registered"); + ret = LTTNG_OK; + } + break; + case LTTNG_DOMAIN_KERNEL: + ret = sync_incr_value_action_kernel(session, creds, condition, + map_name, action_tracer_token, key, state); + if (ret == LTTNG_ERR_KERN_EVENT_EXIST) { + DBG("Incr-value action already registered"); + ret = LTTNG_OK; + } + break; + default: + abort(); + } + + goto end; +end: + if (session) { + session_put(session); + } + return ret; +} + +static +enum lttng_error_code sync_one_tracer_executed_action( + const struct lttng_credentials *creds, + const struct lttng_condition *condition, + const struct lttng_action *action, + enum tracer_executed_action_state state) +{ + enum lttng_action_type action_type; + enum lttng_error_code ret; + + action_type = lttng_action_get_type(action); + assert(action_type != LTTNG_ACTION_TYPE_GROUP); + + switch (action_type) { + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + DBG("Action type \"%s\" is a tracer executed action.", + lttng_action_type_string(action_type)); + + ret = sync_incr_value_action(creds, condition, action, state); + if (ret != LTTNG_OK) { + ERR("Error syncing increment value action to the tracer"); + } + break; + default: + DBG("Action type \"%s\" is not a tracer executed action.", + lttng_action_type_string(action_type)); + ret = LTTNG_OK; + goto end; + } + +end: + return ret; +} + +static +enum lttng_error_code sync_all_tracer_executed_actions( + const struct lttng_trigger *trigger, + const struct lttng_credentials *cmd_creds, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret; + unsigned int i, count; + enum lttng_action_status action_status; + enum lttng_action_type action_type; + const struct lttng_action *action; + const struct lttng_condition *condition; + + condition = lttng_trigger_get_const_condition(trigger); + action = lttng_trigger_get_const_action(trigger); + + action_type = lttng_action_get_type(action); + + DBG("Iterating over all actions of trigger \"%s\" to sync any tracer executed actions", + trigger->name); + + if (action_type != LTTNG_ACTION_TYPE_GROUP) { + ret = sync_one_tracer_executed_action(cmd_creds, condition, action, + state); + } else { + action_status = lttng_action_group_get_count(action, &count); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + for (i = 0; i < count; i++) { + const struct lttng_action *inner_action = + lttng_action_group_get_at_index(action, i); + + ret = sync_one_tracer_executed_action(cmd_creds, condition, + inner_action, state); + if (ret != LTTNG_OK) { + ERR("Error syncing tracer executed action"); + goto end; + } + } + } + +end: + return ret; +} + /* * Fill lttng_channel array of all channels. */ @@ -1523,6 +1866,237 @@ end: return ret; } +enum lttng_error_code cmd_add_map(struct command_ctx *cmd_ctx, int sock) +{ + int ret; + enum lttng_error_code ret_code; + size_t map_len; + struct lttng_payload map_payload; + ssize_t sock_recv_len; + struct lttng_map *map = NULL; + const struct lttng_credentials cmd_creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid), + }; + + lttng_payload_init(&map_payload); + map_len = (size_t) cmd_ctx->lsm.u.add_map.length; + ret = lttng_dynamic_buffer_set_size( + &map_payload.buffer, map_len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + sock_recv_len = lttcomm_recv_unix_sock( + sock, map_payload.buffer.data, map_len); + if (sock_recv_len < 0 || sock_recv_len != map_len) { + ERR("Failed to receive \"register map\" command payload"); + ret_code = LTTNG_ERR_INVALID_MAP; + goto end; + } + + /* Deserialize map. */ + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &map_payload, 0, -1); + + if (lttng_map_create_from_payload(&view, &map) != map_len) { + ERR("Invalid map payload received in \"add map\" command"); + ret_code = LTTNG_ERR_INVALID_MAP; + goto end; + } + } + + switch (lttng_map_get_domain(map)) { + case LTTNG_DOMAIN_KERNEL: + ret_code = map_kernel_add(cmd_ctx->session->kernel_session, map); + if (ret_code != LTTNG_OK) { + ERR("Creating a new kernel map: %s", lttng_strerror(ret_code)); + goto end; + } + + ret_code = LTTNG_OK; + break; + case LTTNG_DOMAIN_UST: + ret = map_ust_add(cmd_ctx->session->ust_session, map); + if (ret) { + ERR("Creating a new UST map: %s", lttng_strerror(-ret)); + ret_code = ret; + goto end; + } + + ret_code = LTTNG_OK; + break; + default: + abort(); + } + + + { + struct lttng_triggers *triggers = NULL; + enum lttng_trigger_status t_status; + unsigned int count, i; + + /* + * FRDESO: beware of moving this code. This is currently not + * racy because this is executed by the client thread and the + * client thread is the thread registering new triggers. If + * this code is relocate special care must be taken. + */ + ret_code = notification_thread_command_list_triggers( + notification_thread_handle, 0, &triggers); + if (ret_code != LTTNG_OK) { + ret = -1; + goto end; + } + + assert(triggers); + + t_status = lttng_triggers_get_count(triggers, &count); + if (t_status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + lttng_triggers_destroy(triggers); + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_trigger *trigger; + + trigger = lttng_triggers_get_at_index(triggers, i); + assert(trigger); + + ret_code = sync_all_tracer_executed_actions(trigger, + &cmd_creds, TRACER_EXECUTED_ACTION_STATE_REGISTER); + assert(ret_code == LTTNG_OK); + } + + lttng_triggers_destroy(triggers); + } + + lttng_map_put(map); + +end: + lttng_payload_reset(&map_payload); + return ret_code; +} + +enum lttng_error_code cmd_enable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name) +{ + struct ltt_ust_session *usess = session->ust_session; + enum lttng_error_code ret_code; + + DBG("Enabling map %s for session %s", map_name, session->name); + + rcu_read_lock(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_map *kmap; + struct ltt_kernel_session *ksess = session->kernel_session; + + kmap = trace_kernel_get_map_by_name(map_name, ksess); + if (kmap == NULL) { + ret_code = LTTNG_ERR_KERNEL_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_kernel_enable(ksess, kmap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_map *umap; + struct lttng_ht *map_ht; + + map_ht = usess->domain_global.maps; + + umap = trace_ust_find_map_by_name(map_ht, map_name); + if (umap == NULL) { + ret_code = LTTNG_ERR_UST_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_ust_enable(usess, umap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + default: + abort(); + } + + ret_code = LTTNG_OK; +error: + rcu_read_unlock(); + return ret_code; +} + +enum lttng_error_code cmd_disable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name) +{ + enum lttng_error_code ret_code; + + rcu_read_lock(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_map *kmap; + struct ltt_kernel_session *ksess = session->kernel_session; + + kmap = trace_kernel_get_map_by_name(map_name, ksess); + if (kmap == NULL) { + ret_code = LTTNG_ERR_KERNEL_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_kernel_disable(ksess, kmap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_map *umap; + struct lttng_ht *map_ht; + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + map_ht = usess->domain_global.maps; + + umap = trace_ust_find_map_by_name(map_ht, map_name); + if (umap == NULL) { + ret_code = LTTNG_ERR_UST_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_ust_disable(usess, umap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + default: + abort(); + } + + ret_code = LTTNG_OK; + +error: + rcu_read_unlock(); + return ret_code; +} + enum lttng_error_code cmd_process_attr_tracker_get_tracking_policy( struct ltt_session *session, enum lttng_domain_type domain, @@ -2594,7 +3168,7 @@ ssize_t cmd_list_syscalls(struct lttng_event **events) int cmd_start_trace(struct ltt_session *session) { enum lttng_error_code ret; - unsigned long nb_chan = 0; + unsigned long nb_chan = 0, nb_map = 0; struct ltt_kernel_session *ksession; struct ltt_ust_session *usess; const bool session_rotated_after_last_stop = @@ -2637,11 +3211,13 @@ int cmd_start_trace(struct ltt_session *session) */ if (usess && usess->domain_global.channels) { nb_chan += lttng_ht_get_count(usess->domain_global.channels); + nb_map += lttng_ht_get_count(usess->domain_global.maps); } if (ksession) { nb_chan += ksession->channel_count; + nb_map += ksession->map_count; } - if (!nb_chan) { + if (!nb_chan && !nb_map) { ret = LTTNG_ERR_NO_CHANNEL; goto error; } @@ -3653,6 +4229,64 @@ end: return ret; } +enum lttng_error_code cmd_list_maps(enum lttng_domain_type domain, + struct ltt_session *session, + struct lttng_map_list **return_map_list) +{ + enum lttng_error_code ret_code; + struct lttng_map_list *map_list = NULL; + + map_list = lttng_map_list_create(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + if (session->kernel_session != NULL) { + struct ltt_kernel_map *kmap; + cds_list_for_each_entry(kmap, + &session->kernel_session->map_list.head, list) { + enum lttng_map_status map_status; + map_status = lttng_map_list_add(map_list, kmap->map); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error appending kernel map to list"); + ret_code = LTTNG_ERR_FATAL; + break; + } + } + + } + break; + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_map *umap; + struct lttng_ht_iter iter; + + rcu_read_lock(); + cds_lfht_for_each_entry(session->ust_session->domain_global.maps->ht, + &iter.iter, umap, node.node) { + enum lttng_map_status map_status; + map_status = lttng_map_list_add(map_list, umap->map); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error appending UST map to list"); + ret_code = LTTNG_ERR_FATAL; + break; + } + } + rcu_read_unlock(); + break; + } + default: + ret_code = LTTNG_ERR_UND; + goto end; + } + + *return_map_list = map_list; + map_list = NULL; + ret_code = LTTNG_OK; +end: + lttng_map_list_destroy(map_list); + return ret_code; +} + /* * Command LTTNG_LIST_EVENTS processed by the client thread. */ @@ -4293,21 +4927,82 @@ end: return ret; } -static enum lttng_error_code trigger_modifies_event_notifier( - const struct lttng_trigger *trigger, bool *adds_event_notifier) +static +enum lttng_error_code synchronize_tracer_notifier_register( + struct notification_thread_handle *notification_thread, + struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds) { - enum lttng_error_code ret_code = LTTNG_OK; - const struct lttng_condition *condition = NULL; + enum lttng_error_code ret_code; + struct lttng_condition *condition = lttng_trigger_get_condition(trigger); + const char *trigger_name; + uid_t trigger_owner; + enum lttng_trigger_status trigger_status; + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction( + trigger); - condition = lttng_trigger_get_const_condition(trigger); - if (!condition) { - ret_code = LTTNG_ERR_INVALID_TRIGGER; - goto end; + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_owner); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? trigger_name : "(unnamed)"; + + session_lock_list(); + switch (trigger_domain) { + case LTTNG_DOMAIN_KERNEL: + { + ret_code = kernel_register_event_notifier(trigger, cmd_creds); + if (ret_code != LTTNG_OK) { + enum lttng_error_code notif_thread_unregister_ret; + + notif_thread_unregister_ret = + notification_thread_command_unregister_trigger( + notification_thread, trigger); + + if (notif_thread_unregister_ret != LTTNG_OK) { + /* Return the original error code. */ + ERR("Failed to unregister trigger from notification thread during error recovery: trigger name = '%s', trigger owner uid = %d, error code = %d", + trigger_name, + (int) trigger_owner, + ret_code); + } + } + break; + } + case LTTNG_DOMAIN_UST: + ust_app_global_update_all_event_notifier_rules(); + break; + case LTTNG_DOMAIN_NONE: + abort(); + default: + { + /* Agent domains. */ + struct agent *agt = agent_find_by_event_notifier_domain( + trigger_domain); + if (!agt) { + agt = agent_create(trigger_domain); + if (!agt) { + ret_code = LTTNG_ERR_NOMEM; + goto end_unlock_session_list; + } + agent_add(agt, trigger_agents_ht_by_domain); + } + + ret_code = trigger_agent_enable(trigger, agt); + if (ret_code != LTTNG_OK) { + goto end_unlock_session_list; + } + + break; + } } - *adds_event_notifier = lttng_condition_get_type(condition) == - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; -end: + ret_code = LTTNG_OK; +end_unlock_session_list: + session_unlock_list(); return ret_code; } @@ -4317,8 +5012,8 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c struct lttng_trigger **return_trigger) { enum lttng_error_code ret_code; - bool must_update_event_notifiers; const char *trigger_name; + const struct lttng_condition *condition; uid_t trigger_owner; enum lttng_trigger_status trigger_status; @@ -4334,7 +5029,7 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c trigger_name, (int) trigger_owner, (int) lttng_credentials_get_uid(cmd_creds)); - /* +/* * Validate the trigger credentials against the command credentials. * Only the root user can register a trigger with non-matching * credentials. @@ -4384,72 +5079,30 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? trigger_name : "(unnamed)"; - ret_code = trigger_modifies_event_notifier(trigger, &must_update_event_notifiers); - if (ret_code != LTTNG_OK) { - ERR("Failed to determine if event modifies event notifiers: trigger name = '%s', trigger owner uid = %d, error code = %d", - trigger_name, (int) trigger_owner, ret_code); - goto end; - } - /* * Synchronize tracers if the trigger adds an event notifier. */ - if (must_update_event_notifiers) { - const enum lttng_domain_type trigger_domain = - lttng_trigger_get_underlying_domain_type_restriction(trigger); - - session_lock_list(); - switch (trigger_domain) { - case LTTNG_DOMAIN_KERNEL: - { - ret_code = kernel_register_event_notifier( - trigger, cmd_creds); - if (ret_code != LTTNG_OK) { - const enum lttng_error_code notif_thread_unregister_ret = - notification_thread_command_unregister_trigger( - notification_thread, - trigger); - - if (notif_thread_unregister_ret != LTTNG_OK) { - /* Return the original error code. */ - ERR("Failed to unregister trigger from notification thread during error recovery: trigger name = '%s', trigger owner uid = %d, error code = %d", - trigger_name, - (int) trigger_owner, - ret_code); - } - } - break; + if (lttng_trigger_needs_tracer_notifier(trigger)) { + ret_code = synchronize_tracer_notifier_register(notification_thread, + trigger, cmd_creds); + if (ret_code != LTTNG_OK) { + ERR("Error registering tracer notifier"); + goto end; } - case LTTNG_DOMAIN_UST: - ust_app_global_update_all_event_notifier_rules(); - break; - case LTTNG_DOMAIN_NONE: - abort(); - default: - { - /* Agent domains. */ - struct agent *agt = agent_find_by_event_notifier_domain( - trigger_domain); - - if (!agt) { - agt = agent_create(trigger_domain); - if (!agt) { - ret_code = LTTNG_ERR_NOMEM; - goto end_unlock_session_list; - } - agent_add(agt, trigger_agents_ht_by_domain); - } - - ret_code = trigger_agent_enable(trigger, agt); - if (ret_code != LTTNG_OK) { - goto end_unlock_session_list; - } + } - break; - } - } + condition = lttng_trigger_get_const_condition(trigger); + /* TODO: Extract condition below to lttng_trigger internal function */ + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT) { + session_lock_list(); + ret_code = sync_all_tracer_executed_actions(trigger, + cmd_creds, TRACER_EXECUTED_ACTION_STATE_REGISTER); session_unlock_list(); + if (ret_code != LTTNG_OK) { + ERR("Error registering tracer executed actions"); + goto end; + } } /* @@ -4467,6 +5120,57 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c } end: return ret_code; +} + +static +enum lttng_error_code synchronize_tracer_notifier_unregister( + const struct lttng_trigger *trigger) +{ + enum lttng_error_code ret_code; + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction( + trigger); + + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); + + session_lock_list(); + switch (trigger_domain) { + case LTTNG_DOMAIN_KERNEL: + ret_code = kernel_unregister_event_notifier(trigger); + break; + case LTTNG_DOMAIN_UST: + ust_app_global_update_all_event_notifier_rules(); + break; + case LTTNG_DOMAIN_NONE: + abort(); + default: + { + /* Agent domains. */ + struct agent *agt = agent_find_by_event_notifier_domain( + trigger_domain); + if (!agt) { + agt = agent_create(trigger_domain); + if (!agt) { + ret_code = LTTNG_ERR_NOMEM; + goto end_unlock_session_list; + } + agent_add(agt, trigger_agents_ht_by_domain); + } + + ret_code = trigger_agent_disable(trigger, agt); + if (ret_code != LTTNG_OK) { + goto end_unlock_session_list; + } + + break; + } + } + + ret_code = LTTNG_OK; + end_unlock_session_list: session_unlock_list(); return ret_code; @@ -4477,15 +5181,14 @@ enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd struct notification_thread_handle *notification_thread) { enum lttng_error_code ret_code; - bool must_update_event_notifiers; const char *trigger_name; + struct lttng_trigger *real_trigger; uid_t trigger_owner; enum lttng_trigger_status trigger_status; trigger_status = lttng_trigger_get_name(trigger, &trigger_name); trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? trigger_name : "(unnamed)"; - trigger_status = lttng_trigger_get_owner_uid( - trigger, &trigger_owner); + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_owner); assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); DBG("Running unregister trigger command: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", @@ -4509,18 +5212,20 @@ enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd } } - ret_code = trigger_modifies_event_notifier(trigger, &must_update_event_notifiers); + ret_code = notification_thread_command_get_trigger( + notification_thread, trigger, &real_trigger); if (ret_code != LTTNG_OK) { - ERR("Failed to determine if event modifies event notifiers: trigger name = '%s', trigger owner uid = %d, error code = %d", + DBG("Failed to get the real trigger from notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", trigger_name, (int) trigger_owner, ret_code); goto end; } - ret_code = notification_thread_command_unregister_trigger(notification_thread, - trigger); + ret_code = notification_thread_command_unregister_trigger( + notification_thread, real_trigger); if (ret_code != LTTNG_OK) { DBG("Failed to unregister trigger from notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", trigger_name, (int) trigger_owner, ret_code); + goto end; } /* @@ -4529,56 +5234,28 @@ enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd * the tracers from producing notifications associated with this * event notifier. */ - if (must_update_event_notifiers) { - const enum lttng_domain_type trigger_domain = - lttng_trigger_get_underlying_domain_type_restriction( - trigger); - - session_lock_list(); - switch (trigger_domain) { - case LTTNG_DOMAIN_KERNEL: - { - ret_code = kernel_unregister_event_notifier( - trigger); - break; + if (lttng_trigger_needs_tracer_notifier(real_trigger)) { + ret_code = synchronize_tracer_notifier_unregister(real_trigger); + if (ret_code != LTTNG_OK) { + ERR("Error unregistering trigger to tracer."); + goto end; } - case LTTNG_DOMAIN_UST: - ust_app_global_update_all_event_notifier_rules(); - break; - case LTTNG_DOMAIN_NONE: - abort(); - default: - { - /* Agent domains. */ - struct agent *agt = agent_find_by_event_notifier_domain( - trigger_domain); - - if (!agt) { - agt = agent_create(trigger_domain); - if (!agt) { - ret_code = LTTNG_ERR_NOMEM; - goto end_unlock_session_list; - } - agent_add(agt, trigger_agents_ht_by_domain); - } - - ret_code = trigger_agent_disable(trigger, agt); - if (ret_code != LTTNG_OK) { - goto end_unlock_session_list; - } - break; - } - } + } - session_unlock_list(); + session_lock_list(); + ret_code = sync_all_tracer_executed_actions(real_trigger, + cmd_creds, TRACER_EXECUTED_ACTION_STATE_UNREGISTER); + session_unlock_list(); + if (ret_code != LTTNG_OK) { + ERR("Error registering tracer executed actions"); + goto end; } + lttng_trigger_put(real_trigger); end: return ret_code; -end_unlock_session_list: - session_unlock_list(); - return ret_code;} +} int cmd_list_triggers(struct command_ctx *cmd_ctx, struct notification_thread_handle *notification_thread, @@ -4603,6 +5280,76 @@ end: lttng_triggers_destroy(triggers); return ret; } + +int cmd_list_map_values(const char *session_name, + const struct lttng_map *map, + const struct lttng_map_query *map_query, + struct lttng_map_content **return_map_content) +{ + enum lttng_error_code ret; + struct ltt_session *session; + enum lttng_domain_type domain; + const char *map_name; + enum lttng_map_status map_status; + + /* Returns a refcounted reference */ + session = session_find_by_name(session_name); + if (!session) { + DBG("Session '%s' not found", session_name); + ret = LTTNG_ERR_SESS_NOT_FOUND; + goto end; + } + + domain = lttng_map_get_domain(map); + + map_status = lttng_map_get_name(map, &map_name); + assert(map_status == LTTNG_MAP_STATUS_OK); + + if (domain == LTTNG_DOMAIN_KERNEL) { + if (session->kernel_session) { + struct ltt_kernel_map *kmap; + + kmap = trace_kernel_get_map_by_name(map_name, + session->kernel_session); + if (kmap) { + ret = kernel_list_map_values(kmap, map_query, + return_map_content); + if (ret != LTTNG_OK) { + ERR("Error listing kernel map '%s' values", map_name); + goto end; + } + } else { + DBG("No kernel map '%s' in session '%s'", map_name, session_name); + } + } + } else if (domain == LTTNG_DOMAIN_UST) { + if (session->ust_session) { + struct ltt_ust_map *umap; + struct ltt_ust_session *usess = session->ust_session; + + umap = trace_ust_find_map_by_name( + usess->domain_global.maps, map_name); + if (umap) { + ret = ust_app_map_list_values(usess, umap, + map_query, return_map_content); + if (ret) { + ret = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + ERR("Error listing UST map '%s' values", map_name); + goto end; + } + } else { + DBG("No UST map '%s' in session '%s'", map_name, session_name); + } + } + } + + + ret = LTTNG_OK; +end: + session_put(session); + return ret; +} + /* * Send relayd sockets from snapshot output to consumer. Ignore request if the * snapshot output is *not* set with a remote destination. diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h index dcda2365d..898eba2ed 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -10,10 +10,12 @@ #include "context.h" #include "lttng-sessiond.h" +#include "lttng/map/map.h" #include "lttng/tracker.h" #include "session.h" #include + struct notification_thread_handle; /* @@ -49,6 +51,13 @@ int cmd_disable_channel(struct ltt_session *session, int cmd_enable_channel(struct ltt_session *session, const struct lttng_domain *domain, const struct lttng_channel *attr, int wpipe); +enum lttng_error_code cmd_add_map(struct command_ctx *cmd_ctx, int sock); + +enum lttng_error_code cmd_enable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name); + +enum lttng_error_code cmd_disable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name); /* Process attribute tracker commands */ enum lttng_error_code cmd_process_attr_tracker_get_tracking_policy( @@ -114,6 +123,9 @@ ssize_t cmd_list_events(enum lttng_domain_type domain, struct lttng_payload *payload); ssize_t cmd_list_channels(enum lttng_domain_type domain, struct ltt_session *session, struct lttng_channel **channels); +enum lttng_error_code cmd_list_maps(enum lttng_domain_type domain, + struct ltt_session *session, + struct lttng_map_list **return_map_list); ssize_t cmd_list_domains(struct ltt_session *session, struct lttng_domain **domains); void cmd_list_lttng_sessions(struct lttng_session *sessions, @@ -167,6 +179,10 @@ int cmd_rotation_set_schedule(struct ltt_session *session, uint64_t value, struct notification_thread_handle *notification_thread_handle); +int cmd_list_map_values(const char *session_name, const struct lttng_map *map, + const struct lttng_map_query *map_query, + struct lttng_map_content **return_map_content); + const struct cmd_completion_handler *cmd_pop_completion_handler(void); int start_kernel_session(struct ltt_kernel_session *ksess); int stop_kernel_session(struct ltt_kernel_session *ksess); diff --git a/src/bin/lttng-sessiond/condition-internal.c b/src/bin/lttng-sessiond/condition-internal.c index dc21ca3af..bde25ea73 100644 --- a/src/bin/lttng-sessiond/condition-internal.c +++ b/src/bin/lttng-sessiond/condition-internal.c @@ -13,9 +13,10 @@ #include #include #include -#include -#include +#include +#include #include +#include #include "condition-internal.h" static @@ -95,7 +96,7 @@ unsigned long lttng_condition_session_rotation_hash( } static -unsigned long lttng_condition_event_rule_hash( +unsigned long lttng_condition_on_event_hash( const struct lttng_condition *condition) { unsigned long hash, condition_type; @@ -103,8 +104,7 @@ unsigned long lttng_condition_event_rule_hash( const struct lttng_event_rule *event_rule; condition_type = (unsigned long) condition->type; - - condition_status = lttng_condition_event_rule_get_rule(condition, + condition_status = lttng_condition_on_event_get_rule(condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); @@ -128,10 +128,41 @@ unsigned long lttng_condition_hash(const struct lttng_condition *condition) case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: return lttng_condition_session_rotation_hash(condition); - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: - return lttng_condition_event_rule_hash(condition); + case LTTNG_CONDITION_TYPE_ON_EVENT: + return lttng_condition_on_event_hash(condition); default: //ERR("[notification-thread] Unexpected condition type caught"); abort(); } } + +LTTNG_HIDDEN +struct lttng_condition *lttng_condition_copy(const struct lttng_condition *condition) +{ + int ret; + struct lttng_payload copy_buffer; + struct lttng_condition *copy = NULL; + + lttng_payload_init(©_buffer); + + ret = lttng_condition_serialize(condition, ©_buffer); + if (ret < 0) { + goto end; + } + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + ©_buffer, 0, -1); + ret = lttng_condition_create_from_payload( + &view, ©); + if (ret < 0) { + copy = NULL; + goto end; + } + } + +end: + lttng_payload_reset(©_buffer); + return copy; +} diff --git a/src/bin/lttng-sessiond/condition-internal.h b/src/bin/lttng-sessiond/condition-internal.h index 2863f7bd8..270a8b1af 100644 --- a/src/bin/lttng-sessiond/condition-internal.h +++ b/src/bin/lttng-sessiond/condition-internal.h @@ -17,4 +17,6 @@ */ unsigned long lttng_condition_hash(const struct lttng_condition *condition); +struct lttng_condition *lttng_condition_copy( + const struct lttng_condition *condition); #endif /* LTTNG_SESSIOND_CONDITION_INTERNAL_H */ diff --git a/src/bin/lttng-sessiond/dispatch.c b/src/bin/lttng-sessiond/dispatch.c index d33a3cba8..5748223d7 100644 --- a/src/bin/lttng-sessiond/dispatch.c +++ b/src/bin/lttng-sessiond/dispatch.c @@ -63,10 +63,13 @@ static void update_ust_app(int app_sock) /* For all tracing session(s) */ cds_list_for_each_entry_safe(sess, stmp, &session_list->head, list) { + if (!session_get(sess)) { continue; } session_lock(sess); + + if (!sess->active || !sess->ust_session) { goto unlock_session; } diff --git a/src/bin/lttng-sessiond/event-notifier-error-accounting.c b/src/bin/lttng-sessiond/event-notifier-error-accounting.c new file mode 100644 index 000000000..aea054866 --- /dev/null +++ b/src/bin/lttng-sessiond/event-notifier-error-accounting.c @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "event-notifier-error-accounting.h" +#include "lttng-ust-error.h" +#include "ust-app.h" + +struct index_ht_entry { + struct lttng_ht_node_u64 node; + uint64_t error_counter_index; + struct rcu_head rcu_head; +}; + +struct error_account_entry { + struct lttng_ht_node_u64 node; + struct rcu_head rcu_head; + struct ustctl_daemon_counter *daemon_counter; + /* + * Those `lttng_ust_object_data` are anonymous handles to the counters + * objects. + * They are only used to be duplicated for each new applications of the + * user. To destroy them, call with the `sock` parameter set to -1. + * e.g. `ustctl_release_object(-1, data)`; + */ + struct lttng_ust_object_data *counter; + struct lttng_ust_object_data **cpu_counters; + int nr_counter_cpu_fds; +}; + +struct kernel_error_account_entry { + int kernel_event_notifier_error_counter_fd; +}; + +static struct kernel_error_account_entry kernel_error_accountant = { 0 }; + +/* Hashtable mapping event notifier token to index_ht_entry */ +static struct lttng_ht *error_counter_indexes_ht; + +/* Hashtable mapping uid to error_account_entry */ +static struct lttng_ht *error_counter_uid_ht; + +static uint64_t error_counter_size = 0; +struct lttng_index_allocator *index_allocator; + +static inline +const char *error_accounting_status_str( + enum event_notifier_error_accounting_status status) +{ + switch (status) { + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK: + return "OK"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR: + return "ERROR"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND: + return "NOT_FOUND"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM: + return "NOMEM"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE: + return "NO_INDEX_AVAILABLE"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_APP_DEAD: + return "APP_DEAD"; + default: + abort(); + } +} + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_init(uint64_t nb_bucket) +{ + enum event_notifier_error_accounting_status status; + + index_allocator = lttng_index_allocator_create(nb_bucket); + if (!index_allocator) { + ERR("Failed to allocate event notifier error counter index"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM; + goto error_index_allocator; + } + + error_counter_indexes_ht = lttng_ht_new(16, LTTNG_HT_TYPE_U64); + error_counter_uid_ht = lttng_ht_new(16, LTTNG_HT_TYPE_U64); + error_counter_size = nb_bucket; + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + +error_index_allocator: + return status; +} + +static +enum event_notifier_error_accounting_status get_error_counter_index_for_token( + uint64_t tracer_token, uint64_t *error_counter_index) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + struct index_ht_entry *index_entry;; + enum event_notifier_error_accounting_status status; + + lttng_ht_lookup(error_counter_indexes_ht, &tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (node) { + index_entry = caa_container_of(node, struct index_ht_entry, node); + *error_counter_index = index_entry->error_counter_index; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + } else { + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND; + } + + return status; +} + +#ifdef HAVE_LIBLTTNG_UST_CTL +static +struct error_account_entry *get_uid_accounting_entry(const struct ust_app *app) +{ + struct error_account_entry *entry; + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + uint64_t key = app->uid; + + lttng_ht_lookup(error_counter_uid_ht, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if(node == NULL) { + entry = NULL; + } else { + entry = caa_container_of(node, struct error_account_entry, node); + } + + return entry; +} + +static +struct error_account_entry *create_uid_accounting_entry( + const struct ust_app *app) +{ + int i, ret; + struct ustctl_counter_dimension dimension[1] = {0}; + struct ustctl_daemon_counter *daemon_counter; + struct lttng_ust_object_data *counter, **counter_cpus; + int *counter_cpu_fds; + struct error_account_entry *entry = NULL; + + entry = zmalloc(sizeof(struct error_account_entry)); + if (!entry) { + PERROR("Allocating event notifier error acounting entry") + goto error; + } + + entry->nr_counter_cpu_fds = ustctl_get_nr_cpu_per_counter(); + counter_cpu_fds = zmalloc(entry->nr_counter_cpu_fds * sizeof(*counter_cpu_fds)); + if (!counter_cpu_fds) { + ret = -1; + goto error_counter_cpu_fds_alloc; + } + + counter_cpus = zmalloc(entry->nr_counter_cpu_fds * sizeof(**counter_cpus)); + if (!counter_cpus) { + ret = -1; + goto error_counter_cpus_alloc; + } + + for (i = 0; i < entry->nr_counter_cpu_fds; i++) { + counter_cpu_fds[i] = shm_create_anonymous("event-notifier-error-accounting"); + //FIXME error handling + } + + + dimension[0].size = error_counter_size; + dimension[0].has_underflow = false; + dimension[0].has_overflow = false; + + daemon_counter = ustctl_create_counter(1, dimension, 0, -1, + entry->nr_counter_cpu_fds, counter_cpu_fds, + USTCTL_COUNTER_BITNESS_32, + USTCTL_COUNTER_ARITHMETIC_MODULAR, + USTCTL_COUNTER_ALLOC_PER_CPU, + false); + assert(daemon_counter); + + ret = ustctl_create_counter_data(daemon_counter, &counter); + assert(ret == 0); + + for (i = 0; i < entry->nr_counter_cpu_fds; i++) { + ret = ustctl_create_counter_cpu_data(daemon_counter, i, + &counter_cpus[i]); + assert(ret == 0); + } + + entry->daemon_counter = daemon_counter; + entry->counter = counter; + entry->cpu_counters = counter_cpus; + + lttng_ht_node_init_u64(&entry->node, app->uid); + lttng_ht_add_unique_u64(error_counter_uid_ht, &entry->node); + + free(counter_cpu_fds); + + goto end; + +error_counter_cpus_alloc: + free(counter_cpu_fds); +error_counter_cpu_fds_alloc: + free(entry); +error: + entry = NULL; +end: + return entry; +} + +static +enum event_notifier_error_accounting_status send_counter_data_to_ust( + struct ust_app *app, + struct lttng_ust_object_data *new_counter) +{ + int ret; + enum event_notifier_error_accounting_status status; + + /* Attach counter to trigger group */ + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_data_to_ust(app->sock, + app->event_notifier_group.object->handle, new_counter); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Error ustctl send counter data to app pid: %d with ret %d", + app->pid, ret); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + } else { + DBG3("UST app send counter data to ust failed. Application is dead."); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_APP_DEAD; + } + goto end; + } + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +static +enum event_notifier_error_accounting_status send_counter_cpu_data_to_ust( + struct ust_app *app, + struct lttng_ust_object_data *counter, + struct lttng_ust_object_data *counter_cpu) +{ + int ret; + enum event_notifier_error_accounting_status status; + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_cpu_data_to_ust(app->sock, + counter, counter_cpu); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Error ustctl send counter cpu data to app pid: %d with ret %d", + app->pid, ret); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + } else { + DBG3("UST app send counter cpu data to ust failed. Application is dead."); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_APP_DEAD; + } + goto end; + } + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +enum event_notifier_error_accounting_status event_notifier_error_accounting_register_app( + struct ust_app *app) +{ + int ret; + uint64_t i; + struct lttng_ust_object_data *new_counter; + struct error_account_entry *entry; + enum event_notifier_error_accounting_status status; + + /* + * Check if we already have a error counter for the user id of this + * app. If not, create one. + */ + rcu_read_lock(); + entry = get_uid_accounting_entry(app); + if (entry == NULL) { + entry = create_uid_accounting_entry(app); + } + + /* Duplicate counter object data*/ + ret = ustctl_duplicate_ust_object_data(&new_counter, + entry->counter); + assert(ret == 0); + + status = send_counter_data_to_ust(app, new_counter); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error sending counter data to UST tracer: status=%s", + error_accounting_status_str(status)); + goto end; + } + + + app->event_notifier_group.counter = new_counter; + app->event_notifier_group.nr_counter_cpu = entry->nr_counter_cpu_fds; + app->event_notifier_group.counter_cpu = + zmalloc(entry->nr_counter_cpu_fds * sizeof(struct lttng_ust_object_data)); + + for (i = 0; i < entry->nr_counter_cpu_fds; i++) { + struct lttng_ust_object_data *new_counter_cpu = NULL; + + ret = ustctl_duplicate_ust_object_data(&new_counter_cpu, + entry->cpu_counters[i]); + assert(ret == 0); + + status = send_counter_cpu_data_to_ust(app, new_counter, + new_counter_cpu); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error sending counter cpu data to UST tracer: status=%s", + error_accounting_status_str(status)); + goto end; + } + app->event_notifier_group.counter_cpu[i] = new_counter_cpu; + } + +end: + rcu_read_unlock(); + return status; +} + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_unregister_app(struct ust_app *app) +{ + enum event_notifier_error_accounting_status status; + struct error_account_entry *entry; + int i; + + rcu_read_lock(); + entry = get_uid_accounting_entry(app); + if (entry == NULL) { + ERR("Event notitifier error accounting entry not found on app teardown"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + for (i = 0; i < app->event_notifier_group.nr_counter_cpu; i++) { + ustctl_release_object(app->sock, + app->event_notifier_group.counter_cpu[i]); + free(app->event_notifier_group.counter_cpu[i]); + } + + free(app->event_notifier_group.counter_cpu); + + ustctl_release_object(app->sock, app->event_notifier_group.counter); + free(app->event_notifier_group.counter); + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + rcu_read_unlock(); + return status; +} + +static +enum event_notifier_error_accounting_status +event_notifier_error_accounting_ust_get_count( + const struct lttng_trigger *trigger, uint64_t *count) +{ + struct lttng_ht_iter iter; + struct error_account_entry *uid_entry; + uint64_t error_counter_index, global_sum = 0; + enum event_notifier_error_accounting_status status; + size_t dimension_indexes[1]; + + /* + * Go over all error counters (ignoring uid) as a trigger (and trigger + * errors) can be generated from any applications that this session + * daemon is managing. + */ + + rcu_read_lock(); + + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), &error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + + dimension_indexes[0] = error_counter_index; + + cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter, + uid_entry, node.node) { + int ret; + int64_t local_value = 0; + bool overflow = 0, underflow = 0; + ret = ustctl_counter_aggregate(uid_entry->daemon_counter, + dimension_indexes, &local_value, &overflow, + &underflow); + assert(ret == 0); + + /* should always be zero or above. */ + assert(local_value >= 0); + global_sum += (uint64_t) local_value; + + } + + + *count = global_sum; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + +end: + rcu_read_unlock(); + return status; +} + +static +enum event_notifier_error_accounting_status event_notifier_error_accounting_ust_clear( + const struct lttng_trigger *trigger) +{ + struct lttng_ht_iter iter; + struct error_account_entry *uid_entry; + uint64_t error_counter_index; + enum event_notifier_error_accounting_status status; + size_t dimension_indexes[1]; + + /* + * Go over all error counters (ignoring uid) as a trigger (and trigger + * errors) can be generated from any applications that this session + * daemon is managing. + */ + + rcu_read_lock(); + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + + dimension_indexes[0] = error_counter_index; + + cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter, + uid_entry, node.node) { + int ret; + ret = ustctl_counter_clear(uid_entry->daemon_counter, + dimension_indexes); + assert(ret == 0); + } + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + rcu_read_unlock(); + return status; +} +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +static +enum event_notifier_error_accounting_status event_notifier_error_accounting_kernel_clear( + const struct lttng_trigger *trigger) +{ + int ret; + uint64_t error_counter_index; + enum event_notifier_error_accounting_status status; + struct lttng_kernel_counter_clear counter_clear = {0}; + + rcu_read_lock(); + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + + counter_clear.index.number_dimensions = 1; + counter_clear.index.dimension_indexes[0] = error_counter_index; + + ret = kernctl_counter_clear( + kernel_error_accountant.kernel_event_notifier_error_counter_fd, + &counter_clear); + if (ret) { + ERR("Error clearing event notifier error counter"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + rcu_read_unlock(); + return status; +} + +enum event_notifier_error_accounting_status event_notifier_error_accounting_register_kernel( + int kernel_event_notifier_group_fd) +{ + int local_fd = -1, ret; + enum event_notifier_error_accounting_status status; + struct lttng_kernel_counter_conf error_counter_conf = {0}; + + error_counter_conf.arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR; + error_counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_64; + error_counter_conf.global_sum_step = 0; + error_counter_conf.number_dimensions = 1; + error_counter_conf.dimensions[0].size = error_counter_size; + error_counter_conf.dimensions[0].has_underflow = false; + error_counter_conf.dimensions[0].has_overflow = false; + + ret = kernctl_create_event_notifier_group_error_counter( + kernel_event_notifier_group_fd, &error_counter_conf); + if (ret < 0) { + PERROR("ioctl kernel create event notifier group error counter"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl event notifier error counter fd"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto error; + } + + DBG("Kernel event notifier group error counter (fd: %d)", local_fd); + + kernel_error_accountant.kernel_event_notifier_error_counter_fd = local_fd; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + +error: + return status; +} + +static +enum event_notifier_error_accounting_status create_error_counter_index_for_token( + uint64_t tracer_token, uint64_t *error_counter_index) +{ + struct index_ht_entry *index_entry;; + enum lttng_index_allocator_status index_alloc_status; + uint64_t local_error_counter_index; + enum event_notifier_error_accounting_status status; + + /* Allocate a new index for that counter. */ + index_alloc_status = lttng_index_allocator_alloc(index_allocator, + &local_error_counter_index); + switch (index_alloc_status) { + case LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY: + DBG("No more index available in the configured event notifier error counter:" + "number-of-indices=%"PRIu64, + lttng_index_allocator_get_index_count( + index_allocator)); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE; + goto end; + case LTTNG_INDEX_ALLOCATOR_STATUS_OK: + break; + default: + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + index_entry = zmalloc(sizeof(*index_entry)); + if (index_entry == NULL) { + PERROR("Event notifier error counter hashtable entry zmalloc"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM; + goto end; + } + + index_entry->error_counter_index = local_error_counter_index; + lttng_ht_node_init_u64(&index_entry->node, tracer_token); + + lttng_ht_add_unique_u64(error_counter_indexes_ht, &index_entry->node); + + *error_counter_index = local_error_counter_index; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +enum event_notifier_error_accounting_status event_notifier_error_accounting_register_event_notifier( + const struct lttng_trigger *trigger, + uint64_t *error_counter_index) +{ + enum event_notifier_error_accounting_status status; + uint64_t local_error_counter_index; + + rcu_read_lock(); + /* + * Check if this event notifier already has a error counter index + * assigned. + */ + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &local_error_counter_index); + switch (status) { + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND: + DBG("Event notifier error counter index for this tracer token not found. Allocating a new one."); + status = create_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &local_error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error creating index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK: + *error_counter_index = local_error_counter_index; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + break; + default: + break; + } + +end: + rcu_read_unlock(); + return status; +} + +static +enum event_notifier_error_accounting_status event_notifier_error_accounting_kernel_get_count( + const struct lttng_trigger *trigger, uint64_t *count) +{ + struct lttng_kernel_counter_aggregate counter_aggregate = {0}; + enum event_notifier_error_accounting_status status; + uint64_t error_counter_index; + int ret; + + rcu_read_lock(); + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), &error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + + counter_aggregate.index.number_dimensions = 1; + counter_aggregate.index.dimension_indexes[0] = error_counter_index; + + assert(kernel_error_accountant.kernel_event_notifier_error_counter_fd); + + ret = kernctl_counter_get_aggregate_value( + kernel_error_accountant.kernel_event_notifier_error_counter_fd, + &counter_aggregate); + if (ret) { + ERR("Error getting event notifier error count."); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + if (counter_aggregate.value.value < 0) { + ERR("Event notifier error counter less than zero."); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + /* Error count can't be negative. */ + assert(counter_aggregate.value.value >= 0); + *count = (uint64_t) counter_aggregate.value.value; + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + +end: + rcu_read_unlock(); + return status; +} + +enum event_notifier_error_accounting_status event_notifier_error_accounting_get_count( + const struct lttng_trigger *trigger, uint64_t *count) +{ + switch (lttng_trigger_get_underlying_domain_type_restriction(trigger)) { + case LTTNG_DOMAIN_KERNEL: + return event_notifier_error_accounting_kernel_get_count(trigger, count); + case LTTNG_DOMAIN_UST: +#ifdef HAVE_LIBLTTNG_UST_CTL + return event_notifier_error_accounting_ust_get_count(trigger, count); +#else + return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +#endif /* HAVE_LIBLTTNG_UST_CTL */ + default: + abort(); + } +} + +static +enum event_notifier_error_accounting_status event_notifier_error_accounting_clear( + const struct lttng_trigger *trigger) +{ + switch (lttng_trigger_get_underlying_domain_type_restriction(trigger)) { + case LTTNG_DOMAIN_KERNEL: + return event_notifier_error_accounting_kernel_clear(trigger); + case LTTNG_DOMAIN_UST: +#ifdef HAVE_LIBLTTNG_UST_CTL + return event_notifier_error_accounting_ust_clear(trigger); +#else + return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +#endif /* HAVE_LIBLTTNG_UST_CTL */ + default: + abort(); + } +} + +static void free_index_ht_entry(struct rcu_head *head) +{ + struct index_ht_entry *entry = caa_container_of(head, + struct index_ht_entry, rcu_head); + free(entry); +} + +void event_notifier_error_accounting_unregister_event_notifier( + const struct lttng_trigger *trigger) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_u64 *node; + struct index_ht_entry *index_entry; + enum event_notifier_error_accounting_status status; + enum lttng_index_allocator_status index_alloc_status; + uint64_t tracer_token = lttng_trigger_get_tracer_token(trigger); + + status = event_notifier_error_accounting_clear(trigger); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error clearing event notifier error counter index: status=%s", + error_accounting_status_str(status)); + } + + rcu_read_lock(); + lttng_ht_lookup(error_counter_indexes_ht, &tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if(node) { + index_entry = caa_container_of(node, struct index_ht_entry, node); + index_alloc_status = lttng_index_allocator_release( + index_allocator, + index_entry->error_counter_index); + if (index_alloc_status != LTTNG_INDEX_ALLOCATOR_STATUS_OK) { + ERR("Error releasing event notifier error counter index: status=%s", + error_accounting_status_str(status)); + } + + lttng_ht_del(error_counter_indexes_ht, &iter); + call_rcu(&index_entry->rcu_head, free_index_ht_entry); + } + rcu_read_unlock(); +} + +static void free_error_account_entry(struct rcu_head *head) +{ + struct error_account_entry *entry = caa_container_of(head, + struct error_account_entry, rcu_head); +#ifdef HAVE_LIBLTTNG_UST_CTL + int i; + for (i = 0; i < entry->nr_counter_cpu_fds; i++) { + ustctl_release_object(-1, entry->cpu_counters[i]); + free(entry->cpu_counters[i]); + } + + free(entry->cpu_counters); + + ustctl_release_object(-1, entry->counter); + free(entry->counter); + + ustctl_destroy_counter(entry->daemon_counter); +#endif /* HAVE_LIBLTTNG_UST_CTL */ + + free(entry); +} + +void event_notifier_error_accounting_fini(void) +{ + struct lttng_ht_iter iter; + struct index_ht_entry *index_entry; + struct error_account_entry *uid_entry; + + lttng_index_allocator_destroy(index_allocator); + + if (kernel_error_accountant.kernel_event_notifier_error_counter_fd) { + int ret = close(kernel_error_accountant.kernel_event_notifier_error_counter_fd); + if (ret) { + PERROR("Closing kernel event notifier error counter"); + } + } + + rcu_read_lock(); + + cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter, + uid_entry, node.node) { + cds_lfht_del(error_counter_uid_ht->ht, &uid_entry->node.node); + call_rcu(&uid_entry->rcu_head, free_error_account_entry); + } + + cds_lfht_for_each_entry(error_counter_indexes_ht->ht, &iter.iter, + index_entry, node.node) { + cds_lfht_del(error_counter_indexes_ht->ht, &index_entry->node.node); + call_rcu(&index_entry->rcu_head, free_index_ht_entry); + } + + rcu_read_unlock(); + + lttng_ht_destroy(error_counter_uid_ht); + lttng_ht_destroy(error_counter_indexes_ht); +} diff --git a/src/bin/lttng-sessiond/event-notifier-error-accounting.h b/src/bin/lttng-sessiond/event-notifier-error-accounting.h new file mode 100644 index 000000000..889efffa3 --- /dev/null +++ b/src/bin/lttng-sessiond/event-notifier-error-accounting.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef _EVENT_NOTIFIER_ERROR_ACCOUNTING_H +#define _EVENT_NOTIFIER_ERROR_ACCOUNTING_H + +#include + +#include + +#include "ust-app.h" + +enum event_notifier_error_accounting_status { + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_APP_DEAD, +}; + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_init(uint64_t nb_bucket); + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_register_kernel( + int kernel_event_notifier_group_fd); + +#ifdef HAVE_LIBLTTNG_UST_CTL +enum event_notifier_error_accounting_status +event_notifier_error_accounting_register_app(struct ust_app *app); + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_unregister_app(struct ust_app *app); +#else /* HAVE_LIBLTTNG_UST_CTL */ +static inline +enum event_notifier_error_accounting_status +event_notifier_error_accounting_register_app(struct ust_app *app) +{ + return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +} + +static inline +enum event_notifier_error_accounting_status +event_notifier_error_accounting_unregister_app(struct ust_app *app) +{ + return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +} +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_register_event_notifier( + const struct lttng_trigger *trigger, + uint64_t *error_counter_index); + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_get_count( + const struct lttng_trigger *trigger, + uint64_t *count); + +void event_notifier_error_accounting_unregister_event_notifier( + const struct lttng_trigger *trigger); + +void event_notifier_error_accounting_fini(void); + +#endif /* _EVENT_NOTIFIER_ERROR_ACCOUNTING_H */ diff --git a/src/bin/lttng-sessiond/event.c b/src/bin/lttng-sessiond/event.c index 125adb9a0..dd2e4b382 100644 --- a/src/bin/lttng-sessiond/event.c +++ b/src/bin/lttng-sessiond/event.c @@ -13,13 +13,14 @@ #include #include #include -#include +#include #include #include #include #include #include #include +#include #include #include "channel.h" @@ -38,7 +39,7 @@ * Add unique UST event based on the event name, filter bytecode and loglevel. */ static void add_unique_ust_event(struct lttng_ht *ht, - struct ltt_ust_event *event) + struct ltt_ust_event *event, struct lttng_map_key *map_key) { struct cds_lfht_node *node_ptr; struct ltt_ust_ht_key key; @@ -52,6 +53,7 @@ static void add_unique_ust_event(struct lttng_ht *ht, key.loglevel_type = event->attr.loglevel_type; key.loglevel_value = event->attr.loglevel; key.exclusion = event->exclusion; + key.key = map_key; node_ptr = cds_lfht_add_unique(ht->ht, ht->hash_fct(event->node.key, lttng_ht_seed), @@ -113,8 +115,8 @@ int event_kernel_enable_event(struct ltt_kernel_channel *kchan, assert(kchan); assert(event); - kevent = trace_kernel_find_event(event->name, kchan, - event->type, filter); + kevent = trace_kernel_find_event(&kchan->events_list, + 0, event->name, event->type, filter); if (kevent == NULL) { ret = kernel_create_event(event, kchan, filter_expression, filter); /* We have passed ownership */ @@ -142,6 +144,104 @@ end: return ret; } +/* + * Disable kernel tracepoint events for a map from the kernel session of + * a specified event_name and event type. + * On type LTTNG_EVENT_ALL all events with event_name are disabled. + * If event_name is NULL all events of the specified type are disabled. + */ +int map_event_kernel_disable_event(struct ltt_kernel_map *kmap, + uint64_t action_tracer_token) +{ + struct ltt_kernel_event_counter *kevent_counter; + struct lttng_ht_iter iter; + const struct lttng_ht_node_u64 *node; + enum lttng_error_code ret_code; + int ret; + + assert(kmap); + + lttng_ht_lookup(kmap->event_counters_ht, (void *) &action_tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (node){ + kevent_counter = caa_container_of(node, + struct ltt_kernel_event_counter, ht_node); + ret = kernctl_disable(kevent_counter->fd); + if (ret < 0) { + ret_code = LTTNG_ERR_KERN_DISABLE_FAIL; + goto end; + } + kevent_counter->enabled = false; + DBG("Disable kernel event counter"); + } else { + ret_code = LTTNG_ERR_NO_EVENT; + goto end; + } + + ret_code = LTTNG_OK; +end: + return ret_code; +} + +/* + * Enable kernel tracepoint event for a map from the kernel session. + * We own filter_expression and filter. + */ +int map_event_kernel_enable_event(struct ltt_kernel_map *kmap, + const struct lttng_credentials *creds, + uint64_t action_tracer_token, + const struct lttng_event_rule *event_rule, + struct lttng_map_key *key) +{ + int err; + enum lttng_error_code ret_code; + struct ltt_kernel_event_counter *kevent_counter; + struct lttng_ht_iter iter; + const struct lttng_ht_node_u64 *node; + + assert(kmap); + assert(event_rule); + assert(key); + + lttng_ht_lookup(kmap->event_counters_ht, (void *) &action_tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (node){ + kevent_counter = caa_container_of(node, + struct ltt_kernel_event_counter, ht_node); + if (kevent_counter->enabled) { + /* At this point, the event is considered enabled */ + ret_code = LTTNG_ERR_KERN_EVENT_EXIST; + goto end; + } + + err = kernctl_enable(kevent_counter->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + ret_code = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel event counter"); + ret_code = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto end; + } + + } else { + + ret_code = kernel_create_event_counter(kmap, creds, + action_tracer_token, event_rule, key); + if (ret_code != LTTNG_OK) { + goto end; + } + } + + ret_code = LTTNG_OK; +end: + return ret_code; +} + /* * ============================ * UST : The Ultimate Frontier! @@ -162,18 +262,26 @@ int event_ust_enable_tracepoint(struct ltt_ust_session *usess, int ret = LTTNG_OK, to_create = 0; struct ltt_ust_event *uevent; + /* + * FIXME: Frdeso. The tracer token should probably me set for regular + * events too. + */ + uint64_t tracer_token = 0; + assert(usess); assert(uchan); assert(event); rcu_read_lock(); - uevent = trace_ust_find_event(uchan->events, event->name, filter, + uevent = trace_ust_find_event(uchan->events, 0, event->name, filter, (enum lttng_ust_loglevel_type) event->loglevel_type, - event->loglevel, exclusion); + event->loglevel, exclusion, NULL); if (!uevent) { - ret = trace_ust_create_event(event, filter_expression, - filter, exclusion, internal_event, &uevent); + ret = trace_ust_create_event(tracer_token, event->name, NULL, event->type, + event->loglevel_type, event->loglevel, + filter_expression, filter, exclusion, + internal_event, &uevent); /* We have passed ownership */ filter_expression = NULL; filter = NULL; @@ -196,7 +304,7 @@ int event_ust_enable_tracepoint(struct ltt_ust_session *usess, uevent->enabled = 1; if (to_create) { /* Add ltt ust event to channel */ - add_unique_ust_event(uchan->events, uevent); + add_unique_ust_event(uchan->events, uevent, NULL); } if (!usess->active) { @@ -205,10 +313,10 @@ int event_ust_enable_tracepoint(struct ltt_ust_session *usess, if (to_create) { /* Create event on all UST registered apps for session */ - ret = ust_app_create_event_glb(usess, uchan, uevent); + ret = ust_app_create_channel_event_glb(usess, uchan, uevent); } else { /* Enable event on all UST registered apps for session */ - ret = ust_app_enable_event_glb(usess, uchan, uevent); + ret = ust_app_enable_channel_event_glb(usess, uchan, uevent); } if (ret < 0) { @@ -252,6 +360,140 @@ error: return ret; } +/* + * Enable UST tracepoint event for a map from a UST session. + */ +enum lttng_error_code map_event_ust_enable_tracepoint( + struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + uint64_t tracer_token, + char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + int ev_loglevel_value, + char *_filter_expression, + struct lttng_bytecode *_filter, + struct lttng_event_exclusion *exclusion, + bool internal_event) +{ + enum lttng_error_code ret_code = LTTNG_OK; + int ret, to_create = 0; + struct ltt_ust_event *uevent; + struct lttng_bytecode *filter = NULL; + char *filter_expression = NULL; + + + assert(usess); + assert(umap); + + /* + * FIXME: FRDESO: this function was copied from ust-app.c + */ + if (_filter_expression) { + filter_expression = strdup(_filter_expression); + } + + if (_filter) { + filter = zmalloc(sizeof(*filter) + _filter->len); + if (!filter) { + PERROR("Failed to allocate lttng_ust_filter_bytecode: bytecode len = %" PRIu32 " bytes", _filter->len); + goto error; + } + + assert(sizeof(struct lttng_bytecode) == + sizeof(struct lttng_ust_filter_bytecode)); + memcpy(filter, _filter, sizeof(*filter) + _filter->len); + } + + rcu_read_lock(); + + uevent = trace_ust_find_event(umap->events, tracer_token, ev_name, filter, + (enum lttng_ust_loglevel_type) ev_loglevel_type, + ev_loglevel_value, exclusion, key); + if (!uevent) { + ret_code = trace_ust_create_event(tracer_token, ev_name, key, ev_type, + ev_loglevel_type, ev_loglevel_value, + filter_expression, filter, exclusion, + internal_event, &uevent); + /* We have passed ownership */ + filter_expression = NULL; + filter = NULL; + exclusion = NULL; + if (ret_code != LTTNG_OK) { + goto error; + } + + /* Valid to set it after the goto error since uevent is still NULL */ + to_create = 1; + } + + if (uevent->enabled) { + /* It's already enabled so everything is OK */ + assert(!to_create); + ret_code = LTTNG_ERR_UST_EVENT_ENABLED; + goto end; + } + + uevent->enabled = 1; + if (to_create) { + /* Add ltt ust event to map */ + add_unique_ust_event(umap->events, uevent, key); + } + + if (!usess->active) { + goto end; + } + + if (to_create) { + /* Create event on all UST registered apps for session */ + ret = ust_app_create_map_event_glb(usess, umap, uevent); + } else { + /* Enable event on all UST registered apps for session */ + ret = ust_app_enable_map_event_glb(usess, umap, uevent); + } + + if (ret < 0) { + if (ret == -LTTNG_UST_ERR_EXIST) { + ret_code = LTTNG_ERR_UST_EVENT_EXIST; + goto end; + } else { + ret_code = LTTNG_ERR_UST_ENABLE_FAIL; + goto error; + } + } + + DBG("Event UST %s %s in map %s", uevent->attr.name, + to_create ? "created" : "enabled", umap->name); + + ret_code = LTTNG_OK; + +end: + rcu_read_unlock(); + free(filter_expression); + free(filter); + free(exclusion); + return ret_code; + +error: + /* + * Only destroy event on creation time (not enabling time) because if the + * event is found in the map (to_create == 0), it means that at some + * point the enable_event worked and it's thus valid to keep it alive. + * Destroying it also implies that we also destroy it's shadow copy to sync + * everyone up. + */ + if (to_create) { + /* In this code path, the uevent was not added to the hash table */ + trace_ust_destroy_event(uevent); + } + rcu_read_unlock(); + free(filter_expression); + free(filter); + free(exclusion); + return ret_code; +} + /* * Disable UST tracepoint of a channel from a UST session. */ @@ -300,7 +542,7 @@ int event_ust_disable_tracepoint(struct ltt_ust_session *usess, if (!usess->active) { goto next; } - ret = ust_app_disable_event_glb(usess, uchan, uevent); + ret = ust_app_disable_channel_event_glb(usess, uchan, uevent); if (ret < 0 && ret != -LTTNG_UST_ERR_EXIST) { ret = LTTNG_ERR_UST_DISABLE_FAIL; goto error; @@ -319,6 +561,69 @@ error: return ret; } +/* + * Disable UST tracepoint of a map from a UST session. + */ +enum lttng_error_code map_event_ust_disable_tracepoint( + struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + uint64_t tracer_token, + char *event_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + int ev_loglevel_value, + char *filter_expression, + struct lttng_bytecode *filter, + struct lttng_event_exclusion *exclusion, + bool internal_event) +{ + int ret; + enum lttng_error_code ret_code; + struct ltt_ust_event *uevent; + + assert(usess); + assert(umap); + assert(event_name); + + rcu_read_lock(); + + /* + * FIXME: frdeso: We need to pass all the parameters to find the right + * event. + */ + uevent = trace_ust_find_event(umap->events, tracer_token, event_name, filter, + (enum lttng_ust_loglevel_type) ev_loglevel_type, + ev_loglevel_value, exclusion, key); + assert(uevent); + + if (uevent->enabled == 0) { + ret_code = LTTNG_OK; + goto end; + } + + uevent->enabled = 0; + DBG2("Event UST %s disabled in map %s", uevent->attr.name, + umap->name); + + if (!usess->active) { + ret_code = LTTNG_OK; + goto end; + } + + ret = ust_app_disable_map_event_glb(usess, umap, uevent); + if (ret < 0 && ret != -LTTNG_UST_ERR_EXIST) { + ret_code = LTTNG_ERR_UST_DISABLE_FAIL; + goto end; + } + + ret_code = LTTNG_OK; + +end: + rcu_read_unlock(); + return ret_code; +} + /* * Disable all UST tracepoints for a channel from a UST session. */ @@ -372,6 +677,56 @@ error: return ret; } +/* + * Disable all UST tracepoints for a map from a UST session. + */ +int map_event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + int ret, error = 0; + struct lttng_ht_iter iter; + struct ltt_ust_event *uevent = NULL; + struct lttng_event *events = NULL; + + assert(usess); + assert(umap); + + rcu_read_lock(); + + /* Disabling existing events */ + cds_lfht_for_each_entry(umap->events->ht, &iter.iter, uevent, + node.node) { + if (uevent->enabled == 1) { + ret = map_event_ust_disable_tracepoint(usess, umap, + uevent->attr.token, + uevent->attr.name, + uevent->key, + uevent->attr.instrumentation, + (enum lttng_loglevel_type) uevent->attr.loglevel_type, + uevent->attr.loglevel, + uevent->filter_expression, + uevent->filter, + uevent->exclusion, + false); + if (ret < 0) { + error = LTTNG_ERR_UST_DISABLE_FAIL; + continue; + } + } + } + + /* + * FIXME: FRDESO: in the equivalent function + * event_ust_disable_all_tracepoints() (above ^) we also iterator over + * all lttng_event. Do we need to do this here too? + */ + + ret = error ? error : LTTNG_OK; + rcu_read_unlock(); + free(events); + return ret; +} + static void agent_enable_all(struct agent *agt) { struct agent_event *aevent; @@ -601,9 +956,9 @@ int trigger_agent_enable(const struct lttng_trigger *trigger, struct agent *agt) condition = lttng_trigger_get_const_condition(trigger); assert(lttng_condition_get_type(condition) == - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + LTTNG_CONDITION_TYPE_ON_EVENT); - c_status = lttng_condition_event_rule_get_rule(condition, &rule); + c_status = lttng_condition_on_event_get_rule(condition, &rule); assert(c_status == LTTNG_CONDITION_STATUS_OK); assert(lttng_event_rule_get_type(rule) == @@ -779,13 +1134,15 @@ static int event_agent_disable_one(struct ltt_ust_session *usess, * happens thanks to an UST filter. The following -1 is actually * ignored since the type is LTTNG_UST_LOGLEVEL_ALL. */ - uevent = trace_ust_find_event(uchan->events, (char *) ust_event_name, - aevent->filter, LTTNG_UST_LOGLEVEL_ALL, -1, NULL); + /* TODO: JORAJ FRDESO: hmmm what to do with tracer token here? + */ + uevent = trace_ust_find_event(uchan->events, 0, (char *) ust_event_name, + aevent->filter, LTTNG_UST_LOGLEVEL_ALL, -1, NULL, NULL); /* If the agent event exists, it must be available on the UST side. */ assert(uevent); if (usess->active) { - ret = ust_app_disable_event_glb(usess, uchan, uevent); + ret = ust_app_disable_channel_event_glb(usess, uchan, uevent); if (ret < 0 && ret != -LTTNG_UST_ERR_EXIST) { ret = LTTNG_ERR_UST_DISABLE_FAIL; goto error; diff --git a/src/bin/lttng-sessiond/event.h b/src/bin/lttng-sessiond/event.h index 8849ee7cc..42b8fb7c2 100644 --- a/src/bin/lttng-sessiond/event.h +++ b/src/bin/lttng-sessiond/event.h @@ -9,6 +9,7 @@ #define _LTT_EVENT_H #include "trace-kernel.h" +#include "trace-ust.h" struct agent; @@ -19,18 +20,59 @@ int event_kernel_enable_event(struct ltt_kernel_channel *kchan, struct lttng_event *event, char *filter_expression, struct lttng_bytecode *filter); +int map_event_kernel_disable_event(struct ltt_kernel_map *kmap, + uint64_t action_tracer_token); + +int map_event_kernel_enable_event(struct ltt_kernel_map *kmap, + const struct lttng_credentials *creds, + uint64_t tracer_token, + const struct lttng_event_rule *event_rule, + struct lttng_map_key *key); + int event_ust_enable_tracepoint(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct lttng_event *event, char *filter_expression, struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, bool internal_event); + +enum lttng_error_code map_event_ust_enable_tracepoint( + struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + uint64_t tracer_token, + char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + int ev_loglevel_value, + char *filter_expression, + struct lttng_bytecode *filter, + struct lttng_event_exclusion *exclusion, + bool internal_event); + int event_ust_disable_tracepoint(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, const char *event_name); +enum lttng_error_code map_event_ust_disable_tracepoint( + struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + uint64_t tracer_token, + char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + int ev_loglevel_value, + char *filter_expression, + struct lttng_bytecode *filter, + struct lttng_event_exclusion *exclusion, + bool internal_event); + int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan); +int map_event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); + int event_agent_enable(struct ltt_ust_session *usess, struct agent *agt, struct lttng_event *event, struct lttng_bytecode *filter, char *filter_expression); diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 23e8eac2a..25d744d11 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -28,12 +28,18 @@ #include #include -#include -#include +#include +#include #include #include -#include - +#include +#include +#include +#include +#include +#include + +#include "event-notifier-error-accounting.h" #include "lttng-sessiond.h" #include "lttng-syscall.h" #include "condition-internal.h" @@ -41,6 +47,8 @@ #include "kernel.h" #include "kernel-consumer.h" #include "kern-modules.h" +#include "map.h" +#include "sessiond-config.h" #include "utils.h" #include "rotate.h" #include "modprobe.h" @@ -540,9 +548,9 @@ static int userspace_probe_event_rule_add_callsites( assert(creds); event_rule_type = lttng_event_rule_get_type(rule); - assert(event_rule_type == LTTNG_EVENT_RULE_TYPE_UPROBE); + assert(event_rule_type == LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE); - status = lttng_event_rule_uprobe_get_location(rule, &location); + status = lttng_event_rule_userspace_probe_get_location(rule, &location); if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) { ret = -1; goto end; @@ -1989,7 +1997,8 @@ int init_kernel_tracer(void) WARN("Failed to create kernel event notifier group"); kernel_tracer_event_notifier_group_fd = -1; } else { - const enum lttng_error_code error_code_ret = + enum event_notifier_error_accounting_status error_accounting_status; + enum lttng_error_code error_code_ret = kernel_create_event_notifier_group_notification_fd( &kernel_tracer_event_notifier_group_notification_fd); @@ -1997,6 +2006,14 @@ int init_kernel_tracer(void) goto error_modules; } + error_accounting_status = event_notifier_error_accounting_register_kernel( + kernel_tracer_event_notifier_group_fd); + if (error_accounting_status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error initializing event notifier error accounting for kernel tracer."); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING; + goto error_modules; + } + kernel_token_to_event_notifier_rule_ht = cds_lfht_new( DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, @@ -2113,8 +2130,6 @@ void cleanup_kernel_tracer(void) kernel_tracer_fd = -1; } - DBG("Unloading kernel modules"); - modprobe_remove_lttng_all(); free(syscall_table); } @@ -2123,13 +2138,8 @@ bool kernel_tracer_is_initialized(void) { return kernel_tracer_fd >= 0; } - -/* - * Clear a kernel session. - * - * Return LTTNG_OK on success or else an LTTng error code. - */ -enum lttng_error_code kernel_clear_session(struct ltt_session *session) +static +enum lttng_error_code kernel_clear_session_channels(struct ltt_session *session) { int ret; enum lttng_error_code status = LTTNG_OK; @@ -2140,9 +2150,6 @@ enum lttng_error_code kernel_clear_session(struct ltt_session *session) assert(ksess); assert(ksess->consumer); - DBG("Clear kernel session %s (session %" PRIu64 ")", - session->name, session->id); - rcu_read_lock(); if (ksess->active) { @@ -2189,6 +2196,7 @@ enum lttng_error_code kernel_clear_session(struct ltt_session *session) } } + goto end; error: switch (-ret) { @@ -2204,6 +2212,116 @@ end: return status; } +static +enum lttng_error_code kernel_map_clear_all(struct ltt_kernel_map *map) +{ + enum lttng_error_code status; + uint64_t descr_count, i; + int ret; + + assert(map); + + ret = kernctl_counter_map_descriptor_count(map->fd, &descr_count); + if (ret) { + ERR("Error getting map descriptor count"); + status = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + for(i = 0; i < descr_count; i++) { + struct lttng_kernel_counter_map_descriptor descriptor = {0}; + struct lttng_kernel_counter_clear counter_clear = {0}; + + descriptor.descriptor_index = i; + + ret = kernctl_counter_map_descriptor(map->fd, &descriptor); + if (ret) { + ERR("Error getting map descriptor %"PRIu64, i); + status = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + counter_clear.index.number_dimensions = 1; + counter_clear.index.dimension_indexes[0] = descriptor.array_index; + + ret = kernctl_counter_clear(map->fd, &counter_clear); + if (ret) { + ERR("Error clearing value of map descriptor %"PRIu64, i); + status = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + } + + status = LTTNG_OK; +end: + return status; +} + +static +enum lttng_error_code kernel_clear_session_maps(struct ltt_session *session) +{ + enum lttng_error_code status = LTTNG_OK; + struct ltt_kernel_map *map; + struct ltt_kernel_session *ksess = session->kernel_session; + + assert(ksess); + + cds_list_for_each_entry(map, &ksess->map_list.head, list) { + DBG("Clear kernel map %" PRIu64 ", session %s", + map->key, session->name); + status = kernel_map_clear_all(map); + if (status != LTTNG_OK) { + ERR("Clearing all values of map"); + goto end; + } + } + +end: + return status; +} + +/* + * Clear a kernel session. + * + * Return LTTNG_OK on success or else an LTTng error code. + */ +enum lttng_error_code kernel_clear_session(struct ltt_session *session) +{ + enum lttng_error_code status = LTTNG_OK; + struct ltt_kernel_session *ksess = session->kernel_session; + + assert(ksess); + assert(ksess->consumer); + + DBG("Clear kernel session %s (session %" PRIu64 ")", + session->name, session->id); + + rcu_read_lock(); + + if (ksess->active) { + ERR("Expecting inactive session %s (%" PRIu64 ")", session->name, session->id); + status = LTTNG_ERR_FATAL; + goto end; + } + + status = kernel_clear_session_channels(session); + if (status != LTTNG_OK) { + goto end; + } + /* + * Iterate and clear all kernel maps. + */ + status = kernel_clear_session_maps(session); + if (status != LTTNG_OK) { + goto end; + } + + +end: + rcu_read_unlock(); + return status; +} + enum lttng_error_code kernel_create_event_notifier_group_notification_fd( int *event_notifier_group_notification_fd) { @@ -2288,6 +2406,193 @@ int match_trigger(struct cds_lfht_node *node, const void *key) return lttng_trigger_is_equal(trigger, event_notifier_rule->trigger); } +static +int add_key_token(struct lttng_kernel_key_token *kernel_key_token, + const struct lttng_map_key_token *key_token) +{ + int ret; + switch (key_token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + const struct lttng_map_key_token_string *str_token; + str_token = (typeof(str_token)) key_token; + + kernel_key_token->type = LTTNG_KERNEL_KEY_TOKEN_STRING; + kernel_key_token->arg.string_ptr = (uint64_t) str_token->string; + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + const struct lttng_map_key_token_variable *var_token; + var_token = (typeof(var_token)) key_token; + switch (var_token->type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + kernel_key_token->type = LTTNG_KERNEL_KEY_TOKEN_EVENT_NAME; + break; + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + /* The kernel events don't have providers */ + ERR("Provider variable token type not supported for kernel tracer"); + ret = -1; + goto end; + default: + abort(); + } + + break; + } + default: + abort(); + } + ret = 0; +end: + return ret; +} + +enum lttng_error_code kernel_create_event_counter( + struct ltt_kernel_map *kmap, + const struct lttng_credentials *creds, + uint64_t action_tracer_token, + const struct lttng_event_rule *event_rule, + struct lttng_map_key *key) +{ + int err, fd, ret = 0; + unsigned int i, key_token_count; + enum lttng_error_code error_code_ret; + enum lttng_map_key_status status; + struct ltt_kernel_event_counter *event_counter; + struct lttng_kernel_counter_event k_counter_event = {}; + + + event_counter = zmalloc(sizeof(*event_counter)); + if (!event_counter) { + error_code_ret = LTTNG_ERR_NOMEM; + goto error; + } + + trace_kernel_init_event_counter_from_event_rule(event_rule, + &k_counter_event); + event_counter->fd = -1; + event_counter->enabled = 1; + event_counter->action_tracer_token = action_tracer_token; + event_counter->filter = lttng_event_rule_get_filter_bytecode(event_rule); + + k_counter_event.event.token = action_tracer_token; + + /* Set the key pattern for this event counter. */ + k_counter_event.key.nr_dimensions = 1; + + status = lttng_map_key_get_token_count(key, &key_token_count); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + error_code_ret = LTTNG_ERR_UNK; + goto error; + } + + assert(key_token_count > 0); + + k_counter_event.key.key_dimensions[0].nr_key_tokens = key_token_count; + + for (i = 0; i < key_token_count; i++) { + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + + ret = add_key_token(&k_counter_event.key.key_dimensions[0].key_tokens[i], + token); + if (ret) { + ERR("Error appending map key token"); + error_code_ret = LTTNG_ERR_INVALID; + goto error; + } + } + + fd = kernctl_create_counter_event(kmap->fd, &k_counter_event); + if (fd < 0) { + switch (-fd) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Event counter type not implemented"); + error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Event counter %s not found!", k_counter_event.event.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + PERROR("create event counter ioctl"); + } + } + + event_counter->fd = fd; + event_counter->enabled = true; + + /* Prevent fd duplication after execlp() */ + err = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("fcntl session fd"); + } + + if (event_counter->filter) { + err = kernctl_filter(event_counter->fd, event_counter->filter); + if (err < 0) { + switch (-err) { + case ENOMEM: + error_code_ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + error_code_ret = LTTNG_ERR_FILTER_INVAL; + break; + } + goto filter_error; + } + } + if (lttng_event_rule_get_type(event_rule) == + LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE) { + ret = userspace_probe_event_rule_add_callsites( + event_rule, creds, event_counter->fd); + if (ret) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto add_callsite_error; + } + } + + err = kernctl_enable(event_counter->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel counter event"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add event to event list */ + rcu_read_lock(); + lttng_ht_node_init_u64(&event_counter->ht_node, + event_counter->action_tracer_token); + lttng_ht_add_unique_u64(kmap->event_counters_ht, + &event_counter->ht_node); + rcu_read_unlock(); + kmap->event_count++; + + DBG("Kernel event counter %s created (fd: %d)", + event_counter->event->name, + event_counter->fd); + error_code_ret = LTTNG_OK; + +add_callsite_error: +filter_error: +enable_error: +error: + return error_code_ret; +} + static enum lttng_error_code kernel_create_event_notifier_rule( struct lttng_trigger *trigger, const struct lttng_credentials *creds, uint64_t token) @@ -2299,8 +2604,10 @@ static enum lttng_error_code kernel_create_event_notifier_rule( enum lttng_event_rule_type event_rule_type; struct ltt_kernel_event_notifier_rule *event_notifier_rule; struct lttng_kernel_event_notifier kernel_event_notifier = {}; + unsigned int capture_bytecode_count = 0, i; const struct lttng_condition *condition = NULL; const struct lttng_event_rule *event_rule = NULL; + enum lttng_condition_status cond_status; assert(trigger); @@ -2308,10 +2615,10 @@ static enum lttng_error_code kernel_create_event_notifier_rule( assert(condition); condition_type = lttng_condition_get_type(condition); - assert(condition_type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + assert(condition_type == LTTNG_CONDITION_TYPE_ON_EVENT); /* Does not acquire a reference. */ - condition_status = lttng_condition_event_rule_get_rule( + condition_status = lttng_condition_on_event_get_rule( condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); assert(event_rule); @@ -2320,6 +2627,7 @@ static enum lttng_error_code kernel_create_event_notifier_rule( assert(event_rule_type != LTTNG_EVENT_RULE_TYPE_UNKNOWN); error_code_ret = trace_kernel_create_event_notifier_rule(trigger, token, + lttng_condition_on_event_get_error_counter_index(condition), &event_notifier_rule); if (error_code_ret != LTTNG_OK) { goto error; @@ -2332,6 +2640,8 @@ static enum lttng_error_code kernel_create_event_notifier_rule( } kernel_event_notifier.event.token = event_notifier_rule->token; + kernel_event_notifier.error_counter_idx = + lttng_condition_on_event_get_error_counter_index(condition); fd = kernctl_create_event_notifier( kernel_tracer_event_notifier_group_fd, @@ -2384,7 +2694,7 @@ static enum lttng_error_code kernel_create_event_notifier_rule( } if (lttng_event_rule_get_type(event_rule) == - LTTNG_EVENT_RULE_TYPE_UPROBE) { + LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE) { ret = userspace_probe_event_rule_add_callsites( event_rule, creds, event_notifier_rule->fd); if (ret) { @@ -2393,6 +2703,25 @@ static enum lttng_error_code kernel_create_event_notifier_rule( } } + /* Set the capture bytecode if any */ + cond_status = lttng_condition_on_event_get_capture_descriptor_count(condition, &capture_bytecode_count); + assert(cond_status == LTTNG_CONDITION_STATUS_OK); + for (i = 0; i < capture_bytecode_count; i++) { + const struct lttng_bytecode *capture_bytecode = + lttng_condition_on_event_get_capture_bytecode_at_index( + condition, i); + if (capture_bytecode == NULL) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto error; + } + + ret = kernctl_capture(event_notifier_rule->fd, capture_bytecode); + if (ret < 0) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto error; + } + } + err = kernctl_enable(event_notifier_rule->fd); if (err < 0) { switch (-err) { @@ -2452,7 +2781,7 @@ enum lttng_error_code kernel_register_event_notifier( assert(condition); /* Does not acquire a reference to the event rule. */ - status = lttng_condition_event_rule_get_rule( + status = lttng_condition_on_event_get_rule( condition, &event_rule); assert(status == LTTNG_CONDITION_STATUS_OK); @@ -2505,6 +2834,210 @@ error: return error_code_ret; } +struct key_ht_entry { + char *key; + struct lttng_ht_node_str node; +}; + +enum lttng_error_code kernel_list_map_values(const struct ltt_kernel_map *map, + const struct lttng_map_query *query, + struct lttng_map_content **map_content) +{ + enum lttng_map_status map_status; + enum lttng_error_code ret_code; + const char *map_name = NULL; + uint64_t descr_count, descr_idx, cpu_idx; + struct lttng_map_content *local_map_content; + struct lttng_ht *key_ht; + struct lttng_ht *values = NULL; + struct lttng_ht_node_str *node; + struct key_ht_entry *ht_entry; + struct lttng_ht_iter iter; + enum lttng_map_query_status map_query_status; + const char *key_filter; + bool sum_cpus = lttng_map_query_get_config_sum_by_cpu(query); + enum lttng_map_query_config_cpu config_cpu; + int ret; + int selected_cpu; + + + local_map_content = lttng_map_content_create(LTTNG_BUFFER_GLOBAL); + if (!local_map_content) { + ERR("Error creating map content"); + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + map_query_status = lttng_map_query_get_key_filter(query, &key_filter); + if (map_query_status == LTTNG_MAP_QUERY_STATUS_NONE) { + key_filter = NULL; + } else if (map_query_status != LTTNG_MAP_QUERY_STATUS_OK) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + config_cpu = lttng_map_query_get_config_cpu(query); + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int count; + map_query_status = lttng_map_query_get_cpu_count(query, &count); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + assert(count == 1); + + map_query_status = lttng_map_query_get_cpu_at_index(query, 0, + &selected_cpu); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + map_status = lttng_map_get_name(map->map, &map_name); + assert(map_status == LTTNG_MAP_STATUS_OK); + + DBG("Listing kernel map values: map-name = '%s'", map_name); + + ret = kernctl_counter_map_descriptor_count(map->fd, &descr_count); + if (ret) { + ERR("Error getting map descriptor count"); + ret_code = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + /* + * The kernel tracer sends us descriptors that may be identical aside + * from their user token field. This ABI was design this way to cover a + * potential use case where the user wants to know what enabler might + * have contributed to a specific bucket. + * + * We use this hashtable to de-duplicate keys. + */ + if (sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!values) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + } + + DBG("Querying kernel for all map values: " + "map-name = '%s', key-value count = %"PRIu64, + map_name, descr_count); + for (cpu_idx = 0; cpu_idx < utils_get_number_of_possible_cpus(); cpu_idx++) { + struct lttng_kernel_counter_read value = {0}; + + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + if (selected_cpu != cpu_idx) { + continue; + } + } + + if (!sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + assert(values); + } + + key_ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!key_ht) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + for(descr_idx = 0; descr_idx < descr_count; descr_idx++) { + struct lttng_kernel_counter_map_descriptor descriptor = {0}; + + DBG("Querying kernel for map key-value descriptor: " + "map-name = '%s', descriptor = %"PRIu64, + map_name, descr_idx); + descriptor.descriptor_index = descr_idx; + + ret = kernctl_counter_map_descriptor(map->fd, &descriptor); + if (ret) { + ERR("Error getting map descriptor %"PRIu64, descr_idx); + ret_code = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + if (key_filter && strcmp(key_filter, descriptor.key) != 0) { + continue; + } + + lttng_ht_lookup(key_ht, descriptor.key, &iter); + node = lttng_ht_iter_get_node_str(&iter); + if (node) { + /* This key was already appended to the list. */ + continue; + } + + + value.index.number_dimensions = 1; + value.index.dimension_indexes[0] = descriptor.array_index; + value.cpu = cpu_idx; + + DBG("Querying kernel for map descriptor value: " + "map-name = '%s', counter-index = %"PRIu64, + map_name, descriptor.array_index); + ret = kernctl_counter_read_value(map->fd, &value); + if (ret) { + ERR("Error getting value of map descriptor %"PRIu64, descr_idx); + ret_code = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + map_add_or_increment_map_values(values, descriptor.key, + value.value.value, value.value.underflow, + value.value.overflow); + + ht_entry = zmalloc(sizeof(*ht_entry)); + assert(ht_entry); + ht_entry->key = strdup(descriptor.key); + lttng_ht_node_init_str(&ht_entry->node, ht_entry->key); + lttng_ht_add_unique_str(key_ht, &ht_entry->node); + } + + if (!sum_cpus) { + ret = map_new_content_section(local_map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL, + sum_cpus, 0, + cpu_idx, values); + if (ret) { + abort(); + } + + lttng_ht_destroy(values); + } + + /* + * Remove all the keys before destroying the hashtable. + */ + cds_lfht_for_each_entry(key_ht->ht, &iter.iter, ht_entry, node.node) { + struct lttng_ht_iter entry_iter; + + entry_iter.iter.node = &ht_entry->node.node; + lttng_ht_del(key_ht, &entry_iter); + + free(ht_entry); + } + + lttng_ht_destroy(key_ht); + } + + if (sum_cpus) { + ret = map_new_content_section(local_map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL, + sum_cpus, 0, 0, values); + if (ret) { + abort(); + } + lttng_ht_destroy(values); + } + + + *map_content = local_map_content; + local_map_content = NULL; + ret_code = LTTNG_OK; + +end: + lttng_map_content_destroy(local_map_content); + return ret_code; +} + int kernel_get_notification_fd(void) { return kernel_tracer_event_notifier_group_notification_fd; diff --git a/src/bin/lttng-sessiond/kernel.h b/src/bin/lttng-sessiond/kernel.h index a2dd7f060..16d809930 100644 --- a/src/bin/lttng-sessiond/kernel.h +++ b/src/bin/lttng-sessiond/kernel.h @@ -87,12 +87,32 @@ enum lttng_error_code kernel_create_event_notifier_group_notification_fd( enum lttng_error_code kernel_destroy_event_notifier_group_notification_fd( int event_notifier_group_notification_fd); +enum lttng_error_code kernel_create_event_counter( + struct ltt_kernel_map *kmap, + const struct lttng_credentials *creds, + uint64_t action_tracer_token, + const struct lttng_event_rule *event_rule, + struct lttng_map_key *key); + +enum lttng_error_code kernel_register_incr_value_action( + struct ltt_session *session, + const struct lttng_condition *condition, + const char *map_name, + uint64_t tracer_token, + struct lttng_map_key *key); + enum lttng_error_code kernel_register_event_notifier( struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds); enum lttng_error_code kernel_unregister_event_notifier( const struct lttng_trigger *trigger); +enum lttng_error_code kernel_synchronize_tracer_executed_action(void); + +enum lttng_error_code kernel_list_map_values(const struct ltt_kernel_map *map, + const struct lttng_map_query *query, + struct lttng_map_content **map_content); + int kernel_get_notification_fd(void); #endif /* _LTT_KERNEL_CTL_H */ diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index a344cd476..6e4950e54 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -49,9 +49,9 @@ #include "consumer.h" #include "context.h" #include "event.h" +#include "event-notifier-error-accounting.h" #include "kernel.h" #include "kernel-consumer.h" -#include "shm.h" #include "lttng-ust-ctl.h" #include "ust-consumer.h" #include "utils.h" @@ -74,6 +74,7 @@ #include "register.h" #include "manage-apps.h" #include "manage-kernel.h" +#include "modprobe.h" static const char *help_msg = #ifdef LTTNG_EMBED_HELP @@ -83,6 +84,8 @@ NULL #endif ; +#define EVENT_NOTIFIER_ERROR_COUNTER_NUMBER_OF_BUCKET_MAX 65535 + const char *progname; static int lockfile_fd = -1; static int opt_print_version; @@ -120,6 +123,7 @@ static const struct option long_options[] = { { "load", required_argument, 0, 'l' }, { "kmod-probes", required_argument, 0, '\0' }, { "extra-kmod-probes", required_argument, 0, '\0' }, + { "event-notifier-error-number-of-bucket", required_argument, 0, '\0' }, { NULL, 0, 0, 0 } }; @@ -697,6 +701,23 @@ static int set_option(int opt, const char *arg, const char *optname) ret = -ENOMEM; } } + } else if (string_match(optname, "event-notifier-error-number-of-bucket")) { + unsigned long v; + + errno = 0; + v = strtoul(arg, NULL, 0); + if (errno != 0 || !isdigit(arg[0])) { + ERR("Wrong value in --event-notifier-error-number-of-bucket parameter: %s", arg); + return -1; + } + if (v == 0 || v >= EVENT_NOTIFIER_ERROR_COUNTER_NUMBER_OF_BUCKET_MAX) { + ERR("Value out of range for --event-notifier-error-number-of-bucket parameter: %s", arg); + return -1; + } + config.event_notifier_error_counter_bucket = (int) v; + DBG3("Number of event notifier error counter set to non default: %i", + config.event_notifier_error_counter_bucket); + goto end; } else if (string_match(optname, "config") || opt == 'f') { /* This is handled in set_options() thus silent skip. */ goto end; @@ -1243,6 +1264,7 @@ static void destroy_all_sessions_and_wait(void) goto unlock_session; } (void) cmd_stop_trace(session); + (void) cmd_destroy_session(session, notification_thread_handle, NULL); unlock_session: @@ -1589,6 +1611,8 @@ int main(int argc, char **argv) goto stop_threads; } + event_notifier_error_accounting_init(config.event_notifier_error_counter_bucket); + /* * Initialize agent app hash table. We allocate the hash table here * since cleanup() can get called after this point. @@ -1826,6 +1850,7 @@ int main(int argc, char **argv) sessiond_wait_for_quit_pipe(-1); stop_threads: + /* * Ensure that the client thread is no longer accepting new commands, * which could cause new sessions to be created. @@ -1864,7 +1889,7 @@ stop_threads: sessiond_cleanup(); /* - * Wait for all pending call_rcu work to complete tearing shutting down + * Wait for all pending call_rcu work to complete before shutting down * the notification thread. This call_rcu work includes shutting down * UST apps and event notifier pipes. */ @@ -1875,6 +1900,27 @@ stop_threads: lttng_thread_put(notification_thread); } + /* + * Error accounting teardown has to be done after the teardown of all + * event notifier pipes to ensure that no tracer may try to use the + * error accounting facilities. + */ + event_notifier_error_accounting_fini(); + + /* + * Unloading the kernel modules needs to be done after all kernel + * ressources have been released. In our case, this includes the + * notification fd, the event notifier group fd, error accounting fd, + * all event and event notifier fds, etc. + * + * In short, at this point, we need to have called close() on all fds + * received from the kernel tracer. + */ + if (is_root && !config.no_kernel) { + DBG("Unloading kernel modules"); + modprobe_remove_lttng_all(); + } + /* * Ensure all prior call_rcu are done. call_rcu callbacks may push * hash tables to the ht_cleanup thread. Therefore, we ensure that diff --git a/src/bin/lttng-sessiond/map.c b/src/bin/lttng-sessiond/map.c new file mode 100644 index 000000000..6e63504f1 --- /dev/null +++ b/src/bin/lttng-sessiond/map.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + + +#include "lttng/domain.h" +#include +#include +#include + +#include "lttng-sessiond.h" +#include "lttng-ust-error.h" +#include "notification-thread-commands.h" +#include "trace-kernel.h" +#include "trace-ust.h" + +#include "map.h" + +enum lttng_error_code map_kernel_add(struct ltt_kernel_session *ksession, + struct lttng_map *map) +{ + enum lttng_error_code ret; + struct ltt_kernel_map *kmap; + enum lttng_map_status map_status; + const char *map_name; + + assert(lttng_map_get_domain(map) == LTTNG_DOMAIN_KERNEL); + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Can't get map name"); + ret = -1; + goto error; + } + + kmap = trace_kernel_get_map_by_name(map_name, ksession); + if (kmap) { + DBG("Kernel map named \"%s\" already present", map_name); + ret = -1; + goto error; + } + + kmap = trace_kernel_create_map(map); + assert(kmap); + + ret = kernctl_create_session_counter(ksession->fd, + &kmap->counter_conf); + if (ret < 0) { + PERROR("ioctl kernel create session counter"); + goto error; + } + + kmap->fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(kmap->fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session counter fd"); + goto error; + } + + kmap->map = map; + lttng_map_get(map); + cds_list_add(&kmap->list, &ksession->map_list.head); + ksession->map_count++; + + DBG("Kernel session counter created (fd: %d)", kmap->fd); + + ret = kernctl_enable(kmap->fd); + if (ret < 0) { + PERROR("Enable kernel map"); + } + + ret = LTTNG_OK; +error: + return ret; +} + +enum lttng_error_code map_kernel_enable(struct ltt_kernel_session *ksess, + struct ltt_kernel_map *kmap) +{ + enum lttng_error_code ret = LTTNG_OK; + const char *map_name; + enum lttng_map_status map_status; + + + assert(ksess); + assert(kmap); + + map_status = lttng_map_get_name(kmap->map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting kernel map name"); + ret = -1; + goto end; + } + + /* If already enabled, everything is OK */ + if (kmap->enabled) { + DBG3("Map %s already enabled. Skipping", map_name); + ret = LTTNG_ERR_UST_MAP_EXIST; + goto end; + } else { + kmap->enabled = 1; + lttng_map_set_is_enabled(kmap->map, true); + DBG2("Map %s enabled successfully", map_name); + } + + DBG2("Map %s being enabled in kernel domain", map_name); + + /* + * Enable map for UST global domain on all applications. Ignore return + * value here since whatever error we got, it means that the map was + * not created on one or many registered applications and we can not report + * this to the user yet. However, at this stage, the map was + * successfully created on the session daemon side so the enable-map + * command is a success. + */ + + ret = kernctl_enable(kmap->fd); + if (ret < 0) { + PERROR("Enable kernel map"); + } + + ret = LTTNG_OK; +end: + return ret; +} + +enum lttng_error_code map_kernel_disable(struct ltt_kernel_session *usess, + struct ltt_kernel_map *kmap) +{ + enum lttng_error_code ret = LTTNG_OK; + enum lttng_map_status map_status; + const char *map_name = NULL; + + assert(usess); + assert(kmap); + + map_status = lttng_map_get_name(kmap->map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting kernel map name"); + ret = -1; + goto end; + } + + /* Already disabled */ + if (kmap->enabled == 0) { + DBG2("Map kernel %s already disabled", map_name); + ret = LTTNG_ERR_KERNEL_MAP_EXIST; + goto end; + } + + kmap->enabled = 0; + lttng_map_set_is_enabled(kmap->map, false); + + DBG2("Map %s being disabled in kernel global domain", map_name); + + /* Disable map for global domain */ + ret = kernctl_disable(kmap->fd); + if (ret < 0) { + ret = LTTNG_ERR_KERNEL_MAP_DISABLE_FAIL; + goto error; + } + + + DBG2("Map %s disabled successfully", map_name); + + return LTTNG_OK; + +end: +error: + return ret; +} + +int map_ust_add(struct ltt_ust_session *usession, struct lttng_map *map) +{ + int ret = 0; + struct ltt_ust_map *umap; + enum lttng_map_status map_status; + const char *map_name; + enum lttng_buffer_type buffer_type; + + assert(lttng_map_get_domain(map) == LTTNG_DOMAIN_UST); + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Can't get map name"); + ret = -1; + goto end; + } + + umap = trace_ust_find_map_by_name(usession->domain_global.maps, + map_name); + if (umap) { + DBG("UST map named \"%s\" already present", map_name); + ret = -1; + goto end; + } + + buffer_type = lttng_map_get_buffer_type(map); + + umap = trace_ust_create_map(map); + assert(umap); + + umap->enabled = 1; + umap->id = trace_ust_get_next_chan_id(usession); + umap->map = map; + lttng_map_get(map); + + lttng_map_set_is_enabled(umap->map, true); + + DBG2("Map %s is being created for UST with buffer type %d and id %" PRIu64, + umap->name, buffer_type, umap->id); + + /* Flag session buffer type. */ + if (!usession->buffer_type_changed) { + usession->buffer_type = buffer_type; + usession->buffer_type_changed = 1; + } else if (usession->buffer_type != buffer_type) { + /* Buffer type was already set. Refuse to create channel. */ + ret = LTTNG_ERR_BUFFER_TYPE_MISMATCH; + goto error_free_map; + } + + rcu_read_lock(); + + /* Adding the map to the map hash table. */ + lttng_ht_add_unique_str(usession->domain_global.maps, &umap->node); + + rcu_read_unlock(); + + DBG2("Map %s created successfully", umap->name); + + ret = 0; + goto end; + +error_free_map: + trace_ust_destroy_map(umap); +end: + return ret; +} + +/* + * Enable UST map for session and domain. + */ +int map_ust_enable(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + int ret = LTTNG_OK; + + assert(usess); + assert(umap); + + /* If already enabled, everything is OK */ + if (umap->enabled) { + DBG3("Map %s already enabled. Skipping", umap->name); + ret = LTTNG_ERR_UST_MAP_EXIST; + goto end; + } else { + umap->enabled = 1; + lttng_map_set_is_enabled(umap->map, true); + DBG2("Map %s enabled successfully", umap->name); + } + + if (!usess->active) { + /* + * The map will be activated against the apps + * when the session is started as part of the + * application map "synchronize" operation. + */ + goto end; + } + + DBG2("Map %s being enabled in UST domain", umap->name); + + /* + * Enable map for UST global domain on all applications. Ignore return + * value here since whatever error we got, it means that the map was + * not created on one or many registered applications and we can not report + * this to the user yet. However, at this stage, the map was + * successfully created on the session daemon side so the enable-map + * command is a success. + */ + (void) ust_app_enable_map_glb(usess, umap); + + +end: + return ret; +} + +int map_ust_disable(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + int ret = LTTNG_OK; + + assert(usess); + assert(umap); + + /* Already disabled */ + if (umap->enabled == 0) { + DBG2("Map UST %s already disabled", umap->name); + ret = LTTNG_ERR_UST_MAP_EXIST; + goto end; + } + + umap->enabled = 0; + lttng_map_set_is_enabled(umap->map, false); + + /* + * If session is inactive we don't notify the tracer right away. We + * wait for the next synchronization. + */ + if (!usess->active) { + goto end; + } + + DBG2("Map %s being disabled in UST global domain", umap->name); + + /* Disable map for global domain */ + ret = ust_app_disable_map_glb(usess, umap); + if (ret < 0 && ret != -LTTNG_UST_ERR_EXIST) { + ret = LTTNG_ERR_UST_MAP_DISABLE_FAIL; + goto error; + } + + + DBG2("Map %s disabled successfully", umap->name); + + return LTTNG_OK; + +end: +error: + return ret; +} + +void map_add_or_increment_map_values(struct lttng_ht *map_values, const char *key, + int64_t value, bool has_underflowed, bool has_overflowed) +{ + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_node_str *node; + struct lttng_ht_iter ht_iter; + + lttng_ht_lookup(map_values, (void *) key, &ht_iter); + node = lttng_ht_iter_get_node_str(&ht_iter); + if (node == NULL) { + /* + * If the key is absent, the key value mapping. + */ + kv_entry = zmalloc(sizeof(*kv_entry)); + if (!kv_entry) { + abort(); + } + + kv_entry->key = strdup(key); + kv_entry->value = value; + kv_entry->has_underflowed = has_underflowed; + kv_entry->has_overflowed = has_overflowed; + + lttng_ht_node_init_str(&kv_entry->node, (char *) kv_entry->key); + lttng_ht_add_unique_str(map_values, &kv_entry->node); + + } else { + /* + * If the key is already present, increment the current value with the + * new value. + */ + kv_entry = caa_container_of(node, typeof(*kv_entry), node); + kv_entry->value += value; + kv_entry->has_underflowed |= has_underflowed; + kv_entry->has_overflowed |= has_overflowed; + } +} + +int map_new_content_section(struct lttng_map_content *map_content, + enum lttng_map_key_value_pair_list_type list_type, + bool summed_all_cpus, unsigned int identifier, + int cpu, struct lttng_ht *values) +{ + int ret; + struct lttng_map_key_value_pair_list *kv_pair_list; + enum lttng_map_status map_status; + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_iter key_iter; + + kv_pair_list = lttng_map_key_value_pair_list_create(list_type, + summed_all_cpus); + switch (list_type) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + map_status = lttng_map_key_value_pair_list_set_identifier( + kv_pair_list, identifier); + assert(map_status == LTTNG_MAP_STATUS_OK); + break; + default: + break; + } + + if (!summed_all_cpus) { + map_status = lttng_map_key_value_pair_list_set_cpu(kv_pair_list, + cpu); + } + + cds_lfht_for_each_entry(values->ht, &key_iter.iter, kv_entry, node.node) { + struct lttng_ht_iter entry_iter; + + struct lttng_map_key_value_pair *pair = + lttng_map_key_value_pair_create(kv_entry->key, + kv_entry->value); + if (kv_entry->has_overflowed) { + lttng_map_key_value_pair_set_has_overflowed(pair); + } + + if (kv_entry->has_underflowed) { + lttng_map_key_value_pair_set_has_underflowed(pair); + } + + map_status = lttng_map_key_value_pair_list_append_key_value( + kv_pair_list, pair); + + entry_iter.iter.node = &kv_entry->node.node; + lttng_ht_del(values, &entry_iter); + + free(kv_entry->key); + free(kv_entry); + } + + map_status = lttng_map_content_append_key_value_list(map_content, + kv_pair_list); + if (map_status != LTTNG_MAP_STATUS_OK) { + lttng_map_key_value_pair_list_destroy(kv_pair_list); + ret = -1; + ERR("Error appending key-value pair list to map content object"); + goto end; + } + ret = 0; +end: + return ret; +} + diff --git a/src/bin/lttng-sessiond/map.h b/src/bin/lttng-sessiond/map.h new file mode 100644 index 000000000..1c84fe667 --- /dev/null +++ b/src/bin/lttng-sessiond/map.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ +#ifndef _LTT_MAP_H +#define _LTT_MAP_H + +#include +#include +#include "trace-kernel.h" +#include "trace-ust.h" + +struct map_kv_ht_entry { + struct lttng_ht_node_str node; + char *key; + int64_t value; + bool has_overflowed; + bool has_underflowed; +}; + +enum lttng_error_code map_kernel_add(struct ltt_kernel_session *ksession, + struct lttng_map *map); +enum lttng_error_code map_kernel_enable(struct ltt_kernel_session *ksession, + struct ltt_kernel_map *kmap); +enum lttng_error_code map_kernel_disable(struct ltt_kernel_session *ksession, + struct ltt_kernel_map *kmap); + +int map_ust_add(struct ltt_ust_session *usession, + struct lttng_map *map); +int map_ust_enable(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); +int map_ust_disable(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); + +void map_add_or_increment_map_values(struct lttng_ht *map_values, const char *key, + int64_t value, bool has_underflowed, bool has_overflowed); + +int map_new_content_section(struct lttng_map_content *map_content, + enum lttng_map_key_value_pair_list_type list_type, + bool summed_all_cpus, unsigned int identifier, + int cpu, struct lttng_ht *values); + +#endif /* _LTT_MAP_H */ diff --git a/src/bin/lttng-sessiond/modprobe.c b/src/bin/lttng-sessiond/modprobe.c index 28385ea39..8d162857f 100644 --- a/src/bin/lttng-sessiond/modprobe.c +++ b/src/bin/lttng-sessiond/modprobe.c @@ -56,6 +56,14 @@ struct kern_modules_param kern_modules_control_core[] = { .name = (char *) "lttng-ring-buffer-event_notifier-client", .load_policy = KERNEL_MODULE_PROPERTY_LOAD_POLICY_OPTIONAL, }, + { + .name = (char *) "lttng-counter-client-percpu-64-modular", + .load_policy = KERNEL_MODULE_PROPERTY_LOAD_POLICY_OPTIONAL, + }, + { + .name = (char *) "lttng-counter-client-percpu-32-modular", + .load_policy = KERNEL_MODULE_PROPERTY_LOAD_POLICY_OPTIONAL, + }, }; /* LTTng kerneltracer probe modules list */ @@ -506,14 +514,23 @@ static void modprobe_remove_lttng(const struct kern_modules_param *modules, modprobe[sizeof(modprobe) - 1] = '\0'; ret = system(modprobe); if (ret == -1) { - ERR("Unable to launch modprobe -r for module %s", - modules[i].name); - } else if (modules[i].load_policy == KERNEL_MODULE_PROPERTY_LOAD_POLICY_REQUIRED && WEXITSTATUS(ret) != 0) { - ERR("Unable to remove module %s", - modules[i].name); + if (modules[i].load_policy == KERNEL_MODULE_PROPERTY_LOAD_POLICY_REQUIRED) { + ERR("Unable to launch modprobe -r for required module %s", + modules[i].name); + } else { + DBG("Unable to launch modprobe -r for optional module %s", + modules[i].name); + } + } else if (WEXITSTATUS(ret) != 0) { + if (modules[i].load_policy == KERNEL_MODULE_PROPERTY_LOAD_POLICY_REQUIRED) { + ERR("Unable to remove required module %s", + modules[i].name); + } else { + DBG("Unable to remove optional module %s", + modules[i].name); + } } else { - DBG("Modprobe removal successful %s", - modules[i].name); + DBG("Modprobe removal successful %s", modules[i].name); } } } diff --git a/src/bin/lttng-sessiond/notification-thread-commands.c b/src/bin/lttng-sessiond/notification-thread-commands.c index 44bee3d3b..bc8db39d7 100644 --- a/src/bin/lttng-sessiond/notification-thread-commands.c +++ b/src/bin/lttng-sessiond/notification-thread-commands.c @@ -382,17 +382,48 @@ int notification_thread_client_communication_update( return run_command_no_wait(handle, &cmd); } +enum lttng_error_code notification_thread_command_get_trigger( + struct notification_thread_handle *handle, + const struct lttng_trigger *trigger, + struct lttng_trigger **real_trigger) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_GET_TRIGGER; + cmd.parameters.get_trigger.trigger= trigger; + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + + ret_code = cmd.reply_code; + *real_trigger = cmd.reply.get_trigger.trigger; + +end: + return ret_code; +} + +/* + * Takes ownership of the payload if present. + */ LTTNG_HIDDEN -struct lttng_event_notifier_notification * -lttng_event_notifier_notification_create(uint64_t tracer_token, - enum lttng_domain_type domain) +struct lttng_event_notifier_notification *lttng_event_notifier_notification_create( + uint64_t tracer_token, + enum lttng_domain_type domain, + char *payload, + size_t payload_size) { struct lttng_event_notifier_notification *notification = NULL; assert(domain != LTTNG_DOMAIN_NONE); + assert((payload && payload_size) || (!payload && !payload_size)); - notification = zmalloc( - sizeof(struct lttng_event_notifier_notification)); + notification = zmalloc(sizeof(struct lttng_event_notifier_notification)); if (notification == NULL) { ERR("[notification-thread] Error allocating notification"); goto end; @@ -400,6 +431,8 @@ lttng_event_notifier_notification_create(uint64_t tracer_token, notification->tracer_token = tracer_token; notification->type = domain; + notification->capture_buffer = payload; + notification->capture_buf_size = payload_size; end: return notification; @@ -413,5 +446,6 @@ void lttng_event_notifier_notification_destroy( return; } + free(notification->capture_buffer); free(notification); } diff --git a/src/bin/lttng-sessiond/notification-thread-commands.h b/src/bin/lttng-sessiond/notification-thread-commands.h index 50751a94d..882959a29 100644 --- a/src/bin/lttng-sessiond/notification-thread-commands.h +++ b/src/bin/lttng-sessiond/notification-thread-commands.h @@ -32,6 +32,7 @@ enum notification_thread_command_type { NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS, NOTIFICATION_COMMAND_TYPE_QUIT, NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE, + NOTIFICATION_COMMAND_TYPE_GET_TRIGGER, }; struct notification_thread_command { @@ -89,12 +90,19 @@ struct notification_thread_command { enum client_transmission_status status; } client_communication_update; + struct { + const struct lttng_trigger *trigger; + } get_trigger; + } parameters; union { struct { struct lttng_triggers *triggers; } list_triggers; + struct { + struct lttng_trigger *trigger; + } get_trigger; } reply; /* lttng_waiter on which to wait for command reply (optional). */ struct lttng_waiter reply_waiter; @@ -166,4 +174,9 @@ enum lttng_error_code notification_thread_command_remove_tracer_event_source( void notification_thread_command_quit( struct notification_thread_handle *handle); +enum lttng_error_code notification_thread_command_get_trigger( + struct notification_thread_handle *handle, + const struct lttng_trigger *trigger, + struct lttng_trigger **real_trigger); + #endif /* NOTIFICATION_THREAD_COMMANDS_H */ diff --git a/src/bin/lttng-sessiond/notification-thread-events.c b/src/bin/lttng-sessiond/notification-thread-events.c index 747902612..0498aac35 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.c +++ b/src/bin/lttng-sessiond/notification-thread-events.c @@ -21,12 +21,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -39,15 +42,20 @@ #include #include "condition-internal.h" +#include "event-notifier-error-accounting.h" #include "notification-thread.h" #include "notification-thread-events.h" #include "notification-thread-commands.h" #include "lttng-sessiond.h" #include "kernel.h" +#include "event.h" #define CLIENT_POLL_MASK_IN (LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP) #define CLIENT_POLL_MASK_IN_OUT (CLIENT_POLL_MASK_IN | LPOLLOUT) +/* The tracers currently limit the capture size to PIPE_BUF (4kb on linux). */ +#define MAX_CAPTURE_SIZE (PIPE_BUF) + enum lttng_object_type { LTTNG_OBJECT_TYPE_UNKNOWN, LTTNG_OBJECT_TYPE_NONE, @@ -116,6 +124,7 @@ struct lttng_trigger_ht_element { struct lttng_trigger *trigger; struct cds_lfht_node node; struct cds_lfht_node node_by_name_uid; + struct cds_list_head client_list_trigger_node; /* call_rcu delayed reclaim. */ struct rcu_head rcu_node; }; @@ -308,7 +317,7 @@ int match_client_list_condition(struct cds_lfht_node *node, const void *key) client_list = caa_container_of(node, struct notification_client_list, notification_trigger_clients_ht_node); - condition = lttng_trigger_get_const_condition(client_list->trigger); + condition = client_list->condition; return !!lttng_condition_is_equal(condition_key, condition); } @@ -346,6 +355,8 @@ const char *notification_command_type_str( return "REMOVE_TRACER_EVENT_SOURCE"; case NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS: return "LIST_TRIGGERS"; + case NOTIFICATION_COMMAND_TYPE_GET_TRIGGER: + return "GET_TRIGGER"; case NOTIFICATION_COMMAND_TYPE_QUIT: return "QUIT"; case NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE: @@ -462,7 +473,7 @@ enum lttng_object_type get_condition_binding_object( case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: return LTTNG_OBJECT_TYPE_SESSION; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: return LTTNG_OBJECT_TYPE_NONE; default: return LTTNG_OBJECT_TYPE_UNKNOWN; @@ -655,62 +666,119 @@ void notification_client_list_release(struct urcu_ref *list_ref) container_of(list_ref, typeof(*list), ref); struct notification_client_list_element *client_list_element, *tmp; + lttng_condition_put(list->condition); + if (list->notification_trigger_clients_ht) { rcu_read_lock(); + cds_lfht_del(list->notification_trigger_clients_ht, &list->notification_trigger_clients_ht_node); rcu_read_unlock(); list->notification_trigger_clients_ht = NULL; } cds_list_for_each_entry_safe(client_list_element, tmp, - &list->list, node) { + &list->clients_list, node) { free(client_list_element); } + + assert(cds_list_empty(&list->triggers_list)); + pthread_mutex_destroy(&list->lock); call_rcu(&list->rcu_node, free_notification_client_list_rcu); } +static +bool condition_applies_to_client(const struct lttng_condition *condition, + struct notification_client *client) +{ + bool applies = false; + struct lttng_condition_list_element *condition_list_element; + + cds_list_for_each_entry(condition_list_element, &client->condition_list, + node) { + applies = lttng_condition_is_equal( + condition_list_element->condition, + condition); + if (applies) { + break; + } + } + return applies; +} + static struct notification_client_list *notification_client_list_create( - const struct lttng_trigger *trigger) + struct notification_thread_state *state, + const struct lttng_condition *condition) { - struct notification_client_list *client_list = - zmalloc(sizeof(*client_list)); + struct notification_client *client; + struct cds_lfht_iter iter; + struct notification_client_list *client_list; + client_list = zmalloc(sizeof(*client_list)); if (!client_list) { - goto error; + goto end; } + pthread_mutex_init(&client_list->lock, NULL); + /* + * The trigger that owns the condition has the first reference to this + * client list. + */ urcu_ref_init(&client_list->ref); cds_lfht_node_init(&client_list->notification_trigger_clients_ht_node); - CDS_INIT_LIST_HEAD(&client_list->list); - client_list->trigger = trigger; -error: - return client_list; -} + CDS_INIT_LIST_HEAD(&client_list->clients_list); + CDS_INIT_LIST_HEAD(&client_list->triggers_list); -static -void publish_notification_client_list( - struct notification_thread_state *state, - struct notification_client_list *list) -{ - const struct lttng_condition *condition = - lttng_trigger_get_const_condition(list->trigger); + /* + * Create a copy of the condition so that it's independent of any + * trigger. The client list may outlive the trigger object (which owns + * the condition) that is used to create it. + */ + client_list->condition = lttng_condition_copy(condition); + + /* Build a list of clients to which this new condition applies. */ + cds_lfht_for_each_entry (state->client_socket_ht, &iter, client, + client_socket_ht_node) { + struct notification_client_list_element *client_list_element; - assert(!list->notification_trigger_clients_ht); - notification_client_list_get(list); + if (!condition_applies_to_client(condition, client)) { + continue; + } - list->notification_trigger_clients_ht = + client_list_element = zmalloc(sizeof(*client_list_element)); + if (!client_list_element) { + goto error_put_client_list; + } + + CDS_INIT_LIST_HEAD(&client_list_element->node); + client_list_element->client = client; + cds_list_add(&client_list_element->node, &client_list->clients_list); + } + + client_list->notification_trigger_clients_ht = state->notification_trigger_clients_ht; rcu_read_lock(); - cds_lfht_add(state->notification_trigger_clients_ht, - lttng_condition_hash(condition), - &list->notification_trigger_clients_ht_node); + /* + * Add the client list to the global list of client list. + */ + cds_lfht_add_unique(state->notification_trigger_clients_ht, + lttng_condition_hash(client_list->condition), + match_client_list_condition, + client_list->condition, + &client_list->notification_trigger_clients_ht_node); rcu_read_unlock(); + goto end; + +error_put_client_list: + notification_client_list_put(client_list); + client_list = NULL; + +end: + return client_list; } -LTTNG_HIDDEN void notification_client_list_put(struct notification_client_list *list) { if (!list) { @@ -998,12 +1066,11 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger, * subscribing. */ cds_lfht_node_init(&client_list.notification_trigger_clients_ht_node); - CDS_INIT_LIST_HEAD(&client_list.list); - client_list.trigger = trigger; + CDS_INIT_LIST_HEAD(&client_list.clients_list); CDS_INIT_LIST_HEAD(&client_list_element.node); client_list_element.client = client; - cds_list_add(&client_list_element.node, &client_list.list); + cds_list_add(&client_list_element.node, &client_list.clients_list); /* Send evaluation result to the newly-subscribed client. */ DBG("[notification-thread] Newly subscribed-to condition evaluated to true, notifying client"); @@ -1077,13 +1144,19 @@ int notification_thread_client_subscribe(struct notification_client *client, * This is correct since the list doesn't own the trigger and the * object is immutable. */ - if (evaluate_condition_for_client(client_list->trigger, condition, - client, state)) { - WARN("[notification-thread] Evaluation of a condition on client subscription failed, aborting."); - ret = -1; - free(client_list_element); - goto end; + struct lttng_trigger_ht_element *trigger_ht_element; + pthread_mutex_lock(&client_list->lock); + cds_list_for_each_entry(trigger_ht_element, + &client_list->triggers_list, client_list_trigger_node) { + if (evaluate_condition_for_client(trigger_ht_element->trigger, condition, + client, state)) { + WARN("[notification-thread] Evaluation of a condition on client subscription failed, aborting."); + ret = -1; + free(client_list_element); + goto end; + } } + pthread_mutex_unlock(&client_list->lock); /* * Add the client to the list of clients interested in a given trigger @@ -1094,7 +1167,7 @@ int notification_thread_client_subscribe(struct notification_client *client, CDS_INIT_LIST_HEAD(&client_list_element->node); pthread_mutex_lock(&client_list->lock); - cds_list_add(&client_list_element->node, &client_list->list); + cds_list_add(&client_list_element->node, &client_list->clients_list); pthread_mutex_unlock(&client_list->lock); end: if (_status) { @@ -1165,7 +1238,7 @@ int notification_thread_client_unsubscribe( pthread_mutex_lock(&client_list->lock); cds_list_for_each_entry_safe(client_list_element, client_tmp, - &client_list->list, node) { + &client_list->clients_list, node) { if (client_list_element->client->id != client->id) { continue; } @@ -1358,25 +1431,6 @@ fail: return false; } -static -bool trigger_applies_to_client(struct lttng_trigger *trigger, - struct notification_client *client) -{ - bool applies = false; - struct lttng_condition_list_element *condition_list_element; - - cds_list_for_each_entry(condition_list_element, &client->condition_list, - node) { - applies = lttng_condition_is_equal( - condition_list_element->condition, - lttng_trigger_get_condition(trigger)); - if (applies) { - break; - } - } - return applies; -} - /* Must be called with RCU read lock held. */ static struct lttng_session_trigger_list *get_session_trigger_list( @@ -1718,7 +1772,6 @@ error: session_info_put(session_info); return 1; } - static void free_channel_trigger_list_rcu(struct rcu_head *node) { @@ -2118,6 +2171,29 @@ end: return ret; } +static +int condition_on_event_update_error_count(struct lttng_trigger *trigger) +{ + int ret = 0; + uint64_t error_count = 0; + struct lttng_condition *condition; + enum event_notifier_error_accounting_status status; + + condition = lttng_trigger_get_condition(trigger); + + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); + + status = event_notifier_error_accounting_get_count(trigger, &error_count); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting event notifier error count."); + ret = -1; + } + + lttng_condition_on_event_set_error_count(condition, error_count); + + return ret; +} + int handle_notification_thread_remove_tracer_event_source_no_result( struct notification_thread_state *state, int tracer_event_source_fd) @@ -2165,6 +2241,12 @@ static int handle_notification_thread_command_list_triggers( continue; } + if (lttng_trigger_needs_tracer_notifier(trigger_ht_element->trigger)) { + ret = condition_on_event_update_error_count( + trigger_ht_element->trigger); + assert(!ret); + } + ret = lttng_triggers_add(local_triggers, trigger_ht_element->trigger); if (ret < 0) { @@ -2186,6 +2268,42 @@ end: return ret; } +static +int handle_notification_thread_command_get_trigger( + struct notification_thread_state *state, + const struct lttng_trigger *trigger, + struct lttng_trigger **real_trigger, + enum lttng_error_code *_cmd_result) +{ + int ret = -1; + struct cds_lfht_iter iter; + struct lttng_trigger_ht_element *trigger_ht_element; + enum lttng_error_code cmd_result = LTTNG_ERR_TRIGGER_NOT_FOUND; + + rcu_read_lock(); + + cds_lfht_for_each_entry(state->triggers_ht, &iter, + trigger_ht_element, node) { + + if (lttng_trigger_is_equal(trigger, trigger_ht_element->trigger)) { + /* + * Take one reference on the return trigger. + */ + *real_trigger = trigger_ht_element->trigger; + lttng_trigger_get(*real_trigger); + ret = 0; + cmd_result = LTTNG_OK; + goto end; + } + } + + +end: + rcu_read_unlock(); + *_cmd_result = cmd_result; + return ret; +} + static bool condition_is_supported(struct lttng_condition *condition) { @@ -2217,12 +2335,12 @@ bool condition_is_supported(struct lttng_condition *condition) is_supported = kernel_supports_ring_buffer_snapshot_sample_positions() == 1; break; } - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: { const struct lttng_event_rule *event_rule; enum lttng_domain_type domain; const enum lttng_condition_status status = - lttng_condition_event_rule_get_rule( + lttng_condition_on_event_get_rule( condition, &event_rule); assert(status == LTTNG_CONDITION_STATUS_OK); @@ -2424,6 +2542,114 @@ enum lttng_error_code generate_trigger_name( return ret_code; } +static inline +void notif_thread_state_remove_trigger_ht_elem( + struct notification_thread_state *state, + struct lttng_trigger_ht_element *trigger_ht_element) +{ + assert(state); + assert(trigger_ht_element); + + cds_lfht_del(state->triggers_ht, &trigger_ht_element->node); + cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid); +} + +static +enum lttng_error_code setup_tracer_notifier( + struct notification_thread_state *state, + struct lttng_trigger *trigger) +{ + enum lttng_error_code ret; + enum event_notifier_error_accounting_status error_accounting_status; + struct cds_lfht_node *node; + uint64_t error_counter_index = 0; + struct lttng_condition *condition = lttng_trigger_get_condition(trigger); + struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL; + + trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element)); + if (!trigger_tokens_ht_element) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Add trigger token to the trigger_tokens_ht. */ + cds_lfht_node_init(&trigger_tokens_ht_element->node); + trigger_tokens_ht_element->token = LTTNG_OPTIONAL_GET(trigger->tracer_token); + trigger_tokens_ht_element->trigger = trigger; + + node = cds_lfht_add_unique(state->trigger_tokens_ht, + hash_key_u64(&trigger_tokens_ht_element->token, lttng_ht_seed), + match_trigger_token, + &trigger_tokens_ht_element->token, + &trigger_tokens_ht_element->node); + if (node != &trigger_tokens_ht_element->node) { + ret = LTTNG_ERR_TRIGGER_EXISTS; + goto error_free_ht_element; + } + + error_accounting_status = event_notifier_error_accounting_register_event_notifier( + trigger, &error_counter_index); + if (error_accounting_status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + if (error_accounting_status == EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE) { + DBG("Trigger group error accounting counter full."); + ret = LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL; + } else { + ERR("Error registering trigger for error accounting"); + ret = LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION; + } + + goto error_remove_ht_element; + } + + lttng_condition_on_event_set_error_counter_index( + condition, error_counter_index); + + ret = LTTNG_OK; + goto end; + +error_remove_ht_element: + cds_lfht_del(state->trigger_tokens_ht, &trigger_tokens_ht_element->node); +error_free_ht_element: + free(trigger_tokens_ht_element); +end: + return ret; +} + +static +void set_action_incr_value_tracer_token(struct notification_thread_state *state, + struct lttng_action *action) +{ + lttng_action_incr_value_set_tracer_token(action, + state->trigger_id.next_tracer_token++); +} + +static +void set_all_action_incr_value_tracer_token(struct notification_thread_state *state, + struct lttng_action *action) +{ + unsigned int i, count; + enum lttng_action_status action_status; + enum lttng_action_type action_type; + action_type = lttng_action_get_type(action); + + if (action_type != LTTNG_ACTION_TYPE_GROUP) { + if (action_type == LTTNG_ACTION_TYPE_INCREMENT_VALUE) { + set_action_incr_value_tracer_token(state, action); + } + } else { + action_status = lttng_action_group_get_count(action, &count); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + for (i = 0; i < count; i++) { + struct lttng_action *inner_action = + lttng_action_group_get_mutable_at_index( + action, i); + set_all_action_incr_value_tracer_token(state, + inner_action); + } + } +} + /* * FIXME A client's credentials are not checked when registering a trigger. * @@ -2447,13 +2673,9 @@ int handle_notification_thread_command_register_trigger( { int ret = 0; struct lttng_condition *condition; - struct notification_client *client; struct notification_client_list *client_list = NULL; struct lttng_trigger_ht_element *trigger_ht_element = NULL; - struct notification_client_list_element *client_list_element; - struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL; struct cds_lfht_node *node; - struct cds_lfht_iter iter; const char* trigger_name; bool free_trigger = true; struct lttng_evaluation *evaluation = NULL; @@ -2469,6 +2691,8 @@ int handle_notification_thread_command_register_trigger( /* Set the trigger's tracer token. */ lttng_trigger_set_tracer_token(trigger, trigger_tracer_token); + set_all_action_incr_value_tracer_token(state, lttng_trigger_get_action(trigger)); + if (lttng_trigger_get_name(trigger, &trigger_name) == LTTNG_TRIGGER_STATUS_UNSET) { const enum lttng_error_code ret_code = generate_trigger_name( @@ -2530,95 +2754,72 @@ int handle_notification_thread_command_register_trigger( goto error_free_ht_element; } - if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { - trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element)); - if (!trigger_tokens_ht_element) { - /* Fatal error. */ - ret = -1; - cds_lfht_del(state->triggers_ht, - &trigger_ht_element->node); - cds_lfht_del(state->triggers_by_name_uid_ht, - &trigger_ht_element->node_by_name_uid); - goto error_free_ht_element; - } + /* + * Some triggers might need a tracer notifier depending on its + * condition and actions. + */ + if (lttng_trigger_needs_tracer_notifier(trigger)) { + enum lttng_error_code error_code; + + error_code = setup_tracer_notifier(state, trigger); + if (error_code != LTTNG_OK) { + notif_thread_state_remove_trigger_ht_elem(state, + trigger_ht_element); + if (error_code == LTTNG_ERR_NOMEM) { + ret = -1; + } else { + *cmd_result = error_code; + ret = 0; + } - /* Add trigger token to the trigger_tokens_ht. */ - cds_lfht_node_init(&trigger_tokens_ht_element->node); - trigger_tokens_ht_element->token = - LTTNG_OPTIONAL_GET(trigger->tracer_token); - trigger_tokens_ht_element->trigger = trigger; - - node = cds_lfht_add_unique(state->trigger_tokens_ht, - hash_key_u64(&trigger_tokens_ht_element->token, - lttng_ht_seed), - match_trigger_token, - &trigger_tokens_ht_element->token, - &trigger_tokens_ht_element->node); - if (node != &trigger_tokens_ht_element->node) { - /* Internal corruption, fatal error. */ - ret = -1; - *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; - cds_lfht_del(state->triggers_ht, - &trigger_ht_element->node); - cds_lfht_del(state->triggers_by_name_uid_ht, - &trigger_ht_element->node_by_name_uid); goto error_free_ht_element; } } - /* - * Ownership of the trigger and of its wrapper was transfered to - * the triggers_ht. Same for token ht element if necessary. - */ - trigger_tokens_ht_element = NULL; - trigger_ht_element = NULL; - free_trigger = false; - /* * The rest only applies to triggers that have a "notify" action. * It is not skipped as this is the only action type currently * supported. */ if (is_trigger_action_notify(trigger)) { - client_list = notification_client_list_create(trigger); + /* + * Find or create the client list of this condition. It may + * already be present if another trigger is already registered + * with the same condition. + */ + client_list = get_client_list_from_condition(state, condition); if (!client_list) { - ret = -1; - goto error_free_ht_element; - } - - /* Build a list of clients to which this new trigger applies. */ - cds_lfht_for_each_entry (state->client_socket_ht, &iter, client, - client_socket_ht_node) { - if (!trigger_applies_to_client(trigger, client)) { - continue; - } - - client_list_element = - zmalloc(sizeof(*client_list_element)); - if (!client_list_element) { - ret = -1; - goto error_put_client_list; + /* + * No client list for this condition yet. We create new + * one and build it up. + */ + client_list = notification_client_list_create(state, condition); + if (!client_list) { + ERR("Error creating notification client list for trigger %s", trigger->name); + goto error_free_ht_element; } - - CDS_INIT_LIST_HEAD(&client_list_element->node); - client_list_element->client = client; - cds_list_add(&client_list_element->node, - &client_list->list); } - /* - * Client list ownership transferred to the - * notification_trigger_clients_ht. - */ - publish_notification_client_list(state, client_list); + CDS_INIT_LIST_HEAD(&trigger_ht_element->client_list_trigger_node); + + pthread_mutex_lock(&client_list->lock); + cds_list_add(&trigger_ht_element->client_list_trigger_node, &client_list->triggers_list); + pthread_mutex_unlock(&client_list->lock); } + /* + * Ownership of the trigger and of its wrapper was transfered to + * the triggers_ht. Same for token ht element if necessary. + */ + trigger_ht_element = NULL; + free_trigger = false; + switch (get_condition_binding_object(condition)) { case LTTNG_OBJECT_TYPE_SESSION: /* Add the trigger to the list if it matches a known session. */ ret = bind_trigger_to_matching_session(trigger, state); if (ret) { - goto error_put_client_list; + goto error_free_ht_element; } break; case LTTNG_OBJECT_TYPE_CHANNEL: @@ -2628,7 +2829,7 @@ int handle_notification_thread_command_register_trigger( */ ret = bind_trigger_to_matching_channels(trigger, state); if (ret) { - goto error_put_client_list; + goto error_free_ht_element; } break; case LTTNG_OBJECT_TYPE_NONE: @@ -2636,7 +2837,7 @@ int handle_notification_thread_command_register_trigger( default: ERR("Unknown object type on which to bind a newly registered trigger was encountered"); ret = -1; - goto error_put_client_list; + goto error_free_ht_element; } /* @@ -2694,7 +2895,7 @@ int handle_notification_thread_command_register_trigger( if (ret) { /* Fatal error. */ - goto error_put_client_list; + goto error_free_ht_element; } DBG("Newly registered trigger's condition evaluated to %s", @@ -2702,7 +2903,7 @@ int handle_notification_thread_command_register_trigger( if (!evaluation) { /* Evaluation yielded nothing. Normal exit. */ ret = 0; - goto end; + goto success; } /* @@ -2723,7 +2924,7 @@ int handle_notification_thread_command_register_trigger( */ ERR("Fatal error occurred while enqueuing action associated to newly registered trigger"); ret = -1; - goto error_put_client_list; + goto error_free_ht_element; case ACTION_EXECUTOR_STATUS_OVERFLOW: /* * TODO Add trigger identification (name/id) when @@ -2733,18 +2934,16 @@ int handle_notification_thread_command_register_trigger( */ WARN("No space left when enqueuing action associated to newly registered trigger"); ret = 0; - goto end; + goto success; default: abort(); } -end: +success: *cmd_result = LTTNG_OK; DBG("Registered trigger: name = `%s`, tracer token = %" PRIu64, trigger_name, trigger_tracer_token); - -error_put_client_list: - notification_client_list_put(client_list); + goto end; error_free_ht_element: if (trigger_ht_element) { @@ -2752,12 +2951,11 @@ error_free_ht_element: call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu); } - - free(trigger_tokens_ht_element); error: if (free_trigger) { lttng_trigger_destroy(trigger); } +end: rcu_read_unlock(); return ret; } @@ -2776,6 +2974,36 @@ void free_notification_trigger_tokens_ht_element_rcu(struct rcu_head *node) rcu_node)); } +static +void teardown_tracer_notifier(struct notification_thread_state *state, + const struct lttng_trigger *trigger) +{ + struct cds_lfht_iter iter; + struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element; + + cds_lfht_for_each_entry(state->trigger_tokens_ht, &iter, + trigger_tokens_ht_element, node) { + + if (!lttng_trigger_is_equal(trigger, + trigger_tokens_ht_element->trigger)) { + continue; + } + + event_notifier_error_accounting_unregister_event_notifier( + trigger_tokens_ht_element->trigger); + + /* TODO talk to all app and remove it */ + DBG("[notification-thread] Removed trigger from tokens_ht"); + cds_lfht_del(state->trigger_tokens_ht, + &trigger_tokens_ht_element->node); + + call_rcu(&trigger_tokens_ht_element->rcu_node, + free_notification_trigger_tokens_ht_element_rcu); + + break; + } +} + static int handle_notification_thread_command_unregister_trigger( struct notification_thread_state *state, @@ -2824,28 +3052,13 @@ int handle_notification_thread_command_unregister_trigger( } } - if (lttng_condition_get_type(condition) == - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { - struct notification_trigger_tokens_ht_element - *trigger_tokens_ht_element; - - cds_lfht_for_each_entry (state->trigger_tokens_ht, &iter, - trigger_tokens_ht_element, node) { - if (!lttng_trigger_is_equal(trigger, - trigger_tokens_ht_element->trigger)) { - continue; - } - - DBG("[notification-thread] Removed trigger from tokens_ht"); - cds_lfht_del(state->trigger_tokens_ht, - &trigger_tokens_ht_element->node); - call_rcu(&trigger_tokens_ht_element->rcu_node, - free_notification_trigger_tokens_ht_element_rcu); - - break; - } + if (lttng_trigger_needs_tracer_notifier(trigger)) { + teardown_tracer_notifier(state, trigger); } + trigger_ht_element = caa_container_of(triggers_ht_node, + struct lttng_trigger_ht_element, node); + if (is_trigger_action_notify(trigger)) { /* * Remove and release the client list from @@ -2854,6 +3067,10 @@ int handle_notification_thread_command_unregister_trigger( client_list = get_client_list_from_condition(state, condition); assert(client_list); + pthread_mutex_lock(&client_list->lock); + cds_list_del(&trigger_ht_element->client_list_trigger_node); + pthread_mutex_unlock(&client_list->lock); + /* Put new reference and the hashtable's reference. */ notification_client_list_put(client_list); notification_client_list_put(client_list); @@ -2861,10 +3078,7 @@ int handle_notification_thread_command_unregister_trigger( } /* Remove trigger from triggers_ht. */ - trigger_ht_element = caa_container_of(triggers_ht_node, - struct lttng_trigger_ht_element, node); - cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid); - cds_lfht_del(state->triggers_ht, triggers_ht_node); + notif_thread_state_remove_trigger_ht_elem(state, trigger_ht_element); /* Release the ownership of the trigger. */ lttng_trigger_destroy(trigger_ht_element->trigger); @@ -2974,6 +3188,19 @@ int handle_notification_thread_command( cmd->reply_code = LTTNG_OK; ret = 1; goto end; + case NOTIFICATION_COMMAND_TYPE_GET_TRIGGER: + { + struct lttng_trigger *trigger = NULL; + + ret = handle_notification_thread_command_get_trigger( + state, + cmd->parameters.get_trigger.trigger, + &trigger, + &cmd->reply_code); + cmd->reply.get_trigger.trigger = trigger; + ret = 0; + break; + } case NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE: { const enum client_transmission_status client_status = @@ -4153,7 +4380,7 @@ int notification_client_list_send_evaluation( pthread_mutex_lock(&client_list->lock); cds_list_for_each_entry_safe(client_list_element, tmp, - &client_list->list, node) { + &client_list->clients_list, node) { enum client_transmission_status transmission_status; struct notification_client *client = client_list_element->client; @@ -4244,6 +4471,8 @@ struct lttng_event_notifier_notification *recv_one_event_notifier_notification( int ret; uint64_t token; struct lttng_event_notifier_notification *notification = NULL; + char *capture_buffer = NULL; + size_t capture_buffer_size; void *reception_buffer; size_t reception_size; @@ -4280,17 +4509,55 @@ struct lttng_event_notifier_notification *recv_one_event_notifier_notification( switch(domain) { case LTTNG_DOMAIN_UST: token = ust_notification.token; + capture_buffer_size = ust_notification.capture_buf_size; break; case LTTNG_DOMAIN_KERNEL: token = kernel_notification.token; + capture_buffer_size = kernel_notification.capture_buf_size; break; default: abort(); } - notification = lttng_event_notifier_notification_create( - token, domain); + if (capture_buffer_size == 0) { + capture_buffer = NULL; + goto skip_capture; + } + + if (capture_buffer_size > MAX_CAPTURE_SIZE) { + ERR("[notification-thread] Event notifier has a capture payload size which exceeds the maximum allowed size: capture_payload_size = %zu bytes, max allowed size = %d bytes", + capture_buffer_size, MAX_CAPTURE_SIZE); + goto end; + } + + capture_buffer = zmalloc(capture_buffer_size); + if (!capture_buffer) { + ERR("[notification-thread] Failed to allocate capture buffer"); + goto end; + } + + /* Fetch additional payload (capture). */ + ret = lttng_read(notification_pipe_read_fd, capture_buffer, capture_buffer_size); + if (ret != capture_buffer_size) { + ERR("[notification-thread] Failed to read from event source pipe (fd = %i)", + notification_pipe_read_fd); + goto end; + } + +skip_capture: + notification = lttng_event_notifier_notification_create(token, domain, + capture_buffer, capture_buffer_size); + if (notification == NULL) { + goto end; + } + + /* + * Ownership transfered to the lttng_event_notifier_notification object. + */ + capture_buffer = NULL; + end: + free(capture_buffer); return notification; } @@ -4307,6 +4574,7 @@ int dispatch_one_event_notifier_notification(struct notification_thread_state *s struct notification_client_list *client_list = NULL; const char *trigger_name; int ret; + unsigned int capture_count = 0; /* Find triggers associated with this token. */ rcu_read_lock(); @@ -4339,14 +4607,34 @@ int dispatch_one_event_notifier_notification(struct notification_thread_state *s trigger_status = lttng_trigger_get_name(element->trigger, &trigger_name); assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + if (lttng_condition_on_event_get_capture_descriptor_count( + lttng_trigger_get_const_condition(element->trigger), + &capture_count) != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to get capture count"); + ret = -1; + goto end; + } + + if (!notification->capture_buffer && capture_count != 0) { + ERR("Expected capture but capture buffer is null"); + ret = -1; + goto end; + } + evaluation = lttng_evaluation_event_rule_create( - trigger_name); + container_of(lttng_trigger_get_const_condition( + element->trigger), + struct lttng_condition_on_event, + parent), + trigger_name, + notification->capture_buffer, + notification->capture_buf_size, false); + if (evaluation == NULL) { ERR("[notification-thread] Failed to create event rule hit evaluation while creating and enqueuing action executor job"); ret = -1; goto end_unlock; } - client_list = get_client_list_from_condition(state, lttng_trigger_get_const_condition(element->trigger)); executor_status = action_executor_enqueue(state->executor, @@ -4374,7 +4662,7 @@ int dispatch_one_event_notifier_notification(struct notification_thread_state *s /* Warn clients that a notification (or more) was dropped. */ pthread_mutex_lock(&client_list->lock); cds_list_for_each_entry_safe(client_list_element, tmp, - &client_list->list, node) { + &client_list->clients_list, node) { enum client_transmission_status transmission_status; struct notification_client *client = client_list_element->client; @@ -4404,6 +4692,7 @@ next_client: pthread_mutex_unlock(&client_list->lock); break; } + case ACTION_EXECUTOR_STATUS_INVALID: case ACTION_EXECUTOR_STATUS_ERROR: /* Fatal error, shut down everything. */ ERR("Fatal error encoutered while enqueuing action to the action executor"); @@ -4417,6 +4706,7 @@ next_client: end_unlock: notification_client_list_put(client_list); rcu_read_unlock(); +end: return ret; } @@ -4425,14 +4715,14 @@ int handle_one_event_notifier_notification( struct notification_thread_state *state, int pipe, enum lttng_domain_type domain) { - int ret; + int ret = 0; struct lttng_event_notifier_notification *notification = NULL; notification = recv_one_event_notifier_notification(pipe, domain); if (notification == NULL) { + /* Reception failed, don't consider it fatal. */ ERR("[notification-thread] Error receiving an event notifier notification from tracer: fd = %i, domain = %s", pipe, lttng_domain_type_str(domain)); - ret = -1; goto end; } diff --git a/src/bin/lttng-sessiond/notification-thread-internal.h b/src/bin/lttng-sessiond/notification-thread-internal.h index 0403527dc..38d968a4d 100644 --- a/src/bin/lttng-sessiond/notification-thread-internal.h +++ b/src/bin/lttng-sessiond/notification-thread-internal.h @@ -82,6 +82,8 @@ struct channel_info { struct lttng_event_notifier_notification { uint64_t tracer_token; enum lttng_domain_type type; + size_t capture_buf_size; + char *capture_buffer; }; struct notification_client_list_element { @@ -111,8 +113,9 @@ struct notification_client_list_element { struct notification_client_list { pthread_mutex_t lock; struct urcu_ref ref; - const struct lttng_trigger *trigger; - struct cds_list_head list; + struct lttng_condition *condition; + struct cds_list_head triggers_list; + struct cds_list_head clients_list; /* Weak reference to container. */ struct cds_lfht *notification_trigger_clients_ht; struct cds_lfht_node notification_trigger_clients_ht_node; @@ -248,10 +251,15 @@ int notification_thread_client_communication_update( notification_client_id id, enum client_transmission_status transmission_status); +/* + * Takes ownership of the payload if present. + */ LTTNG_HIDDEN struct lttng_event_notifier_notification *lttng_event_notifier_notification_create( uint64_t tracer_token, - enum lttng_domain_type domain); + enum lttng_domain_type domain, + char *payload, + size_t payload_size); LTTNG_HIDDEN void lttng_event_notifier_notification_destroy( diff --git a/src/bin/lttng-sessiond/notification-thread.c b/src/bin/lttng-sessiond/notification-thread.c index dd1dc3e8f..b91dc5798 100644 --- a/src/bin/lttng-sessiond/notification-thread.c +++ b/src/bin/lttng-sessiond/notification-thread.c @@ -27,6 +27,7 @@ #include "lttng-sessiond.h" #include "health-sessiond.h" #include "thread.h" +#include "testpoint.h" #include "kernel.h" #include @@ -35,6 +36,8 @@ #include #include + +int notifier_consumption_paused; /* * Destroy the thread data previously created by the init function. */ @@ -482,6 +485,7 @@ int init_thread_state(struct notification_thread_handle *handle, if (!state->channels_ht) { goto error; } + state->sessions_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); if (!state->sessions_ht) { @@ -574,6 +578,17 @@ static int handle_event_notification_pipe(int event_source_fd, goto end; } + if (testpoint(sessiond_handle_notifier_event_pipe)) { + ret = 0; + goto end; + } + + if (caa_unlikely(notifier_consumption_paused)) { + DBG("Event notifier notification consumption paused, sleeping..."); + sleep(1); + goto end; + } + ret = handle_notification_thread_event_notification( state, event_source_fd, domain); if (ret) { @@ -582,6 +597,7 @@ static int handle_event_notification_pipe(int event_source_fd, ret = -1; goto end; } + end: return ret; } @@ -640,6 +656,10 @@ void *thread_notification(void *data) goto end; } + if (testpoint(sessiond_thread_notification)) { + goto end; + } + while (true) { int fd_count, i; diff --git a/src/bin/lttng-sessiond/register.c b/src/bin/lttng-sessiond/register.c index 6381c1179..ac1583dc2 100644 --- a/src/bin/lttng-sessiond/register.c +++ b/src/bin/lttng-sessiond/register.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -20,7 +21,6 @@ #include "testpoint.h" #include "health-sessiond.h" #include "fd-limit.h" -#include "shm.h" #include "utils.h" #include "thread.h" diff --git a/src/bin/lttng-sessiond/save.c b/src/bin/lttng-sessiond/save.c index 6ca756365..da6d948c4 100644 --- a/src/bin/lttng-sessiond/save.c +++ b/src/bin/lttng-sessiond/save.c @@ -131,6 +131,87 @@ end: return ret; } +static +int save_map_attributes(struct config_writer *writer, + struct lttng_map *map) +{ + int ret; + unsigned int i; + + ret = config_writer_write_element_unsigned_int(writer, + config_element_bitness, + lttng_map_get_bitness(map)==LTTNG_MAP_BITNESS_32BITS ? 32: 64); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + assert( lttng_map_get_boundary_policy(map) == LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW); + ret = config_writer_write_element_string(writer, + config_element_boundary_policy, config_boundary_policy_overflow); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_bool(writer, + config_element_coalesce_hits, + lttng_map_get_coalesce_hits(map)); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_open_element(writer, + config_element_dimensions); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + for (i = 0; i < lttng_map_get_dimension_count(map); i++) { + enum lttng_map_status map_status; + uint64_t dim_len; + + ret = config_writer_open_element(writer, + config_element_dimension); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + map_status = lttng_map_get_dimension_length(map, i, &dim_len); + if (map_status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_unsigned_int(writer, + config_element_dimension_size, dim_len); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + /* dimension */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + } + + /* dimensions */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + +end: + return ret; +} + + /* Return LTTNG_OK on success else a LTTNG_ERR* code. */ static int save_ust_channel_attributes(struct config_writer *writer, @@ -1642,6 +1723,61 @@ end: return ret; } +/* Return LTTNG_OK on success else a LTTNG_ERR* code. */ +static +int save_kernel_map(struct config_writer *writer, + struct ltt_kernel_map *kmap) +{ + int ret; + const char *map_name = NULL; + enum lttng_map_status map_status; + + assert(writer); + assert(kmap); + + ret = config_writer_open_element(writer, config_element_map); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + map_status = lttng_map_get_name(kmap->map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_string(writer, config_element_name, + map_name); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_bool(writer, config_element_enabled, + lttng_map_get_is_enabled(kmap->map)); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = save_map_attributes(writer, kmap->map); + if (ret) { + goto end; + } + + /* map */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = LTTNG_OK; +end: + return ret; +} + /* Return LTTNG_OK on success else a LTTNG_ERR* code. */ static int save_ust_channel(struct config_writer *writer, @@ -1745,6 +1881,56 @@ end: return ret; } +/* Return LTTNG_OK on success else a LTTNG_ERR* code. */ +static +int save_ust_map(struct config_writer *writer, + struct ltt_ust_map *ust_map, + struct ltt_ust_session *session) +{ + int ret; + + assert(writer); + assert(ust_map); + assert(session); + + ret = config_writer_open_element(writer, config_element_map); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_string(writer, config_element_name, + ust_map->name); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_bool(writer, config_element_enabled, + ust_map->enabled); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = save_map_attributes(writer, ust_map->map); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + /* /map */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = LTTNG_OK; +end: + return ret; +} + /* Return LTTNG_OK on success else a LTTNG_ERR* code. */ static int save_kernel_session(struct config_writer *writer, @@ -1752,6 +1938,7 @@ int save_kernel_session(struct config_writer *writer, { int ret; struct ltt_kernel_channel *kchan; + struct ltt_kernel_map *kmap; assert(writer); assert(session); @@ -1785,6 +1972,14 @@ int save_kernel_session(struct config_writer *writer, } } + cds_list_for_each_entry(kmap, &session->kernel_session->map_list.head, + list) { + ret = save_kernel_map(writer, kmap); + if (ret != LTTNG_OK) { + goto end; + } + } + /* /channels */ ret = config_writer_close_element(writer); if (ret) { @@ -2085,6 +2280,7 @@ int save_ust_domain(struct config_writer *writer, { int ret; struct ltt_ust_channel *ust_chan; + struct ltt_ust_map *ust_map; const char *buffer_type_string; struct lttng_ht_node_str *node; struct lttng_ht_iter iter; @@ -2155,6 +2351,32 @@ int save_ust_domain(struct config_writer *writer, goto end; } + ret = config_writer_open_element(writer, config_element_maps); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + rcu_read_lock(); + cds_lfht_for_each_entry(session->ust_session->domain_global.maps->ht, + &iter.iter, node, node) { + ust_map = caa_container_of(node, struct ltt_ust_map, node); + if (domain == LTTNG_DOMAIN_UST) { + ret = save_ust_map(writer, ust_map, session->ust_session); + if (ret != LTTNG_OK) { + rcu_read_unlock(); + goto end; + } + } + } + rcu_read_unlock(); + + /* /maps */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + if (domain == LTTNG_DOMAIN_UST) { ret = config_writer_open_element( writer, config_element_process_attr_trackers); diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h index 5ff20ad41..3c6c34792 100644 --- a/src/bin/lttng-sessiond/session.h +++ b/src/bin/lttng-sessiond/session.h @@ -210,6 +210,7 @@ void session_unlock(struct ltt_session *session); * are being transmitted to the various applications. */ void session_lock_list(void); + int session_trylock_list(void); void session_unlock_list(void); diff --git a/src/bin/lttng-sessiond/sessiond-config.c b/src/bin/lttng-sessiond/sessiond-config.c index c7a918307..fbd301e97 100644 --- a/src/bin/lttng-sessiond/sessiond-config.c +++ b/src/bin/lttng-sessiond/sessiond-config.c @@ -24,6 +24,7 @@ struct sessiond_config sessiond_config_build_defaults = { .verbose_consumer = 0, .agent_tcp_port = { .begin = DEFAULT_AGENT_TCP_PORT_RANGE_BEGIN, .end = DEFAULT_AGENT_TCP_PORT_RANGE_END }, + .event_notifier_error_counter_bucket = 4096, .app_socket_timeout = DEFAULT_APP_SOCKET_RW_TIMEOUT, .no_kernel = false, diff --git a/src/bin/lttng-sessiond/sessiond-config.h b/src/bin/lttng-sessiond/sessiond-config.h index 9ce036e70..9369f4cc0 100644 --- a/src/bin/lttng-sessiond/sessiond-config.h +++ b/src/bin/lttng-sessiond/sessiond-config.h @@ -29,6 +29,8 @@ struct sessiond_config { int verbose_consumer; /* Agent TCP port range for registration. Used by the agent thread. */ struct config_int_range agent_tcp_port; + + int event_notifier_error_counter_bucket; /* Socket timeout for receiving and sending (in seconds). */ int app_socket_timeout; diff --git a/src/bin/lttng-sessiond/testpoint.h b/src/bin/lttng-sessiond/testpoint.h index 8524e1fd3..2ddd66283 100644 --- a/src/bin/lttng-sessiond/testpoint.h +++ b/src/bin/lttng-sessiond/testpoint.h @@ -22,5 +22,7 @@ TESTPOINT_DECL(sessiond_thread_manage_consumer); TESTPOINT_DECL(sessiond_thread_ht_cleanup); TESTPOINT_DECL(sessiond_thread_app_manage_notify); TESTPOINT_DECL(sessiond_thread_app_reg_dispatch); +TESTPOINT_DECL(sessiond_thread_notification); +TESTPOINT_DECL(sessiond_handle_notifier_event_pipe); #endif /* SESSIOND_TESTPOINT_H */ diff --git a/src/bin/lttng-sessiond/trace-kernel.c b/src/bin/lttng-sessiond/trace-kernel.c index 2623b2971..b550ccab5 100644 --- a/src/bin/lttng-sessiond/trace-kernel.c +++ b/src/bin/lttng-sessiond/trace-kernel.c @@ -13,18 +13,23 @@ #include #include +#include #include #include #include #include #include -#include -#include +#include +#include +#include +#include #include #include #include #include -#include +#include +#include +#include #include #include #include @@ -35,6 +40,23 @@ #include "lttng-sessiond.h" #include "notification-thread-commands.h" +/* Next available map key. Access under next_map_key_lock. */ +static uint64_t _next_map_key; +static pthread_mutex_t next_map_key_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * Return the incremented value of next_map_key. + */ +static uint64_t get_next_map_key(void) +{ + uint64_t ret; + + pthread_mutex_lock(&next_map_key_lock); + ret = ++_next_map_key; + pthread_mutex_unlock(&next_map_key_lock); + return ret; +} + /* * Find the channel name for the given kernel session. */ @@ -66,10 +88,40 @@ struct ltt_kernel_channel *trace_kernel_get_channel_by_name( } /* - * Find the event for the given channel. + * Find the map name for the given kernel session. + */ +struct ltt_kernel_map *trace_kernel_get_map_by_name( + const char *name, struct ltt_kernel_session *session) +{ + struct ltt_kernel_map *map; + + assert(session); + assert(name); + + DBG("Trying to find map %s", name); + + cds_list_for_each_entry(map, &session->map_list.head, list) { + enum lttng_map_status status; + const char *cur_map_name; + + status = lttng_map_get_name(map->map, &cur_map_name); + assert(status == LTTNG_MAP_STATUS_OK); + + if (strcmp(name, cur_map_name) == 0) { + DBG("Found map by name %s", name); + return map; + } + } + + return NULL; +} + +/* + * Find the event for the given channel or map. */ struct ltt_kernel_event *trace_kernel_find_event( - char *name, struct ltt_kernel_channel *channel, + struct ltt_kernel_event_list *events_list, + uint64_t tracer_token, char *name, enum lttng_event_type type, struct lttng_bytecode *filter) { @@ -77,9 +129,11 @@ struct ltt_kernel_event *trace_kernel_find_event( int found = 0; assert(name); - assert(channel); - cds_list_for_each_entry(ev, &channel->events_list.head, list) { + cds_list_for_each_entry(ev, &events_list->head, list) { + if (ev->event->token != tracer_token) { + continue; + } if (type != LTTNG_EVENT_ALL && ev->type != type) { continue; } @@ -100,10 +154,10 @@ struct ltt_kernel_event *trace_kernel_find_event( break; } if (found) { - DBG("Found event %s for channel %s", name, - channel->channel->name); + DBG("Kernel event %s found", name); return ev; } else { + DBG("Kernel event %s not found", name); return NULL; } } @@ -160,9 +214,11 @@ struct ltt_kernel_session *trace_kernel_create_session(void) lks->fd = -1; lks->metadata_stream_fd = -1; lks->channel_count = 0; + lks->map_count = 0; lks->stream_count_global = 0; lks->metadata = NULL; CDS_INIT_LIST_HEAD(&lks->channel_list.head); + CDS_INIT_LIST_HEAD(&lks->map_list.head); lks->tracker_pid = process_attr_tracker_create(); if (!lks->tracker_pid) { @@ -274,6 +330,68 @@ error: return NULL; } +struct ltt_kernel_map *trace_kernel_create_map( + const struct lttng_map *map) +{ + struct ltt_kernel_map *kernel_map = NULL; + unsigned int i, number_dimensions; + + kernel_map = zmalloc(sizeof(*kernel_map)); + if (!kernel_map) { + PERROR("ltt_kernel_map zmalloc"); + goto end; + } + + switch (lttng_map_get_boundary_policy(map)) { + case LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW: + kernel_map->counter_conf.arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR; + break; + default: + abort(); + } + + switch (lttng_map_get_bitness(map)) { + case LTTNG_MAP_BITNESS_32BITS: + kernel_map->counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_32; + break; + case LTTNG_MAP_BITNESS_64BITS: + kernel_map->counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_64; + break; + default: + abort(); + } + + kernel_map->counter_conf.coalesce_hits = lttng_map_get_coalesce_hits(map); + + number_dimensions = lttng_map_get_dimension_count(map); + assert(number_dimensions <= LTTNG_KERNEL_COUNTER_DIMENSION_MAX); + + kernel_map->counter_conf.number_dimensions = number_dimensions; + + for (i = 0; i < kernel_map->counter_conf.number_dimensions; i++) { + enum lttng_map_status map_status; + uint64_t dimension_length; + + map_status = lttng_map_get_dimension_length(map, i, + &dimension_length); + assert(map_status == LTTNG_MAP_STATUS_OK); + + kernel_map->counter_conf.dimensions[i].size = dimension_length; + + //FIXME: We need to turn on overflow and underflow + kernel_map->counter_conf.dimensions[i].has_overflow = false; + kernel_map->counter_conf.dimensions[i].has_underflow = false; + } + + kernel_map->fd = -1; + kernel_map->enabled = 1; + kernel_map->key = get_next_map_key(); + + kernel_map->event_counters_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); +end: + return kernel_map; +} + /* * Allocate and init a kernel context object. * @@ -485,6 +603,7 @@ error: enum lttng_error_code trace_kernel_create_event_notifier_rule( struct lttng_trigger *trigger, uint64_t token, + uint64_t error_counter_index, struct ltt_kernel_event_notifier_rule **event_notifier_rule) { enum lttng_error_code ret = LTTNG_OK; @@ -501,9 +620,9 @@ enum lttng_error_code trace_kernel_create_event_notifier_rule( assert(condition); condition_type = lttng_condition_get_type(condition); - assert(condition_type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + assert(condition_type == LTTNG_CONDITION_TYPE_ON_EVENT); - condition_status = lttng_condition_event_rule_get_rule( + condition_status = lttng_condition_on_event_get_rule( condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); assert(event_rule); @@ -522,6 +641,7 @@ enum lttng_error_code trace_kernel_create_event_notifier_rule( local_kernel_token_event_rule->fd = -1; local_kernel_token_event_rule->enabled = 1; local_kernel_token_event_rule->token = token; + local_kernel_token_event_rule->error_counter_index = error_counter_index; /* Get the reference of the event rule. */ lttng_trigger_get(trigger); @@ -538,19 +658,26 @@ error: return ret; } +enum trace_kernel_event_type { + TRACE_KERNEL_EVENT_TYPE_NOTIFIER, + TRACE_KERNEL_EVENT_TYPE_COUNTER, +}; + /* - * Initialize a kernel trigger from an event rule. + * Initialize a kernel event from an event rule. */ -enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( +static +enum lttng_error_code trace_kernel_init_event_from_event_rule( const struct lttng_event_rule *rule, - struct lttng_kernel_event_notifier *kernel_event_notifier) + struct lttng_kernel_event *kernel_event, + enum trace_kernel_event_type event_type) { enum lttng_error_code ret_code; const char *name; int strncpy_ret; switch (lttng_event_rule_get_type(rule)) { - case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: { uint64_t address = 0, offset = 0; const char *symbol_name = NULL; @@ -558,7 +685,7 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( enum lttng_kernel_probe_location_status k_status; enum lttng_event_rule_status status; - status = lttng_event_rule_kprobe_get_location(rule, &location); + status = lttng_event_rule_kernel_probe_get_location(rule, &location); if (status != LTTNG_EVENT_RULE_STATUS_OK) { ret_code = LTTNG_ERR_PROBE_LOCATION_INVAL; goto error; @@ -585,12 +712,68 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( abort(); } - kernel_event_notifier->event.instrumentation = LTTNG_KERNEL_KPROBE; - kernel_event_notifier->event.u.kprobe.addr = address; - kernel_event_notifier->event.u.kprobe.offset = offset; + kernel_event->instrumentation = LTTNG_KERNEL_KPROBE; + kernel_event->u.kprobe.addr = address; + kernel_event->u.kprobe.offset = offset; + if (symbol_name) { + strncpy_ret = lttng_strncpy( + kernel_event->u.kprobe.symbol_name, + symbol_name, LTTNG_KERNEL_SYM_NAME_LEN); + + if (strncpy_ret) { + ret_code = LTTNG_ERR_INVALID; + goto error; + } + } + + kernel_event->u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + + status = lttng_event_rule_kernel_probe_get_event_name(rule, &name); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + ret_code = LTTNG_OK; + break; + } + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + { + uint64_t address = 0, offset = 0; + const char *symbol_name = NULL; + const struct lttng_kernel_function_location *location = NULL; + enum lttng_kernel_function_location_status k_status; + enum lttng_event_rule_status status; + + status = lttng_event_rule_kernel_function_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret_code = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + switch (lttng_kernel_function_location_get_type(location)) { + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS: + { + k_status = lttng_kernel_function_location_address_get_address( + location, &address); + assert(k_status == LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK); + break; + } + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET: + { + k_status = lttng_kernel_function_location_symbol_get_offset( + location, &offset); + assert(k_status == LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK); + symbol_name = lttng_kernel_function_location_symbol_get_name( + location); + break; + } + default: + abort(); + } + + kernel_event->instrumentation = LTTNG_KERNEL_KRETPROBE; + kernel_event->u.kretprobe.addr = address; + kernel_event->u.kretprobe.offset = offset; if (symbol_name) { strncpy_ret = lttng_strncpy( - kernel_event_notifier->event.u.kprobe.symbol_name, + kernel_event->u.kretprobe.symbol_name, symbol_name, LTTNG_KERNEL_SYM_NAME_LEN); if (strncpy_ret) { @@ -599,25 +782,26 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( } } - kernel_event_notifier->event.u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; - status = lttng_event_rule_kprobe_get_name(rule, &name); + kernel_event->u.kretprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + + status = lttng_event_rule_kernel_function_get_event_name(rule, &name); assert(status == LTTNG_EVENT_RULE_STATUS_OK); ret_code = LTTNG_OK; break; } - case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: { const struct lttng_userspace_probe_location* location = NULL; const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; enum lttng_event_rule_status status; - status = lttng_event_rule_uprobe_get_location(rule, &location); + status = lttng_event_rule_userspace_probe_get_location(rule, &location); if (status != LTTNG_EVENT_RULE_STATUS_OK) { ret_code = LTTNG_ERR_PROBE_LOCATION_INVAL; goto error; } - kernel_event_notifier->event.instrumentation = LTTNG_KERNEL_UPROBE; + kernel_event->instrumentation = LTTNG_KERNEL_UPROBE; lookup = lttng_userspace_probe_location_get_lookup_method( location); @@ -633,20 +817,20 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: /* Get the file descriptor on the target binary. */ - kernel_event_notifier->event.u.uprobe.fd = + kernel_event->u.uprobe.fd = lttng_userspace_probe_location_function_get_binary_fd(location); break; case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: /* Get the file descriptor on the target binary. */ - kernel_event_notifier->event.u.uprobe.fd = + kernel_event->u.uprobe.fd = lttng_userspace_probe_location_tracepoint_get_binary_fd(location); break; default: abort(); } - status = lttng_event_rule_uprobe_get_name(rule, &name); + status = lttng_event_rule_userspace_probe_get_event_name(rule, &name); assert(status == LTTNG_EVENT_RULE_STATUS_OK); ret_code = LTTNG_OK; break; @@ -661,7 +845,7 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( assert(domain == LTTNG_DOMAIN_KERNEL); assert(status == LTTNG_EVENT_RULE_STATUS_OK); - kernel_event_notifier->event.instrumentation = + kernel_event->instrumentation = LTTNG_KERNEL_TRACEPOINT; ret_code = LTTNG_OK; @@ -675,24 +859,31 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( assert(status == LTTNG_EVENT_RULE_STATUS_OK); - kernel_event_notifier->event.instrumentation = + kernel_event->instrumentation = LTTNG_KERNEL_SYSCALL; - kernel_event_notifier->event.u.syscall.abi = + kernel_event->u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_ALL; - kernel_event_notifier->event.u.syscall.entryexit = - LTTNG_KERNEL_SYSCALL_ENTRY; - kernel_event_notifier->event.u.syscall.match = + kernel_event->u.syscall.match = LTTNG_KERNEL_SYSCALL_MATCH_NAME; + switch (event_type) { + case TRACE_KERNEL_EVENT_TYPE_COUNTER: + kernel_event->u.syscall.entryexit = + LTTNG_KERNEL_SYSCALL_ENTRYEXIT; + break; + case TRACE_KERNEL_EVENT_TYPE_NOTIFIER: + kernel_event->u.syscall.entryexit = + LTTNG_KERNEL_SYSCALL_ENTRY; + break; + } ret_code = LTTNG_OK; break; } - case LTTNG_EVENT_RULE_TYPE_KRETPROBE: default: abort(); break; } - strncpy_ret = lttng_strncpy(kernel_event_notifier->event.name, name, + strncpy_ret = lttng_strncpy(kernel_event->name, name, LTTNG_KERNEL_SYM_NAME_LEN); if (strncpy_ret) { ret_code = LTTNG_ERR_INVALID; @@ -702,6 +893,25 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( error: return ret_code; } + +enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_kernel_event_notifier *kernel_event_notifier) +{ + return trace_kernel_init_event_from_event_rule(rule, + &kernel_event_notifier->event, + TRACE_KERNEL_EVENT_TYPE_NOTIFIER); +} + +enum lttng_error_code trace_kernel_init_event_counter_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_kernel_counter_event *kernel_counter_event) +{ + return trace_kernel_init_event_from_event_rule(rule, + &kernel_counter_event->event, + TRACE_KERNEL_EVENT_TYPE_COUNTER); +} + /* * Allocate and initialize a kernel metadata. * @@ -857,6 +1067,41 @@ void trace_kernel_destroy_event(struct ltt_kernel_event *event) free(event); } +/* + * Free kernel event counter structure from RCU context + */ +static void free_event_counter_rcu(struct rcu_head *rcu_node) +{ + struct ltt_kernel_event_counter *event_counter = caa_container_of(rcu_node, + struct ltt_kernel_event_counter, rcu_node); + + free(event_counter); +} +/* + * Cleanup kernel event counter structure. + */ +void trace_kernel_destroy_event_counter(struct ltt_kernel_event_counter *event_counter) +{ + assert(event_counter); + + if (event_counter->fd >= 0) { + int ret; + + DBG("[trace] Closing event counter fd %d", event_counter->fd); + /* Close kernel fd */ + ret = close(event_counter->fd); + if (ret) { + PERROR("close"); + } + } else { + DBG("[trace] Tearing down event counter (no associated file descriptor)"); + } + + lttng_map_key_put(event_counter->key); + call_rcu(&event_counter->rcu_node, free_event_counter_rcu); +} + + /* * Cleanup kernel event structure. */ @@ -954,6 +1199,38 @@ void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel) free(channel); } +/* + * Cleanup kernel map structure. + */ +void trace_kernel_destroy_map(struct ltt_kernel_map *map) +{ + int ret; + struct ltt_kernel_event_counter *event_counter; + struct lttng_ht_iter iter; + + assert(map); + + DBG("[trace] Closing map fd %d", map->fd); + /* Close kernel fd */ + if (map->fd >= 0) { + ret = close(map->fd); + if (ret) { + PERROR("close"); + } + } + + /* For each event counter in the map hashtable */ + cds_lfht_for_each_entry(map->event_counters_ht->ht, &iter.iter, + event_counter, ht_node.node) { + trace_kernel_destroy_event_counter(event_counter); + } + + /* Remove from map list */ + cds_list_del(&map->list); + + free(map); +} + /* * Cleanup kernel metadata structure. */ @@ -984,6 +1261,7 @@ void trace_kernel_destroy_metadata(struct ltt_kernel_metadata *metadata) void trace_kernel_destroy_session(struct ltt_kernel_session *session) { struct ltt_kernel_channel *channel, *ctmp; + struct ltt_kernel_map *map, *map_tmp; int ret; assert(session); @@ -1012,6 +1290,10 @@ void trace_kernel_destroy_session(struct ltt_kernel_session *session) cds_list_for_each_entry_safe(channel, ctmp, &session->channel_list.head, list) { trace_kernel_destroy_channel(channel); } + + cds_list_for_each_entry_safe(map, map_tmp, &session->map_list.head, list) { + trace_kernel_destroy_map(map); + } } /* Free elements needed by destroy notifiers. */ diff --git a/src/bin/lttng-sessiond/trace-kernel.h b/src/bin/lttng-sessiond/trace-kernel.h index 89a4ab19f..35e054f91 100644 --- a/src/bin/lttng-sessiond/trace-kernel.h +++ b/src/bin/lttng-sessiond/trace-kernel.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,11 @@ struct ltt_kernel_channel_list { struct cds_list_head head; }; +/* map list */ +struct ltt_kernel_map_list { + struct cds_list_head head; +}; + struct ltt_kernel_context { struct lttng_kernel_context ctx; struct cds_list_head list; @@ -52,9 +58,10 @@ struct ltt_kernel_event { struct lttng_userspace_probe_location *userspace_probe_location; }; -/* Kernel event */ +/* Kernel event notifier */ struct ltt_kernel_event_notifier_rule { int fd; + uint64_t error_counter_index; int enabled; enum lttng_event_type type; struct lttng_trigger *trigger; @@ -66,6 +73,24 @@ struct ltt_kernel_event_notifier_rule { struct rcu_head rcu_node; }; +/* Kernel event counter */ +struct ltt_kernel_event_counter { + int fd; + int enabled; + uint64_t action_tracer_token; + struct lttng_kernel_event *event; + struct lttng_event_rule *event_rule; + const struct lttng_bytecode *filter; + struct lttng_userspace_probe_location *userspace_probe_location; + struct cds_list_head list; + struct lttng_ht_node_u64 ht_node; + + /* refcounted */ + struct lttng_map_key *key; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; +}; + /* Kernel channel */ struct ltt_kernel_channel { int fd; @@ -84,6 +109,20 @@ struct ltt_kernel_channel { bool sent_to_consumer; }; +/* Kernel map */ +struct ltt_kernel_map { + int fd; + uint64_t key; /* Key to reference this map with the notification thread. */ + int enabled; + unsigned int event_count; + struct lttng_map *map; + struct lttng_kernel_counter_conf counter_conf; + struct lttng_ht *event_counters_ht; + struct cds_list_head list; + /* Session pointer which has a reference to this object. */ + struct ltt_kernel_session *session; +}; + /* Metadata */ struct ltt_kernel_metadata { int fd; @@ -110,9 +149,11 @@ struct ltt_kernel_session { int metadata_stream_fd; int consumer_fds_sent; unsigned int channel_count; + unsigned int map_count; unsigned int stream_count_global; struct ltt_kernel_metadata *metadata; struct ltt_kernel_channel_list channel_list; + struct ltt_kernel_map_list map_list; /* UID/GID of the user owning the session */ uid_t uid; gid_t gid; @@ -144,18 +185,23 @@ struct ltt_kernel_event *trace_kernel_get_event_by_name( char *name, struct ltt_kernel_channel *channel, enum lttng_event_type type); struct ltt_kernel_event *trace_kernel_find_event( - char *name, struct ltt_kernel_channel *channel, + struct ltt_kernel_event_list *events_list, + uint64_t tracer_token, char *name, enum lttng_event_type type, struct lttng_bytecode *filter); struct ltt_kernel_channel *trace_kernel_get_channel_by_name( const char *name, struct ltt_kernel_session *session); +struct ltt_kernel_map *trace_kernel_get_map_by_name( + const char *name, struct ltt_kernel_session *session); + /* * Create functions malloc() the data structure. */ struct ltt_kernel_session *trace_kernel_create_session(void); struct ltt_kernel_channel *trace_kernel_create_channel( struct lttng_channel *chan); +struct ltt_kernel_map *trace_kernel_create_map(const struct lttng_map *map); enum lttng_error_code trace_kernel_create_event(struct lttng_event *ev, char *filter_expression, struct lttng_bytecode *filter, struct ltt_kernel_event **kernel_event); @@ -167,12 +213,16 @@ struct ltt_kernel_context *trace_kernel_create_context( enum lttng_error_code trace_kernel_create_event_notifier_rule( struct lttng_trigger *trigger, uint64_t token, + uint64_t error_counter_index, struct ltt_kernel_event_notifier_rule **event_notifier_rule); struct ltt_kernel_context *trace_kernel_copy_context( struct ltt_kernel_context *ctx); enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( const struct lttng_event_rule *rule, struct lttng_kernel_event_notifier *kernel_event_notifier); +enum lttng_error_code trace_kernel_init_event_counter_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_kernel_counter_event *kernel_counter_event); /* * Destroy functions free() the data structure and remove from linked list if @@ -181,10 +231,13 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( void trace_kernel_destroy_session(struct ltt_kernel_session *session); void trace_kernel_destroy_metadata(struct ltt_kernel_metadata *metadata); void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel); +void trace_kernel_destroy_map(struct ltt_kernel_map *map); void trace_kernel_destroy_event(struct ltt_kernel_event *event); void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream); void trace_kernel_destroy_context(struct ltt_kernel_context *ctx); void trace_kernel_destroy_event_notifier_rule(struct ltt_kernel_event_notifier_rule *rule); +void trace_kernel_destroy_event_counter( + struct ltt_kernel_event_counter *event_counter); void trace_kernel_free_session(struct ltt_kernel_session *session); #endif /* _LTT_TRACE_KERNEL_H */ diff --git a/src/bin/lttng-sessiond/trace-ust.c b/src/bin/lttng-sessiond/trace-ust.c index 527b354f1..3df0e65dc 100644 --- a/src/bin/lttng-sessiond/trace-ust.c +++ b/src/bin/lttng-sessiond/trace-ust.c @@ -18,7 +18,11 @@ #include #include +#include +#include + #include "buffer-registry.h" +#include "map.h" #include "trace-ust.h" #include "utils.h" #include "ust-app.h" @@ -73,7 +77,14 @@ int trace_ust_ht_match_event(struct cds_lfht_node *node, const void *_key) key = _key; ev_loglevel_value = event->attr.loglevel; - /* Match the 4 elements of the key: name, filter, loglevel, exclusions. */ + /* Match the 6 elements of the key: tracer_token, map_key, name, filter, loglevel, exclusions. */ + if (event->attr.token != key->tracer_token) { + goto no_match; + } + + if (!lttng_map_key_is_equal(event->key, key->key)) { + goto no_match; + } /* Event name */ if (strncmp(event->attr.name, key->name, sizeof(event->attr.name)) != 0) { @@ -189,14 +200,44 @@ error: return NULL; } +/* + * Find the map in the hashtable and return map pointer. RCU read side + * lock MUST be acquired before calling this. + */ +struct ltt_ust_map *trace_ust_find_map_by_name(struct lttng_ht *ht, + const char *name) +{ + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + + if (name[0] == '\0') { + goto error; + } + + lttng_ht_lookup(ht, (void *)name, &iter); + node = lttng_ht_iter_get_node_str(&iter); + if (node == NULL) { + goto error; + } + + DBG2("Trace UST map %s found by name", name); + + return caa_container_of(node, struct ltt_ust_map, node); + +error: + DBG2("Trace UST map %s not found by name", name); + return NULL; +} + /* * Find the event in the hashtable and return event pointer. RCU read side lock * MUST be acquired before calling this. */ struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, - char *name, struct lttng_bytecode *filter, + uint64_t tracer_token, char *name, struct lttng_bytecode *filter, enum lttng_ust_loglevel_type loglevel_type, int loglevel_value, - struct lttng_event_exclusion *exclusion) + struct lttng_event_exclusion *exclusion, + struct lttng_map_key *map_key) { struct lttng_ht_node_str *node; struct lttng_ht_iter iter; @@ -205,11 +246,13 @@ struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, assert(name); assert(ht); + key.tracer_token = tracer_token; key.name = name; key.filter = filter; key.loglevel_type = loglevel_type; key.loglevel_value = loglevel_value; key.exclusion = exclusion; + key.key = map_key; cds_lfht_lookup(ht->ht, ht->hash_fct((void *) name, lttng_ht_seed), trace_ust_ht_match_event, &key, &iter.iter); @@ -301,6 +344,8 @@ struct ltt_ust_session *trace_ust_create_session(uint64_t session_id) /* Alloc UST global domain channels' HT */ lus->domain_global.channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + /* Alloc UST global domain maps' HT */ + lus->domain_global.maps = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); /* Alloc agent hash table. */ lus->agents = lttng_ht_new(0, LTTNG_HT_TYPE_U64); @@ -330,6 +375,7 @@ error: process_attr_tracker_destroy(lus->tracker_vuid); process_attr_tracker_destroy(lus->tracker_vgid); ht_cleanup_push(lus->domain_global.channels); + ht_cleanup_push(lus->domain_global.maps); ht_cleanup_push(lus->agents); free(lus); error_alloc: @@ -405,6 +451,67 @@ error: return luc; } +/* + * Allocate and initialize a ust map data structure. + * + * Return pointer to structure or NULL. + */ +struct ltt_ust_map *trace_ust_create_map(const struct lttng_map *map) +{ + struct ltt_ust_map *umap = NULL; + enum lttng_map_status map_status; + const char *map_name = NULL; + unsigned int dimension_count; + uint64_t dimension_len; + + umap = zmalloc(sizeof(*umap)); + if (!umap) { + PERROR("ltt_ust_map zmalloc"); + goto end; + } + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Can't get map name"); + umap = NULL; + goto end; + } + + dimension_count = lttng_map_get_dimension_count(map); + assert(dimension_count == 1); + + map_status = lttng_map_get_dimension_length(map, 0, &dimension_len); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Can't get map first dimension length"); + umap = NULL; + goto end; + } + assert(dimension_len > 0); + + strncpy(umap->name, map_name, sizeof(umap->name)); + + umap->enabled = 1; + umap->bucket_count = dimension_len; + umap->coalesce_hits = lttng_map_get_coalesce_hits(map); + umap->bitness = lttng_map_get_bitness(map); + umap->nr_cpu = ustctl_get_nr_cpu_per_counter(); + + umap->dead_app_kv_values.dead_app_kv_values_32bits = lttng_ht_new(0, + LTTNG_HT_TYPE_STRING); + umap->dead_app_kv_values.dead_app_kv_values_64bits = lttng_ht_new(0, + LTTNG_HT_TYPE_STRING); + pthread_mutex_init(&umap->dead_app_kv_values.lock, NULL); + + umap->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + + /* Init node */ + lttng_ht_node_init_str(&umap->node, umap->name); + + DBG2("Trace UST map %s created", umap->name); +end: + return umap; +} + /* * Validates an exclusion list. * @@ -444,7 +551,12 @@ end: * * Return an lttng_error_code */ -enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, +enum lttng_error_code trace_ust_create_event(uint64_t tracer_token, + const char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + enum lttng_loglevel ev_loglevel, char *filter_expression, struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, @@ -454,8 +566,6 @@ enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, struct ltt_ust_event *local_ust_event; enum lttng_error_code ret = LTTNG_OK; - assert(ev); - if (exclusion && validate_exclusion(exclusion)) { ret = LTTNG_ERR_INVALID; goto error; @@ -469,8 +579,9 @@ enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, } local_ust_event->internal = internal_event; + local_ust_event->attr.token = tracer_token; - switch (ev->type) { + switch (ev_type) { case LTTNG_EVENT_PROBE: local_ust_event->attr.instrumentation = LTTNG_UST_PROBE; break; @@ -484,44 +595,53 @@ enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, local_ust_event->attr.instrumentation = LTTNG_UST_TRACEPOINT; break; default: - ERR("Unknown ust instrumentation type (%d)", ev->type); + ERR("Unknown ust instrumentation type (%d)", ev_type); ret = LTTNG_ERR_INVALID; goto error_free_event; } /* Copy event name */ - strncpy(local_ust_event->attr.name, ev->name, LTTNG_UST_SYM_NAME_LEN); + strncpy(local_ust_event->attr.name, ev_name, LTTNG_UST_SYM_NAME_LEN); local_ust_event->attr.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; - switch (ev->loglevel_type) { + switch (ev_loglevel_type) { case LTTNG_EVENT_LOGLEVEL_ALL: local_ust_event->attr.loglevel_type = LTTNG_UST_LOGLEVEL_ALL; local_ust_event->attr.loglevel = -1; /* Force to -1 */ break; case LTTNG_EVENT_LOGLEVEL_RANGE: local_ust_event->attr.loglevel_type = LTTNG_UST_LOGLEVEL_RANGE; - local_ust_event->attr.loglevel = ev->loglevel; + local_ust_event->attr.loglevel = ev_loglevel; break; case LTTNG_EVENT_LOGLEVEL_SINGLE: local_ust_event->attr.loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE; - local_ust_event->attr.loglevel = ev->loglevel; + local_ust_event->attr.loglevel = ev_loglevel; break; default: - ERR("Unknown ust loglevel type (%d)", ev->loglevel_type); + ERR("Unknown ust loglevel type (%d)", ev_loglevel_type); ret = LTTNG_ERR_INVALID; goto error_free_event; } /* Same layout. */ + local_ust_event->key = key; local_ust_event->filter_expression = filter_expression; local_ust_event->filter = filter; local_ust_event->exclusion = exclusion; + /* Take a reference on the lttng_map_key to bounds its lifetime to the + * ust_event. + */ + if (key) { + lttng_map_key_get(key); + } + /* Init node */ lttng_ht_node_init_str(&local_ust_event->node, local_ust_event->attr.name); - DBG2("Trace UST event %s, loglevel (%d,%d) created", - local_ust_event->attr.name, local_ust_event->attr.loglevel_type, + DBG2("Trace UST event %s, tracer token %"PRIu64", loglevel (%d,%d) created", + local_ust_event->attr.name, local_ust_event->attr.token, + local_ust_event->attr.loglevel_type, local_ust_event->attr.loglevel); *ust_event = local_ust_event; @@ -1242,6 +1362,7 @@ void trace_ust_destroy_event(struct ltt_ust_event *event) assert(event); DBG2("Trace destroy UST event %s", event->attr.name); + lttng_map_key_put(event->key); free(event->filter_expression); free(event->filter); free(event->exclusion); @@ -1311,6 +1432,53 @@ static void _trace_ust_destroy_channel(struct ltt_ust_channel *channel) free(channel); } +/* + * Cleanup ust map structure. + * + * Should _NOT_ be called with RCU read lock held. + */ +static void _trace_ust_destroy_map(struct ltt_ust_map *map) +{ + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_iter ht_iter; + struct lttng_ht *dead_app_kv_ht; + + assert(map); + + DBG2("Trace destroy UST map %s", map->name); + + /* + * Remove all the keys before destroying the hashtables. + */ + dead_app_kv_ht = map->dead_app_kv_values.dead_app_kv_values_32bits; + cds_lfht_for_each_entry(dead_app_kv_ht->ht, &ht_iter.iter, kv_entry, node.node) { + struct lttng_ht_iter entry_iter; + + entry_iter.iter.node = &kv_entry->node.node; + lttng_ht_del(dead_app_kv_ht, &entry_iter); + + free(kv_entry->key); + free(kv_entry); + } + lttng_ht_destroy(map->dead_app_kv_values.dead_app_kv_values_32bits); + + dead_app_kv_ht = map->dead_app_kv_values.dead_app_kv_values_64bits; + cds_lfht_for_each_entry(dead_app_kv_ht->ht, &ht_iter.iter, kv_entry, node.node) { + struct lttng_ht_iter entry_iter; + + entry_iter.iter.node = &kv_entry->node.node; + lttng_ht_del(dead_app_kv_ht, &entry_iter); + + free(kv_entry->key); + free(kv_entry); + } + + lttng_ht_destroy(map->dead_app_kv_values.dead_app_kv_values_64bits); + + lttng_map_put(map->map); + free(map); +} + /* * URCU intermediate call to complete destroy channel. */ @@ -1324,6 +1492,19 @@ static void destroy_channel_rcu(struct rcu_head *head) _trace_ust_destroy_channel(channel); } +/* + * URCU intermediate call to complete destroy map. + */ +static void destroy_map_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_str *node = + caa_container_of(head, struct lttng_ht_node_str, head); + struct ltt_ust_map *map = + caa_container_of(node, struct ltt_ust_map, node); + + _trace_ust_destroy_map(map); +} + void trace_ust_destroy_channel(struct ltt_ust_channel *channel) { /* Destroying all events of the channel */ @@ -1334,6 +1515,14 @@ void trace_ust_destroy_channel(struct ltt_ust_channel *channel) call_rcu(&channel->node.head, destroy_channel_rcu); } +void trace_ust_destroy_map(struct ltt_ust_map *map) +{ + /* Destroying all events of the map */ + destroy_events(map->events); + + call_rcu(&map->node.head, destroy_map_rcu); +} + /* * Remove an UST channel from a channel HT. */ @@ -1351,6 +1540,23 @@ void trace_ust_delete_channel(struct lttng_ht *ht, assert(!ret); } +/* + * Remove an UST map from a map HT. + */ +void trace_ust_delete_map(struct lttng_ht *ht, + struct ltt_ust_map *map) +{ + int ret; + struct lttng_ht_iter iter; + + assert(ht); + assert(map); + + iter.iter.node = &map->node.node; + ret = lttng_ht_del(ht, &iter); + assert(!ret); +} + /* * Iterate over a hash table containing channels and cleanup safely. */ @@ -1374,6 +1580,28 @@ static void destroy_channels(struct lttng_ht *channels) ht_cleanup_push(channels); } +/* + * Iterate over a hash table containing maps and cleanup safely. + */ +static void destroy_maps(struct lttng_ht *maps) +{ + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + + assert(maps); + + rcu_read_lock(); + cds_lfht_for_each_entry(maps->ht, &iter.iter, node, node) { + struct ltt_ust_map *map = + caa_container_of(node, struct ltt_ust_map, node); + + trace_ust_delete_map(maps, map); + trace_ust_destroy_map(map); + } + rcu_read_unlock(); + + ht_cleanup_push(maps); +} /* * Cleanup UST global domain. */ @@ -1382,6 +1610,7 @@ static void destroy_domain_global(struct ltt_ust_domain_global *dom) assert(dom); destroy_channels(dom->channels); + destroy_maps(dom->maps); } /* diff --git a/src/bin/lttng-sessiond/trace-ust.h b/src/bin/lttng-sessiond/trace-ust.h index 53e1ab2b5..b254ad05d 100644 --- a/src/bin/lttng-sessiond/trace-ust.h +++ b/src/bin/lttng-sessiond/trace-ust.h @@ -23,11 +23,13 @@ struct agent; struct ltt_ust_ht_key { + uint64_t tracer_token; const char *name; const struct lttng_bytecode *filter; enum lttng_ust_loglevel_type loglevel_type; int loglevel_value; const struct lttng_event_exclusion *exclusion; + struct lttng_map_key *key; }; /* Context hash table nodes */ @@ -45,6 +47,9 @@ struct ltt_ust_event { char *filter_expression; struct lttng_bytecode *filter; struct lttng_event_exclusion *exclusion; + + /* refcounted */ + struct lttng_map_key *key; /* * An internal event is an event which was created by the session daemon * through which, for example, events emitted in Agent domains are @@ -76,9 +81,31 @@ struct ltt_ust_channel { uint64_t monitor_timer_interval; }; +struct ltt_ust_map_dead_pid_kv_values { + pthread_mutex_t lock; + struct lttng_ht *dead_app_kv_values_32bits; + struct lttng_ht *dead_app_kv_values_64bits; +}; + +/* UST map */ +struct ltt_ust_map { + uint64_t id; /* unique id per session. */ + char name[LTTNG_UST_SYM_NAME_LEN]; + unsigned int enabled; + size_t bucket_count; + bool coalesce_hits; + enum lttng_map_bitness bitness; + uint64_t nr_cpu; + struct lttng_ht_node_str node; + struct lttng_map *map; + struct lttng_ht *events; + struct ltt_ust_map_dead_pid_kv_values dead_app_kv_values; +}; + /* UST domain global (LTTNG_DOMAIN_UST) */ struct ltt_ust_domain_global { struct lttng_ht *channels; + struct lttng_ht *maps; struct cds_list_head registry_buffer_uid_list; }; @@ -182,11 +209,14 @@ int trace_ust_ht_match_event_by_name(struct cds_lfht_node *node, * Lookup functions. NULL is returned if not found. */ struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, - char *name, struct lttng_bytecode *filter, + uint64_t tracer_token, char *name, struct lttng_bytecode *filter, enum lttng_ust_loglevel_type loglevel_type, int loglevel_value, - struct lttng_event_exclusion *exclusion); + struct lttng_event_exclusion *exclusion, + struct lttng_map_key *key); struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht, const char *name); +struct ltt_ust_map *trace_ust_find_map_by_name(struct lttng_ht *ht, + const char *name); struct agent *trace_ust_find_agent(struct ltt_ust_session *session, enum lttng_domain_type domain_type); @@ -196,17 +226,26 @@ struct agent *trace_ust_find_agent(struct ltt_ust_session *session, struct ltt_ust_session *trace_ust_create_session(uint64_t session_id); struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, enum lttng_domain_type domain); -enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, +struct ltt_ust_map *trace_ust_create_map(const struct lttng_map *map); +enum lttng_error_code trace_ust_create_event(uint64_t tracer_token, + const char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + enum lttng_loglevel ev_loglevel, char *filter_expression, struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, - bool internal_event, struct ltt_ust_event **ust_event); + bool internal_event, + struct ltt_ust_event **ust_event); struct ltt_ust_context *trace_ust_create_context( const struct lttng_event_context *ctx); int trace_ust_match_context(const struct ltt_ust_context *uctx, const struct lttng_event_context *ctx); void trace_ust_delete_channel(struct lttng_ht *ht, struct ltt_ust_channel *channel); +void trace_ust_delete_map(struct lttng_ht *ht, + struct ltt_ust_map *map); /* * Destroy functions free() the data structure and remove from linked list if @@ -214,6 +253,7 @@ void trace_ust_delete_channel(struct lttng_ht *ht, */ void trace_ust_destroy_session(struct ltt_ust_session *session); void trace_ust_destroy_channel(struct ltt_ust_channel *channel); +void trace_ust_destroy_map(struct ltt_ust_map *map); void trace_ust_destroy_event(struct ltt_ust_event *event); void trace_ust_destroy_context(struct ltt_ust_context *ctx); void trace_ust_free_session(struct ltt_ust_session *session); @@ -255,7 +295,12 @@ struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht, { return NULL; } - +static inline +struct ltt_ust_map *trace_ust_find_map_by_name(struct lttng_ht *ht, + const char *name) +{ + return NULL; +} static inline struct ltt_ust_session *trace_ust_create_session(unsigned int session_id) { @@ -268,11 +313,22 @@ struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, return NULL; } static inline -enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, - const char *filter_expression, +struct ltt_ust_map *trace_ust_create_map(const struct lttng_map *map) +{ + return NULL; +} +static inline +enum lttng_error_code trace_ust_create_event(uint64_t tracer_token, + const char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + enum lttng_loglevel ev_loglevel, + char *filter_expression, struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, - bool internal_event, struct ltt_ust_event **ust_event) + bool internal_event, + struct ltt_ust_event **ust_event) { return LTTNG_ERR_NO_UST; } @@ -286,6 +342,11 @@ void trace_ust_destroy_channel(struct ltt_ust_channel *channel) { } +static inline +void trace_ust_destroy_map(struct ltt_ust_map *map) +{ +} + static inline void trace_ust_destroy_event(struct ltt_ust_event *event) { @@ -310,9 +371,10 @@ int trace_ust_match_context(const struct ltt_ust_context *uctx, } static inline struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, - char *name, struct lttng_bytecode *filter, + uint64_t tracer_token, char *name, struct lttng_bytecode *filter, enum lttng_ust_loglevel_type loglevel_type, int loglevel_value, - struct lttng_event_exclusion *exclusion) + struct lttng_event_exclusion *exclusion, + struct lttng_map_key *key) { return NULL; } diff --git a/src/bin/lttng-sessiond/ust-abi-internal.h b/src/bin/lttng-sessiond/ust-abi-internal.h index 0f12be083..413302c32 100644 --- a/src/bin/lttng-sessiond/ust-abi-internal.h +++ b/src/bin/lttng-sessiond/ust-abi-internal.h @@ -95,6 +95,7 @@ struct lttng_ust_event { enum lttng_ust_loglevel_type loglevel_type; int loglevel; /* value, -1: all */ + uint64_t token; char padding[LTTNG_UST_EVENT_PADDING1]; /* Per instrumentation type configuration */ @@ -109,9 +110,10 @@ struct lttng_ust_event_notifier { char padding[LTTNG_UST_EVENT_NOTIFIER_PADDING]; } LTTNG_PACKED; -#define LTTNG_UST_EVENT_NOTIFIER_NOTIFICATION_PADDING 34 +#define LTTNG_UST_EVENT_NOTIFIER_NOTIFICATION_PADDING 32 struct lttng_ust_event_notifier_notification { uint64_t token; + uint16_t capture_buf_size; char padding[LTTNG_UST_EVENT_NOTIFIER_NOTIFICATION_PADDING]; } LTTNG_PACKED; diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index 43864bd0f..839a71437 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -7,11 +7,14 @@ */ #define _LGPL_SOURCE +#include +#include #include #include #include #include #include +#include #include #include #include @@ -22,16 +25,24 @@ #include #include #include +#include +#include #include #include #include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include "buffer-registry.h" +#include "condition-internal.h" #include "fd-limit.h" #include "health-sessiond.h" #include "ust-app.h" @@ -44,6 +55,9 @@ #include "notification-thread-commands.h" #include "rotate.h" #include "event.h" +#include "event-notifier-error-accounting.h" +#include "map.h" + struct lttng_ht *ust_app_ht; struct lttng_ht *ust_app_ht_by_sock; @@ -56,6 +70,10 @@ int ust_app_flush_app_session(struct ust_app *app, struct ust_app_session *ua_se static uint64_t _next_channel_key; static pthread_mutex_t next_channel_key_lock = PTHREAD_MUTEX_INITIALIZER; +/* Next available map key. Access under next_map_key_lock. */ +static uint64_t _next_map_key; +static pthread_mutex_t next_map_key_lock = PTHREAD_MUTEX_INITIALIZER; + /* Next available session ID. Access under next_session_id_lock. */ static uint64_t _next_session_id; static pthread_mutex_t next_session_id_lock = PTHREAD_MUTEX_INITIALIZER; @@ -73,6 +91,19 @@ static uint64_t get_next_channel_key(void) return ret; } +/* + * Return the incremented value of next_map_key. + */ +static uint64_t get_next_map_key(void) +{ + uint64_t ret; + + pthread_mutex_lock(&next_map_key_lock); + ret = ++_next_map_key; + pthread_mutex_unlock(&next_map_key_lock); + return ret; +} + /* * Return the atomically incremented value of next_session_id. */ @@ -119,7 +150,14 @@ static int ht_match_ust_app_event(struct cds_lfht_node *node, const void *_key) key = _key; ev_loglevel_value = event->attr.loglevel; - /* Match the 4 elements of the key: name, filter, loglevel, exclusions */ + /* + * Match the 5 elements of the key: + * tracer token, name, filter, loglevel, exclusions + */ + + if (event->attr.token != key->tracer_token) { + goto no_match; + } /* Event name */ if (strncmp(event->attr.name, key->name, sizeof(event->attr.name)) != 0) { @@ -182,25 +220,23 @@ no_match: * Unique add of an ust app event in the given ht. This uses the custom * ht_match_ust_app_event match function and the event name as hash. */ -static void add_unique_ust_app_event(struct ust_app_channel *ua_chan, +static void add_unique_ust_app_event(struct lttng_ht *events_ht, struct ust_app_event *event) { struct cds_lfht_node *node_ptr; struct ust_app_ht_key key; - struct lttng_ht *ht; - assert(ua_chan); - assert(ua_chan->events); + assert(events_ht); assert(event); - ht = ua_chan->events; key.name = event->attr.name; key.filter = event->filter; key.loglevel_type = event->attr.loglevel; key.exclusion = event->exclusion; + key.tracer_token = event->attr.token; - node_ptr = cds_lfht_add_unique(ht->ht, - ht->hash_fct(event->node.key, lttng_ht_seed), + node_ptr = cds_lfht_add_unique(events_ht->ht, + events_ht->hash_fct(event->node.key, lttng_ht_seed), ht_match_ust_app_event, &key, &event->node.node); assert(node_ptr == &event->node.node); } @@ -395,6 +431,33 @@ static int release_ust_app_stream(int sock, struct ust_app_stream *stream, return ret; } +/* + * Release ust data object of the given map_counter. + * + * Return 0 on success or else a negative value. + */ +static int release_ust_app_map_counter(int sock, struct ust_app_map_counter *map_counter, + struct ust_app *app) +{ + int ret = 0; + + assert(map_counter); + + if (map_counter->obj) { + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_release_object(sock, map_counter->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app sock %d release map_counter obj failed with ret %d", + sock, ret); + } + lttng_fd_put(LTTNG_FD_APPS, 2); + free(map_counter->obj); + } + + return ret; +} + /* * Delete ust app stream safely. RCU read lock must be held before calling * this function. @@ -409,6 +472,20 @@ void delete_ust_app_stream(int sock, struct ust_app_stream *stream, free(stream); } +/* + * Delete ust app map_counter safely. RCU read lock must be held before calling + * this function. + */ +static +void delete_ust_app_map_counter(int sock, struct ust_app_map_counter *map_counter, + struct ust_app *app) +{ + assert(map_counter); + + (void) release_ust_app_map_counter(sock, map_counter, app); + free(map_counter); +} + /* * We need to execute ht_destroy outside of RCU read-side critical * section and outside of call_rcu thread, so we postpone its execution @@ -426,6 +503,22 @@ void delete_ust_app_channel_rcu(struct rcu_head *head) free(ua_chan); } +/* + * We need to execute ht_destroy outside of RCU read-side critical + * section and outside of call_rcu thread, so we postpone its execution + * using ht_cleanup_push. It is simpler than to change the semantic of + * the many callers of delete_ust_app_session(). + */ +static +void delete_ust_app_map_rcu(struct rcu_head *head) +{ + struct ust_app_map *ua_map = + caa_container_of(head, struct ust_app_map, rcu_head); + + ht_cleanup_push(ua_map->events); + free(ua_map); +} + /* * Extract the lost packet or discarded events counter when the channel is * being deleted and store the value in the parent channel so we can @@ -555,7 +648,7 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan, if (ua_chan->obj != NULL) { /* Remove channel from application UST object descriptor. */ iter.iter.node = &ua_chan->ust_objd_node.node; - ret = lttng_ht_del(app->ust_objd, &iter); + ret = lttng_ht_del(app->ust_chan_objd, &iter); assert(!ret); pthread_mutex_lock(&app->sock_lock); ret = ustctl_release_object(sock, ua_chan->obj); @@ -570,6 +663,123 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan, call_rcu(&ua_chan->rcu_head, delete_ust_app_channel_rcu); } +static +void copy_ust_app_map_values(int sock, struct ust_app_map *ua_map, + struct ust_app *app) +{ + struct ltt_ust_map_dead_pid_kv_values *kv_pair_list = ua_map->dead_app_kv_values; + struct ust_registry_session *ust_reg_sess; + struct ust_registry_map *ust_reg_map; + struct ust_registry_map_index_ht_entry *map_index_entry; + struct lttng_ht_iter key_iter; + struct lttng_ht *dead_app_kv_values; + + assert(app->buffer_type == LTTNG_BUFFER_PER_PID); + ust_reg_sess = get_session_registry(ua_map->session); + + pthread_mutex_lock(&ust_reg_sess->lock); + ust_reg_map = ust_registry_map_find(ust_reg_sess, ua_map->key); + pthread_mutex_unlock(&ust_reg_sess->lock); + assert(ust_reg_map); + + DBG("Aggregating dead map values"); + + pthread_mutex_lock(&kv_pair_list->lock); + + if (app->bits_per_long == 32) { + dead_app_kv_values = kv_pair_list->dead_app_kv_values_32bits; + } else { + dead_app_kv_values = kv_pair_list->dead_app_kv_values_64bits; + } + + /* Iterate over all the formated_key -> counter index */ + cds_lfht_for_each_entry(ust_reg_map->key_string_to_bucket_index_ht->ht, + &key_iter.iter, map_index_entry, node.node) { + bool overflow = 0, underflow = 0; + int64_t local_value = 0; + int ret; + size_t dimension_indexes[1] = {map_index_entry->index}; + + ret = ustctl_counter_aggregate(ua_map->map_handle, + dimension_indexes, &local_value, &overflow, + &underflow); + if (ret) { + ERR("Error getting counter value from the tracer: key = '%s'", + map_index_entry->formated_key); + ret = -1; + goto end; + } + + map_add_or_increment_map_values(dead_app_kv_values, + map_index_entry->formated_key, local_value, + underflow, overflow); + + } + +end: + pthread_mutex_unlock(&kv_pair_list->lock); + return; +} +/* + * Delete ust app map safely. RCU read lock must be held before calling + * this function. + * + * The session list lock must be held by the caller. + */ +static +void delete_ust_app_map(int sock, struct ust_app_map *ua_map, + struct ust_app *app) +{ + int ret; + struct lttng_ht_iter iter; + struct ust_app_event *ua_event; + struct ust_app_map_counter *map_counter, *ctmp; + struct ust_registry_session *registry; + + assert(ua_map); + + DBG3("UST app deleting map %s", ua_map->name); + + /* Wipe stream */ + cds_list_for_each_entry_safe(map_counter, ctmp, &ua_map->counters.head, list) { + cds_list_del(&map_counter->list); + delete_ust_app_map_counter(sock, map_counter, app); + } + + /* Wipe events */ + cds_lfht_for_each_entry(ua_map->events->ht, &iter.iter, ua_event, + node.node) { + ret = lttng_ht_del(ua_map->events, &iter); + assert(!ret); + delete_ust_app_event(sock, ua_event, app); + } + + if (ua_map->session->buffer_type == LTTNG_BUFFER_PER_PID) { + /* Wipe and free registry from session registry. */ + registry = get_session_registry(ua_map->session); + if (registry) { + ust_registry_map_del_free(registry, ua_map->key); + } + } + + if (ua_map->obj != NULL) { + /* Remove map from application UST object descriptor. */ + iter.iter.node = &ua_map->ust_objd_node.node; + ret = lttng_ht_del(app->ust_map_objd, &iter); + assert(!ret); + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_release_object(sock, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app sock %d release map obj failed with ret %d", + sock, ret); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + free(ua_map->obj); + } + call_rcu(&ua_map->rcu_head, delete_ust_app_map_rcu); +} + int ust_app_register_done(struct ust_app *app) { int ret; @@ -858,6 +1068,7 @@ void delete_ust_app_session_rcu(struct rcu_head *head) caa_container_of(head, struct ust_app_session, rcu_head); ht_cleanup_push(ua_sess->channels); + ht_cleanup_push(ua_sess->maps); free(ua_sess); } @@ -874,6 +1085,7 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess, int ret; struct lttng_ht_iter iter; struct ust_app_channel *ua_chan; + struct ust_app_map *ua_map; struct ust_registry_session *registry; assert(ua_sess); @@ -908,6 +1120,16 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess, delete_ust_app_channel(sock, ua_chan, app); } + cds_lfht_for_each_entry(ua_sess->maps->ht, &iter.iter, ua_map, + node.node) { + if (ua_sess->buffer_type == LTTNG_BUFFER_PER_PID) { + copy_ust_app_map_values(sock, ua_map, app); + } + ret = lttng_ht_del(ua_sess->maps, &iter); + assert(!ret); + delete_ust_app_map(sock, ua_map, app); + } + /* In case of per PID, the registry is kept in the session. */ if (ua_sess->buffer_type == LTTNG_BUFFER_PER_PID) { struct buffer_reg_pid *reg_pid = buffer_reg_pid_find(ua_sess->id); @@ -990,7 +1212,8 @@ void delete_ust_app(struct ust_app *app) ht_cleanup_push(app->sessions); ht_cleanup_push(app->ust_sessions_objd); - ht_cleanup_push(app->ust_objd); + ht_cleanup_push(app->ust_chan_objd); + ht_cleanup_push(app->ust_map_objd); ht_cleanup_push(app->token_to_event_notifier_rule_ht); /* @@ -999,6 +1222,8 @@ void delete_ust_app(struct ust_app *app) */ if (app->event_notifier_group.object) { enum lttng_error_code ret_code; + enum event_notifier_error_accounting_status status; + const int event_notifier_read_fd = lttng_pipe_get_readfd( app->event_notifier_group.event_pipe); @@ -1009,6 +1234,11 @@ void delete_ust_app(struct ust_app *app) ERR("Failed to remove application tracer event source from notification thread"); } + status = event_notifier_error_accounting_unregister_app(app); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error unregistering app from event notifier error accounting"); + } + ustctl_release_object(sock, app->event_notifier_group.object); free(app->event_notifier_group.object); } @@ -1110,6 +1340,7 @@ struct ust_app_session *alloc_ust_app_session(void) ua_sess->handle = -1; ua_sess->channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); ua_sess->metadata_attr.type = LTTNG_UST_CHAN_METADATA; + ua_sess->maps = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); pthread_mutex_init(&ua_sess->lock, NULL); return ua_sess; @@ -1172,6 +1403,43 @@ error: return NULL; } +/* + * Alloc new UST app map. + */ +static +struct ust_app_map *alloc_ust_app_map(const char *name, + struct ust_app_session *ua_sess) +{ + struct ust_app_map *ua_map; + + /* Init most of the default value by allocating and zeroing */ + ua_map = zmalloc(sizeof(struct ust_app_map)); + if (ua_map == NULL) { + PERROR("malloc"); + goto error; + } + + /* Setup map name */ + strncpy(ua_map->name, name, sizeof(ua_map->name)); + ua_map->name[sizeof(ua_map->name) - 1] = '\0'; + + ua_map->enabled = 1; + ua_map->handle = -1; + ua_map->session = ua_sess; + ua_map->key = get_next_map_key(); + ua_map->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + lttng_ht_node_init_str(&ua_map->node, ua_map->name); + + CDS_INIT_LIST_HEAD(&ua_map->counters.head); + + DBG3("UST app map %s allocated", ua_map->name); + + return ua_map; + +error: + return NULL; +} + /* * Allocate and initialize a UST app stream. * @@ -1194,6 +1462,28 @@ error: return stream; } +/* + * Allocate and initialize a UST app map_counter. + * + * Return newly allocated map_counter pointer or NULL on error. + */ +struct ust_app_map_counter *ust_app_alloc_map_counter(void) +{ + struct ust_app_map_counter *map_counter = NULL; + + map_counter = zmalloc(sizeof(*map_counter)); + if (map_counter == NULL) { + PERROR("zmalloc ust app map_counter"); + goto error; + } + + /* Zero could be a valid value for a handle so flag it to -1. */ + map_counter->handle = -1; + +error: + return map_counter; +} + /* * Alloc new UST app event. */ @@ -1228,6 +1518,7 @@ error: return NULL; } + /* * Allocate a new UST app event notifier rule. */ @@ -1253,9 +1544,9 @@ static struct ust_app_event_notifier_rule *alloc_ust_app_event_notifier_rule( condition = lttng_trigger_get_condition(trigger); assert(condition); - assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); - assert(LTTNG_CONDITION_STATUS_OK == lttng_condition_event_rule_get_rule(condition, &event_rule)); + assert(LTTNG_CONDITION_STATUS_OK == lttng_condition_on_event_get_rule(condition, &event_rule)); assert(event_rule); /* Acquire the event notifier's reference to the trigger. */ @@ -1429,7 +1720,8 @@ error: static struct ust_app_event *find_ust_app_event(struct lttng_ht *ht, const char *name, const struct lttng_bytecode *filter, int loglevel_value, - const struct lttng_event_exclusion *exclusion) + const struct lttng_event_exclusion *exclusion, + uint64_t tracer_token) { struct lttng_ht_iter iter; struct lttng_ht_node_str *node; @@ -1445,6 +1737,7 @@ static struct ust_app_event *find_ust_app_event(struct lttng_ht *ht, key.loglevel_type = loglevel_value; /* lttng_event_exclusion and lttng_ust_event_exclusion structures are similar */ key.exclusion = exclusion; + key.tracer_token = tracer_token; /* Lookup using the event name as hash and a custom match fct. */ cds_lfht_lookup(ht->ht, ht->hash_fct((void *) name, lttng_ht_seed), @@ -1775,6 +2068,44 @@ error: return ret; } +/* + * Disable the specified map on to UST tracer for the UST session. + */ +static int disable_ust_map(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_map *ua_map) +{ + int ret; + + health_code_update(); + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_disable(app->sock, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app map %s disable failed for app (pid: %d) " + "and session handle %d with ret %d", + ua_map->name, app->pid, ua_sess->handle, ret); + } else { + /* + * This is normal behavior, an application can die during the + * creation process. Don't report an error so the execution can + * continue normally. + */ + ret = 0; + DBG3("UST app disable map failed. Application is dead."); + } + goto error; + } + + DBG2("UST app map %s disabled successfully for app (pid: %d)", + ua_map->name, app->pid); + +error: + health_code_update(); + return ret; +} + /* * Enable the specified channel on to UST tracer for the UST session. */ @@ -1815,6 +2146,46 @@ error: return ret; } +/* + * Enable the specified map on to UST tracer for the UST session. + */ +static int enable_ust_map(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_map *ua_map) +{ + int ret; + + health_code_update(); + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_enable(app->sock, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app map %s enable failed for app (pid: %d) " + "and session handle %d with ret %d", + ua_map->name, app->pid, ua_sess->handle, ret); + } else { + /* + * This is normal behavior, an application can die during the + * creation process. Don't report an error so the execution can + * continue normally. + */ + ret = 0; + DBG3("UST app enable map failed. Application is dead."); + } + goto error; + } + + ua_map->enabled = 1; + + DBG2("UST app map %s enabled successfully for app (pid: %d)", + ua_map->name, app->pid); + +error: + health_code_update(); + return ret; +} + /* * Enable the specified event on to UST tracer for the UST session. */ @@ -1904,13 +2275,64 @@ error: return ret; } +/* + * Send map and stream buffer to application. + * + * Return 0 on success. On error, a negative value is returned. + */ +static int send_map_pid_to_ust(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_map *ua_map) +{ + int ret; + struct ust_app_map_counter *counter, *ctmp; + + assert(app); + assert(ua_sess); + assert(ua_map); + + health_code_update(); + + DBG("UST app sending map %s to UST app sock %d", ua_map->name, + app->sock); + + /* Send map to the application. */ + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_data_to_ust(app->sock, + ua_sess->handle, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + assert(ret == 0); + + ua_map->handle = ua_map->obj->handle; + + health_code_update(); + + /* Send all streams to application. */ + cds_list_for_each_entry_safe(counter, ctmp, &ua_map->counters.head, list) { + pthread_mutex_lock(&app->sock_lock); + // Do send the per cpu counter here + ret = ustctl_send_counter_cpu_data_to_ust(app->sock, + ua_map->obj, counter->obj); + pthread_mutex_unlock(&app->sock_lock); + assert(ret == 0); + + /* We don't need the stream anymore once sent to the tracer. */ + cds_list_del(&counter->list); + delete_ust_app_map_counter(-1, counter, app); + } + /* Flag the map that it is sent to the application. */ + ua_map->is_sent = 1; + + health_code_update(); + return ret; +} + /* * Create the specified event onto the UST tracer for a UST session. * * Should be called with session mutex held. */ static -int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, +int create_ust_channel_event(struct ust_app *app, struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan, struct ust_app_event *ua_event) { int ret = 0; @@ -1995,13 +2417,173 @@ error: return ret; } -static int init_ust_event_notifier_from_event_rule( - const struct lttng_event_rule *rule, - struct lttng_ust_event_notifier *event_notifier) +static +void add_key_token(struct lttng_ust_key_token *ust_key_token, + const struct lttng_map_key_token *key_token) { - enum lttng_event_rule_status status; - enum lttng_loglevel_type loglevel_type; - enum lttng_ust_loglevel_type ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + switch (key_token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + const struct lttng_map_key_token_string *str_token; + str_token = (typeof(str_token)) key_token; + + ust_key_token->type = LTTNG_UST_KEY_TOKEN_STRING; + strncpy(ust_key_token->arg.string, str_token->string, + LTTNG_UST_KEY_TOKEN_STRING_LEN_MAX); + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + const struct lttng_map_key_token_variable *var_token; + var_token = (typeof(var_token)) key_token; + switch (var_token->type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + ust_key_token->type = LTTNG_UST_KEY_TOKEN_EVENT_NAME; + break; + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + ust_key_token->type = LTTNG_UST_KEY_TOKEN_PROVIDER_NAME; + break; + default: + abort(); + } + + break; + } + default: + abort(); + } +} + +/* + * Create the specified event onto the UST tracer for a UST session. + * + * Should be called with session mutex held. + */ +static +int create_ust_map_event(struct ust_app *app, struct ust_app_session *ua_sess, + struct ust_app_map *ua_map, const struct lttng_map_key *key, + struct ust_app_event *ua_event) +{ + int ret = 0; + unsigned int i, key_token_count; + enum lttng_map_key_status status; + struct lttng_ust_counter_event counter_event = {0}; + + health_code_update(); + + memcpy(&counter_event.event, &ua_event->attr, sizeof(struct lttng_ust_event)); + + status = lttng_map_key_get_token_count(key, &key_token_count); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + ret = LTTNG_ERR_UNK; + goto error; + } + + assert(key_token_count > 0); + + counter_event.key.nr_dimensions = 1; + counter_event.key.key_dimensions[0].nr_key_tokens = key_token_count; + + if (key_token_count > LTTNG_UST_NR_KEY_TOKEN) { + ERR("Too many key tokens for UST tracer: token count = %u token count max =%u", + key_token_count, LTTNG_UST_NR_KEY_TOKEN); + ret = LTTNG_ERR_INVALID; + goto error; + } + + for (i = 0; i < key_token_count; i++) { + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + + add_key_token(&counter_event.key.key_dimensions[0].key_tokens[i], + token); + } + + /* Create UST event on tracer */ + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_counter_create_event(app->sock, &counter_event, ua_map->obj, + &ua_event->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + abort(); + ERR("Error ustctl counter create event %s for app pid: %d with ret %d", + ua_event->attr.name, app->pid, ret); + } else { + /* + * This is normal behavior, an application can die during the + * creation process. Don't report an error so the execution can + * continue normally. + */ + ret = 0; + DBG3("UST app counter create event failed. Application is dead."); + } + goto error; + } + + ua_event->handle = ua_event->obj->handle; + + DBG2("UST app map event %s created successfully for pid:%d object: %p", + ua_event->attr.name, app->pid, ua_event->obj); + + health_code_update(); + + /* Set filter if one is present. */ + if (ua_event->filter) { + ret = set_ust_object_filter(app, ua_event->filter, ua_event->obj); + if (ret < 0) { + goto error; + } + } + + /* Set exclusions for the event */ + if (ua_event->exclusion) { + ret = set_ust_object_exclusions(app, ua_event->exclusion, ua_event->obj); + if (ret < 0) { + goto error; + } + } + + /* If event not enabled, disable it on the tracer */ + if (ua_event->enabled) { + /* + * We now need to explicitly enable the event, since it + * is now disabled at creation. + */ + ret = enable_ust_object(app, ua_event->obj); + if (ret < 0) { + /* + * If we hit an EPERM, something is wrong with our enable call. If + * we get an EEXIST, there is a problem on the tracer side since we + * just created it. + */ + switch (ret) { + case -LTTNG_UST_ERR_PERM: + /* Code flow problem */ + assert(0); + case -LTTNG_UST_ERR_EXIST: + /* It's OK for our use case. */ + ret = 0; + break; + default: + break; + } + goto error; + } + } + +error: + health_code_update(); + return ret; +} + +static int init_ust_event_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_ust_event *event) +{ + enum lttng_event_rule_status status; + enum lttng_ust_loglevel_type ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; int loglevel = -1, ret = 0; const char *pattern; @@ -2009,8 +2591,6 @@ static int init_ust_event_notifier_from_event_rule( assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT); - memset(event_notifier, 0, sizeof(*event_notifier)); - if (lttng_event_rule_targets_agent_domain(rule)) { /* * Special event for agents @@ -2023,44 +2603,43 @@ static int init_ust_event_notifier_from_event_rule( loglevel = 0; ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; } else { - status = lttng_event_rule_tracepoint_get_pattern( - rule, &pattern); - if (status != LTTNG_EVENT_RULE_STATUS_OK) { - /* At this point, this is a fatal error. */ - abort(); - } + const struct lttng_log_level_rule *log_level_rule; - status = lttng_event_rule_tracepoint_get_log_level_type( - rule, &loglevel_type); + status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern); if (status != LTTNG_EVENT_RULE_STATUS_OK) { /* At this point, this is a fatal error. */ abort(); } - switch (loglevel_type) { - case LTTNG_EVENT_LOGLEVEL_ALL: + status = lttng_event_rule_tracepoint_get_log_level_rule( + rule, &log_level_rule); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; - break; - case LTTNG_EVENT_LOGLEVEL_RANGE: - ust_loglevel_type = LTTNG_UST_LOGLEVEL_RANGE; - break; - case LTTNG_EVENT_LOGLEVEL_SINGLE: - ust_loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE; - break; - default: - /* Unknown log level specification type. */ - abort(); - } + } else if (status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_log_level_rule_status llr_status; + + switch (lttng_log_level_rule_get_type(log_level_rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE; + llr_status = lttng_log_level_rule_exactly_get_level(log_level_rule, &loglevel); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_RANGE; + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(log_level_rule, &loglevel); + break; + default: + abort(); + } - if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { - status = lttng_event_rule_tracepoint_get_log_level( - rule, &loglevel); - assert(status == LTTNG_EVENT_RULE_STATUS_OK); + assert(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + } else { + /* At this point this is a fatal error */ + assert(0); } } - event_notifier->event.instrumentation = LTTNG_UST_TRACEPOINT; - ret = lttng_strncpy(event_notifier->event.name, pattern, + event->instrumentation = LTTNG_UST_TRACEPOINT; + ret = lttng_strncpy(event->name, pattern, LTTNG_UST_SYM_NAME_LEN - 1); if (ret) { ERR("Failed to copy event rule pattern to notifier: pattern = '%s' ", @@ -2068,8 +2647,8 @@ static int init_ust_event_notifier_from_event_rule( goto end; } - event_notifier->event.loglevel_type = ust_loglevel_type; - event_notifier->event.loglevel = loglevel; + event->loglevel_type = ust_loglevel_type; + event->loglevel = loglevel; end: return ret; } @@ -2082,9 +2661,8 @@ static int create_ust_event_notifier(struct ust_app *app, struct ust_app_event_notifier_rule *ua_event_notifier_rule) { int ret = 0; - enum lttng_condition_status condition_status; + struct lttng_ust_event_notifier event_notifier = {0}; const struct lttng_condition *condition = NULL; - struct lttng_ust_event_notifier event_notifier; const struct lttng_event_rule *event_rule = NULL; unsigned int capture_bytecode_count = 0, i; enum lttng_condition_status cond_status; @@ -2095,15 +2673,15 @@ static int create_ust_event_notifier(struct ust_app *app, condition = lttng_trigger_get_const_condition( ua_event_notifier_rule->trigger); assert(condition); - assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); - condition_status = lttng_condition_event_rule_get_rule(condition, &event_rule); - assert(condition_status == LTTNG_CONDITION_STATUS_OK); + lttng_condition_on_event_get_rule(condition, &event_rule); assert(event_rule); assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT); - init_ust_event_notifier_from_event_rule(event_rule, &event_notifier); + init_ust_event_from_event_rule(event_rule, &event_notifier.event); event_notifier.event.token = ua_event_notifier_rule->token; + event_notifier.error_counter_index = ua_event_notifier_rule->error_counter_index; /* Create UST event notifier against the tracer. */ pthread_mutex_lock(&app->sock_lock); @@ -2158,13 +2736,13 @@ static int create_ust_event_notifier(struct ust_app *app, } /* Set the capture bytecodes. */ - cond_status = lttng_condition_event_rule_get_capture_descriptor_count( + cond_status = lttng_condition_on_event_get_capture_descriptor_count( condition, &capture_bytecode_count); assert(cond_status == LTTNG_CONDITION_STATUS_OK); for (i = 0; i < capture_bytecode_count; i++) { const struct lttng_bytecode *capture_bytecode = - lttng_condition_event_rule_get_capture_bytecode_at_index( + lttng_condition_on_event_get_capture_bytecode_at_index( condition, i); ret = set_ust_capture(app, capture_bytecode, i, @@ -2841,6 +3419,26 @@ error: return ret; } +/* + * Lookup ust app map for session and disable it on the tracer side. + */ +static +int disable_ust_app_map(struct ust_app_session *ua_sess, + struct ust_app_map *ua_map, struct ust_app *app) +{ + int ret; + + ret = disable_ust_map(app, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + + ua_map->enabled = 0; + +error: + return ret; +} + /* * Lookup ust app channel for session and enable it on the tracer side. This * MUST be called with a RCU read side lock acquired. @@ -2872,6 +3470,37 @@ error: return ret; } +/* + * Lookup ust app map for session and enable it on the tracer side. This + * MUST be called with a RCU read side lock acquired. + */ +static int enable_ust_app_map(struct ust_app_session *ua_sess, + struct ltt_ust_map *umap, struct ust_app *app) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app_map *ua_map; + + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &iter); + ua_map_node = lttng_ht_iter_get_node_str(&iter); + if (ua_map_node == NULL) { + DBG2("Unable to find map %s in ust session id %" PRIu64, + umap->name, ua_sess->tracing_id); + goto error; + } + + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + ret = enable_ust_map(app, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + +error: + return ret; +} + /* * Ask the consumer to create a channel and get it if successful. * @@ -2969,6 +3598,118 @@ error: return ret; } +static int create_map_object(struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, struct ust_app_map *ua_map) +{ + int i, ret, nr_counter_cpu; + struct ustctl_counter_dimension dimension[1] = {0}; + struct ustctl_daemon_counter *daemon_counter; + struct lttng_ust_object_data **counter_cpus; + enum ustctl_counter_bitness bitness; + int *counter_cpu_fds; + + assert(usess); + assert(ua_sess); + assert(ua_map); + assert(ua_map->bucket_count > 0); + + DBG("Creating UST map \"%s\"", ua_map->name); + + if (ua_map->bitness == LTTNG_MAP_BITNESS_32BITS) { + bitness = USTCTL_COUNTER_BITNESS_32; + } else { + bitness = USTCTL_COUNTER_BITNESS_64; + } + + nr_counter_cpu = ustctl_get_nr_cpu_per_counter(); + counter_cpu_fds = zmalloc(nr_counter_cpu * sizeof(*counter_cpu_fds)); + if (!counter_cpu_fds) { + ret = -1; + goto end; + } + + counter_cpus = zmalloc(nr_counter_cpu * sizeof(**counter_cpus)); + if (!counter_cpus) { + ret = -1; + goto free_cpu_fds; + } + + /* Need one fd for each cpu counter of the map. */ + ret = lttng_fd_get(LTTNG_FD_APPS, nr_counter_cpu); + if (ret < 0) { + ERR("Exhausted number of available FD upon create map"); + goto free_cpu_counters; + } + + for (i = 0; i < nr_counter_cpu; i++) { + counter_cpu_fds[i] = shm_create_anonymous("ust-map-counter"); + if (counter_cpu_fds[i] < 0) { + ERR("Error creating anonymous shared memory object"); + ret = -1; + goto error; + } + } + + dimension[0].size = ua_map->bucket_count; + dimension[0].has_underflow = false; + dimension[0].has_overflow = false; + + daemon_counter = ustctl_create_counter(1, dimension, 0, -1, + nr_counter_cpu, counter_cpu_fds, + bitness, + USTCTL_COUNTER_ARITHMETIC_MODULAR, + USTCTL_COUNTER_ALLOC_PER_CPU, + ua_map->coalesce_hits); + assert(daemon_counter); + + DBG("Created daemon counter succesfully"); + + ua_map->map_handle = daemon_counter; + + ret = ustctl_create_counter_data(daemon_counter, &ua_map->obj); + assert(ret == 0); + DBG("Created counter data succesfully"); + + for (i = 0; i < nr_counter_cpu; i++) { + struct ust_app_map_counter *counter; + + /* Create UST counter */ + counter = ust_app_alloc_map_counter(); + if (counter == NULL) { + ret = -ENOMEM; + goto release_counters; + } + + ret = ustctl_create_counter_cpu_data(daemon_counter, i, + &counter->obj); + if (ret < 0) { + ERR("Creating map counter cpu data"); + free(counter); + goto error; + } + + cds_list_add_tail(&counter->list, &ua_map->counters.head); + ua_map->counters.count++; + + DBG2("UST app map counter %d created successfully", + ua_map->counters.count); + } + + ret = 0; + goto end; + +error: +release_counters: + //TODO +free_cpu_counters: + free(counter_cpus); + +free_cpu_fds: + free(counter_cpu_fds); +end: + return ret; +} + /* * Duplicate the ust data object of the ust app stream and save it in the * buffer registry stream. @@ -2983,7 +3724,7 @@ static int duplicate_stream_object(struct buffer_reg_stream *reg_stream, assert(reg_stream); assert(stream); - /* Reserve the amount of file descriptor we need. */ + /* Duplicating a stream requires 2 new fds. Reserve them. */ ret = lttng_fd_get(LTTNG_FD_APPS, 2); if (ret < 0) { ERR("Exhausted number of available FD upon duplicate stream"); @@ -3005,21 +3746,57 @@ error: return ret; } +/* + * Duplicate the ust data object of the ust app map_counter and save it in the + * buffer registry map_counter. + * + * Return 0 on success or else a negative value. + */ +static int duplicate_map_counter_object(struct buffer_reg_map_counter *reg_map_counter, + struct ust_app_map_counter *map_counter) +{ + int ret; + + assert(reg_map_counter); + assert(map_counter); + + /* Duplicating a map_counter requires 2 new fds. Reserve them. */ + ret = lttng_fd_get(LTTNG_FD_APPS, 2); + if (ret < 0) { + ERR("Exhausted number of available FD upon duplicate map_counter"); + goto error; + } + + /* Duplicate object for map_counter once the original is in the registry. */ + ret = ustctl_duplicate_ust_object_data(&map_counter->obj, + reg_map_counter->obj.ust); + if (ret < 0) { + ERR("Duplicate map_counter obj from %p to %p failed with ret %d", + reg_map_counter->obj.ust, map_counter->obj, ret); + lttng_fd_put(LTTNG_FD_APPS, 2); + goto error; + } + map_counter->handle = map_counter->obj->handle; + +error: + return ret; +} + /* * Duplicate the ust data object of the ust app. channel and save it in the * buffer registry channel. * * Return 0 on success or else a negative value. */ -static int duplicate_channel_object(struct buffer_reg_channel *reg_chan, +static int duplicate_channel_object(struct buffer_reg_channel *buf_reg_chan, struct ust_app_channel *ua_chan) { int ret; - assert(reg_chan); + assert(buf_reg_chan); assert(ua_chan); - /* Need two fds for the channel. */ + /* Duplicating a channel requires 1 new fd. Reserve it. */ ret = lttng_fd_get(LTTNG_FD_APPS, 1); if (ret < 0) { ERR("Exhausted number of available FD upon duplicate channel"); @@ -3027,10 +3804,10 @@ static int duplicate_channel_object(struct buffer_reg_channel *reg_chan, } /* Duplicate object for stream once the original is in the registry. */ - ret = ustctl_duplicate_ust_object_data(&ua_chan->obj, reg_chan->obj.ust); + ret = ustctl_duplicate_ust_object_data(&ua_chan->obj, buf_reg_chan->obj.ust); if (ret < 0) { ERR("Duplicate channel obj from %p to %p failed with ret: %d", - reg_chan->obj.ust, ua_chan->obj, ret); + buf_reg_chan->obj.ust, ua_chan->obj, ret); goto error; } ua_chan->handle = ua_chan->obj->handle; @@ -3044,22 +3821,60 @@ error_fd_get: } /* - * For a given channel buffer registry, setup all streams of the given ust - * application channel. + * Duplicate the ust data object of the ust app. map and save it in the + * buffer registry map. * * Return 0 on success or else a negative value. */ -static int setup_buffer_reg_streams(struct buffer_reg_channel *reg_chan, - struct ust_app_channel *ua_chan, - struct ust_app *app) +static int duplicate_map_object(struct buffer_reg_map *buf_reg_map, + struct ust_app_map *ua_map) { - int ret = 0; - struct ust_app_stream *stream, *stmp; + int ret; - assert(reg_chan); - assert(ua_chan); + assert(buf_reg_map); + assert(ua_map); - DBG2("UST app setup buffer registry stream"); + /* Duplicating a map requires 1 new fd. Reserve it. */ + ret = lttng_fd_get(LTTNG_FD_APPS, 1); + if (ret < 0) { + ERR("Exhausted number of available FD upon duplicate map"); + goto error_fd_get; + } + + /* Duplicate object for stream once the original is in the registry. */ + ret = ustctl_duplicate_ust_object_data(&ua_map->obj, buf_reg_map->obj.ust); + if (ret < 0) { + ERR("Duplicate map obj from %p to %p failed with ret: %d", + buf_reg_map->obj.ust, ua_map->obj, ret); + goto error; + } + ua_map->handle = ua_map->obj->handle; + + return 0; + +error: + lttng_fd_put(LTTNG_FD_APPS, 1); +error_fd_get: + return ret; +} + +/* + * For a given channel buffer registry, setup all streams of the given ust + * application channel. + * + * Return 0 on success or else a negative value. + */ +static int setup_buffer_reg_streams(struct buffer_reg_channel *buf_reg_chan, + struct ust_app_channel *ua_chan, + struct ust_app *app) +{ + int ret = 0; + struct ust_app_stream *stream, *stmp; + + assert(buf_reg_chan); + assert(ua_chan); + + DBG2("UST app setup buffer registry stream"); /* Send all streams to application. */ cds_list_for_each_entry_safe(stream, stmp, &ua_chan->streams.head, list) { @@ -3076,7 +3891,7 @@ static int setup_buffer_reg_streams(struct buffer_reg_channel *reg_chan, */ reg_stream->obj.ust = stream->obj; stream->obj = NULL; - buffer_reg_stream_add(reg_stream, reg_chan); + buffer_reg_stream_add(reg_stream, buf_reg_chan); /* We don't need the streams anymore. */ cds_list_del(&stream->list); @@ -3087,6 +3902,50 @@ error: return ret; } +/* + * For a given map buffer registry, setup all counters of the given ust + * application map. + * + * Return 0 on success or else a negative value. + */ +static int setup_buffer_reg_map_counters(struct buffer_reg_map *buf_reg_map, + struct ust_app_map *ua_map, + struct ust_app *app) +{ + int ret = 0; + struct ust_app_map_counter *counter, *stmp; + + assert(buf_reg_map); + assert(ua_map); + + DBG2("UST app setup buffer registry counter"); + + /* Send all counters to application. */ + cds_list_for_each_entry_safe(counter, stmp, &ua_map->counters.head, list) { + struct buffer_reg_map_counter *reg_counter; + + ret = buffer_reg_map_counter_create(®_counter); + if (ret < 0) { + goto error; + } + + /* + * Keep original pointer and nullify it in the counter so the delete + * counter call does not release the object. + */ + reg_counter->obj.ust = counter->obj; + counter->obj = NULL; + buffer_reg_map_counter_add(reg_counter, buf_reg_map); + + /* We don't need the counters anymore. */ + cds_list_del(&counter->list); + delete_ust_app_map_counter(-1, counter, app); + } + +error: + return ret; +} + /* * Create a buffer registry channel for the given session registry and * application channel object. If regp pointer is valid, it's set with the @@ -3099,7 +3958,7 @@ static int create_buffer_reg_channel(struct buffer_reg_session *reg_sess, struct ust_app_channel *ua_chan, struct buffer_reg_channel **regp) { int ret; - struct buffer_reg_channel *reg_chan = NULL; + struct buffer_reg_channel *buf_reg_chan = NULL; assert(reg_sess); assert(ua_chan); @@ -3107,14 +3966,14 @@ static int create_buffer_reg_channel(struct buffer_reg_session *reg_sess, DBG2("UST app creating buffer registry channel for %s", ua_chan->name); /* Create buffer registry channel. */ - ret = buffer_reg_channel_create(ua_chan->tracing_channel_id, ®_chan); + ret = buffer_reg_channel_create(ua_chan->tracing_channel_id, &buf_reg_chan); if (ret < 0) { goto error_create; } - assert(reg_chan); - reg_chan->consumer_key = ua_chan->key; - reg_chan->subbuf_size = ua_chan->attr.subbuf_size; - reg_chan->num_subbuf = ua_chan->attr.num_subbuf; + assert(buf_reg_chan); + buf_reg_chan->consumer_key = ua_chan->key; + buf_reg_chan->subbuf_size = ua_chan->attr.subbuf_size; + buf_reg_chan->num_subbuf = ua_chan->attr.num_subbuf; /* Create and add a channel registry to session. */ ret = ust_registry_channel_add(reg_sess->reg.ust, @@ -3122,17 +3981,63 @@ static int create_buffer_reg_channel(struct buffer_reg_session *reg_sess, if (ret < 0) { goto error; } - buffer_reg_channel_add(reg_sess, reg_chan); + buffer_reg_channel_add(reg_sess, buf_reg_chan); if (regp) { - *regp = reg_chan; + *regp = buf_reg_chan; } return 0; error: /* Safe because the registry channel object was not added to any HT. */ - buffer_reg_channel_destroy(reg_chan, LTTNG_DOMAIN_UST); + buffer_reg_channel_destroy(buf_reg_chan, LTTNG_DOMAIN_UST); +error_create: + return ret; +} + +/* + * Create a buffer registry map for the given session registry and + * application map object. If regp pointer is valid, it's set with the + * created object. Important, the created object is NOT added to the session + * registry hash table. + * + * Return 0 on success else a negative value. + */ +static int create_buffer_reg_map(struct buffer_reg_session *reg_sess, + struct ust_app_map *ua_map, struct buffer_reg_map **regp) +{ + int ret; + struct buffer_reg_map *buf_reg_map = NULL; + + assert(reg_sess); + assert(ua_map); + + DBG2("UST app creating buffer registry map for %s", ua_map->name); + + /* Create buffer registry map. */ + ret = buffer_reg_map_create(ua_map->tracing_map_id, &buf_reg_map); + if (ret < 0) { + goto error_create; + } + assert(buf_reg_map); + + /* Create and add a map registry to session. */ + ret = ust_registry_map_add(reg_sess->reg.ust, ua_map->tracing_map_id); + if (ret < 0) { + goto error; + } + buffer_reg_map_add(reg_sess, buf_reg_map); + + if (regp) { + *regp = buf_reg_map; + } + + return 0; + +error: + /* Safe because the registry map object was not added to any HT. */ + buffer_reg_map_destroy(buf_reg_map, LTTNG_DOMAIN_UST); error_create: return ret; } @@ -3144,32 +4049,70 @@ error_create: * Return 0 on success else a negative value. */ static int setup_buffer_reg_channel(struct buffer_reg_session *reg_sess, - struct ust_app_channel *ua_chan, struct buffer_reg_channel *reg_chan, + struct ust_app_channel *ua_chan, struct buffer_reg_channel *buf_reg_chan, struct ust_app *app) { int ret; assert(reg_sess); - assert(reg_chan); + assert(buf_reg_chan); assert(ua_chan); assert(ua_chan->obj); DBG2("UST app setup buffer registry channel for %s", ua_chan->name); /* Setup all streams for the registry. */ - ret = setup_buffer_reg_streams(reg_chan, ua_chan, app); + ret = setup_buffer_reg_streams(buf_reg_chan, ua_chan, app); if (ret < 0) { goto error; } - reg_chan->obj.ust = ua_chan->obj; + buf_reg_chan->obj.ust = ua_chan->obj; ua_chan->obj = NULL; return 0; error: - buffer_reg_channel_remove(reg_sess, reg_chan); - buffer_reg_channel_destroy(reg_chan, LTTNG_DOMAIN_UST); + buffer_reg_channel_remove(reg_sess, buf_reg_chan); + buffer_reg_channel_destroy(buf_reg_chan, LTTNG_DOMAIN_UST); + return ret; +} + +/* + * Setup buffer registry map for the given session registry and application + * map object. If regp pointer is valid, it's set with the created object. + * + * Return 0 on success else a negative value. + */ +static int setup_buffer_reg_map(struct buffer_reg_session *reg_sess, + struct ust_app_map *ua_map, struct buffer_reg_map *buf_reg_map, + struct ust_app *app) +{ + int ret; + + assert(reg_sess); + assert(buf_reg_map); + assert(ua_map); + assert(ua_map->obj); + + DBG2("UST app setup buffer registry map for %s", ua_map->name); + + /* Setup all counters for the registry. */ + ret = setup_buffer_reg_map_counters(buf_reg_map, ua_map, app); + if (ret < 0) { + goto error; + } + + buf_reg_map->obj.ust = ua_map->obj; + ua_map->obj = NULL; + buf_reg_map->daemon_counter = ua_map->map_handle; + ua_map->map_handle = NULL; + + return 0; + +error: + buffer_reg_map_remove(reg_sess, buf_reg_map); + buffer_reg_map_destroy(buf_reg_map, LTTNG_DOMAIN_UST); return ret; } @@ -3178,21 +4121,21 @@ error: * * Return 0 on success else a negative value. */ -static int send_channel_uid_to_ust(struct buffer_reg_channel *reg_chan, +static int send_channel_uid_to_ust(struct buffer_reg_channel *buf_reg_chan, struct ust_app *app, struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan) { int ret; struct buffer_reg_stream *reg_stream; - assert(reg_chan); + assert(buf_reg_chan); assert(app); assert(ua_sess); assert(ua_chan); DBG("UST app sending buffer registry channel to ust sock %d", app->sock); - ret = duplicate_channel_object(reg_chan, ua_chan); + ret = duplicate_channel_object(buf_reg_chan, ua_chan); if (ret < 0) { goto error; } @@ -3209,8 +4152,8 @@ static int send_channel_uid_to_ust(struct buffer_reg_channel *reg_chan, health_code_update(); /* Send all streams to application. */ - pthread_mutex_lock(®_chan->stream_list_lock); - cds_list_for_each_entry(reg_stream, ®_chan->streams, lnode) { + pthread_mutex_lock(&buf_reg_chan->stream_list_lock); + cds_list_for_each_entry(reg_stream, &buf_reg_chan->streams, lnode) { struct ust_app_stream stream; ret = duplicate_stream_object(reg_stream, &stream); @@ -3236,7 +4179,87 @@ static int send_channel_uid_to_ust(struct buffer_reg_channel *reg_chan, ua_chan->is_sent = 1; error_stream_unlock: - pthread_mutex_unlock(®_chan->stream_list_lock); + pthread_mutex_unlock(&buf_reg_chan->stream_list_lock); +error: + return ret; +} + +/* + * Send buffer registry map to the application. + * + * Return 0 on success else a negative value. + */ +static int send_map_uid_to_ust(struct buffer_reg_map *buf_reg_map, + struct ust_app *app, struct ust_app_session *ua_sess, + struct ust_app_map *ua_map) +{ + int ret; + struct buffer_reg_map_counter *reg_map_counter; + + assert(buf_reg_map); + assert(app); + assert(ua_sess); + assert(ua_map); + + DBG("UST app sending buffer registry map to ust sock %d", app->sock); + + ret = duplicate_map_object(buf_reg_map, ua_map); + if (ret < 0) { + goto error; + } + + /* Send map to the application. */ + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_data_to_ust(app->sock, + ua_sess->handle, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + assert(ret == 0); + if (ret < 0) { + if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) { + ret = -ENOTCONN; /* Caused by app exiting. */ + } + goto error_map_counter_unlock; + } + + ua_map->handle = ua_map->obj->handle; + + health_code_update(); + + /* Send all map_counters to application. */ + pthread_mutex_lock(&buf_reg_map->counter_list_lock); + cds_list_for_each_entry(reg_map_counter, &buf_reg_map->counters, lnode) { + struct ust_app_map_counter map_counter; + + ret = duplicate_map_counter_object(reg_map_counter, &map_counter); + if (ret < 0) { + goto error_map_counter_unlock; + } + + pthread_mutex_lock(&app->sock_lock); + // Do send the per cpu counter here + ret = ustctl_send_counter_cpu_data_to_ust(app->sock, + ua_map->obj, map_counter.obj); + pthread_mutex_unlock(&app->sock_lock); + assert(ret == 0); + if (ret < 0) { + (void) release_ust_app_map_counter(-1, &map_counter, app); + if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) { + ret = -ENOTCONN; /* Caused by app exiting. */ + } + goto error_map_counter_unlock; + } + + /* + * The return value is not important here. This function will + * output an error if needed. + */ + (void) release_ust_app_map_counter(-1, &map_counter, app); + } + ua_map->is_sent = 1; + +error_map_counter_unlock: + pthread_mutex_unlock(&buf_reg_map->counter_list_lock); error: return ret; } @@ -3255,10 +4278,10 @@ static int create_channel_per_uid(struct ust_app *app, { int ret; struct buffer_reg_uid *reg_uid; - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct ltt_session *session = NULL; enum lttng_error_code notification_ret; - struct ust_registry_channel *chan_reg; + struct ust_registry_channel *ust_reg_chan; assert(app); assert(usess); @@ -3275,14 +4298,14 @@ static int create_channel_per_uid(struct ust_app *app, */ assert(reg_uid); - reg_chan = buffer_reg_channel_find(ua_chan->tracing_channel_id, + buf_reg_chan = buffer_reg_channel_find(ua_chan->tracing_channel_id, reg_uid); - if (reg_chan) { + if (buf_reg_chan) { goto send_channel; } /* Create the buffer registry channel object. */ - ret = create_buffer_reg_channel(reg_uid->registry, ua_chan, ®_chan); + ret = create_buffer_reg_channel(reg_uid->registry, ua_chan, &buf_reg_chan); if (ret < 0) { ERR("Error creating the UST channel \"%s\" registry instance", ua_chan->name); @@ -3311,8 +4334,8 @@ static int create_channel_per_uid(struct ust_app *app, */ ust_registry_channel_del_free(reg_uid->registry->reg.ust, ua_chan->tracing_channel_id, false); - buffer_reg_channel_remove(reg_uid->registry, reg_chan); - buffer_reg_channel_destroy(reg_chan, LTTNG_DOMAIN_UST); + buffer_reg_channel_remove(reg_uid->registry, buf_reg_chan); + buffer_reg_channel_destroy(buf_reg_chan, LTTNG_DOMAIN_UST); goto error; } @@ -3320,7 +4343,7 @@ static int create_channel_per_uid(struct ust_app *app, * Setup the streams and add it to the session registry. */ ret = setup_buffer_reg_channel(reg_uid->registry, - ua_chan, reg_chan, app); + ua_chan, buf_reg_chan, app); if (ret < 0) { ERR("Error setting up UST channel \"%s\"", ua_chan->name); goto error; @@ -3328,11 +4351,11 @@ static int create_channel_per_uid(struct ust_app *app, /* Notify the notification subsystem of the channel's creation. */ pthread_mutex_lock(®_uid->registry->reg.ust->lock); - chan_reg = ust_registry_channel_find(reg_uid->registry->reg.ust, + ust_reg_chan = ust_registry_channel_find(reg_uid->registry->reg.ust, ua_chan->tracing_channel_id); - assert(chan_reg); - chan_reg->consumer_key = ua_chan->key; - chan_reg = NULL; + assert(ust_reg_chan); + ust_reg_chan->consumer_key = ua_chan->key; + ust_reg_chan = NULL; pthread_mutex_unlock(®_uid->registry->reg.ust->lock); notification_ret = notification_thread_command_add_channel( @@ -3350,7 +4373,7 @@ static int create_channel_per_uid(struct ust_app *app, send_channel: /* Send buffers to the application. */ - ret = send_channel_uid_to_ust(reg_chan, app, ua_sess, ua_chan); + ret = send_channel_uid_to_ust(buf_reg_chan, app, ua_sess, ua_chan); if (ret < 0) { if (ret != -ENOTCONN) { ERR("Error sending channel to application"); @@ -3365,6 +4388,142 @@ error: return ret; } +/* + * Create and send to the application the created buffers with per UID buffers. + * + * This MUST be called with a RCU read side lock acquired. + * The session list lock and the session's lock must be acquired. + * + * Return 0 on success else a negative value. + */ +static int create_map_per_uid(struct ust_app *app, + struct ltt_ust_session *usess, struct ust_app_session *ua_sess, + struct ust_app_map *ua_map) +{ + int ret; + struct buffer_reg_uid *buffer_reg_uid; + struct buffer_reg_map *buffer_reg_map; + struct ltt_session *session = NULL; + struct ust_registry_map *ust_reg_map; + + assert(app); + assert(usess); + assert(ua_sess); + assert(ua_map); + + DBG("UST app creating map %s with per UID buffers", ua_map->name); + + buffer_reg_uid = buffer_reg_uid_find(usess->id, app->bits_per_long, app->uid); + /* + * The session creation handles the creation of this global registry + * object. If none can be find, there is a code flow problem or a + * teardown race. + */ + assert(buffer_reg_uid); + + buffer_reg_map = buffer_reg_map_find(ua_map->tracing_map_id, + buffer_reg_uid); + if (buffer_reg_map) { + goto send_map; + } + + /* Create the buffer registry map object. */ + ret = create_buffer_reg_map(buffer_reg_uid->registry, ua_map, + &buffer_reg_map); + if (ret < 0) { + ERR("Error creating the UST map \"%s\" registry instance", + ua_map->name); + goto error; + } + + session = session_find_by_id(ua_sess->tracing_id); + assert(session); + assert(pthread_mutex_trylock(&session->lock)); + assert(session_trylock_list()); + + /* + */ + ret = create_map_object(usess, ua_sess, ua_map); + assert(ret == 0); + if (ret < 0) { + ERR("Error creating UST map object: map_name = \"%s\"", ua_map->name); + goto error; + } + + /* + * Setup the streams and add it to the session registry. + */ + ret = setup_buffer_reg_map(buffer_reg_uid->registry, ua_map, + buffer_reg_map, app); + if (ret < 0) { + ERR("Error setting up UST map \"%s\"", ua_map->name); + goto error; + } + + /* Notify the notification subsystem of the map's creation. */ + pthread_mutex_lock(&buffer_reg_uid->registry->reg.ust->lock); + ust_reg_map = ust_registry_map_find(buffer_reg_uid->registry->reg.ust, + ua_map->tracing_map_id); + assert(ust_reg_map); + ust_reg_map = NULL; + pthread_mutex_unlock(&buffer_reg_uid->registry->reg.ust->lock); + +send_map: + /* Send buffers to the application. */ + ret = send_map_uid_to_ust(buffer_reg_map, app, ua_sess, ua_map); + if (ret < 0) { + if (ret != -ENOTCONN) { + ERR("Error sending map to application"); + } + goto error; + } + +error: + if (session) { + session_put(session); + } + return ret; +} + +//static int destroy_map_per_uid(struct ust_app *app, +// struct ltt_ust_session *usess, struct ust_app_session *ua_sess, +// struct ust_app_map *ua_map) +//{ +// int ret; +// struct buffer_reg_uid *buffer_reg_uid; +// struct buffer_reg_map *buffer_reg_map; +// +// assert(app); +// assert(usess); +// assert(ua_sess); +// assert(ua_map); +// +// DBG("UST app destroy map %s with per UID buffers", ua_map->name); +// +// buffer_reg_uid = buffer_reg_uid_find(usess->id, app->bits_per_long, app->uid); +// /* +// * The session creation handles the creation of this global registry +// * object. If none can be find, there is a code flow problem or a +// * teardown race. +// */ +// assert(buffer_reg_uid); +// +// buffer_reg_map = buffer_reg_map_find(ua_map->tracing_map_id, +// buffer_reg_uid); +// if (!buffer_reg_map) { +// ERR("Can't find map in buffer registry: map-name = '%s', uid = %d", +// ua_map->name, app->uid); +// ret = -1; +// goto end; +// } +// +// buffer_reg_map_destroy(buffer_reg_map, LTTNG_DOMAIN_UST); +// +// ret = 0; +//end: +// return ret; +//} + /* * Create and send to the application the created buffers with per PID buffers. * @@ -3382,7 +4541,7 @@ static int create_channel_per_pid(struct ust_app *app, enum lttng_error_code cmd_ret; struct ltt_session *session = NULL; uint64_t chan_reg_key; - struct ust_registry_channel *chan_reg; + struct ust_registry_channel *ust_reg_chan; assert(app); assert(usess); @@ -3431,9 +4590,9 @@ static int create_channel_per_pid(struct ust_app *app, chan_reg_key = ua_chan->key; pthread_mutex_lock(®istry->lock); - chan_reg = ust_registry_channel_find(registry, chan_reg_key); - assert(chan_reg); - chan_reg->consumer_key = ua_chan->key; + ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key); + assert(ust_reg_chan); + ust_reg_chan->consumer_key = ua_chan->key; pthread_mutex_unlock(®istry->lock); cmd_ret = notification_thread_command_add_channel( @@ -3462,54 +4621,133 @@ error: } /* - * From an already allocated ust app channel, create the channel buffers if - * needed and send them to the application. This MUST be called with a RCU read - * side lock acquired. + * Create and send to the application the created buffers with per PID buffers. * * Called with UST app session lock held. + * The session list lock and the session's lock must be acquired. * - * Return 0 on success or else a negative value. Returns -ENOTCONN if - * the application exited concurrently. + * Return 0 on success else a negative value. */ -static int ust_app_channel_send(struct ust_app *app, +static int create_map_per_pid(struct ust_app *app, struct ltt_ust_session *usess, struct ust_app_session *ua_sess, - struct ust_app_channel *ua_chan) + struct ust_app_map *ua_map) { int ret; + struct ust_registry_session *registry; + struct ltt_session *session = NULL; + uint64_t map_reg_key; + struct ust_registry_map *ust_reg_map; assert(app); assert(usess); - assert(usess->active); assert(ua_sess); - assert(ua_chan); + assert(ua_map); - /* Handle buffer type before sending the channel to the application. */ - switch (usess->buffer_type) { - case LTTNG_BUFFER_PER_UID: - { - ret = create_channel_per_uid(app, usess, ua_sess, ua_chan); - if (ret < 0) { - goto error; - } - break; - } - case LTTNG_BUFFER_PER_PID: - { - ret = create_channel_per_pid(app, usess, ua_sess, ua_chan); - if (ret < 0) { - goto error; - } - break; - } - default: - assert(0); - ret = -EINVAL; - goto error; - } + DBG("UST app creating map %s with per PID buffers", ua_map->name); - /* Initialize ust objd object using the received handle and add it. */ - lttng_ht_node_init_ulong(&ua_chan->ust_objd_node, ua_chan->handle); - lttng_ht_add_unique_ulong(app->ust_objd, &ua_chan->ust_objd_node); + rcu_read_lock(); + + registry = get_session_registry(ua_sess); + /* The UST app session lock is held, registry shall not be null. */ + assert(registry); + + /* Create and add a new map registry to session. */ + ret = ust_registry_map_add(registry, ua_map->key); + if (ret < 0) { + ERR("Error creating the UST map \"%s\" registry instance", + ua_map->name); + goto error; + } + + session = session_find_by_id(ua_sess->tracing_id); + assert(session); + + assert(pthread_mutex_trylock(&session->lock)); + assert(session_trylock_list()); + + /* Create and get map. */ + ret = create_map_object(usess, ua_sess, ua_map); + if (ret < 0) { + ERR("Error creating UST map object: map_name = \"%s\" ", + ua_map->name); + goto error_remove_from_registry; + } + + ret = send_map_pid_to_ust(app, ua_sess, ua_map); + if (ret < 0) { + if (ret != -ENOTCONN) { + ERR("Error sending map to application"); + } + goto error_remove_from_registry; + } + + map_reg_key = ua_map->key; + pthread_mutex_lock(®istry->lock); + ust_reg_map = ust_registry_map_find(registry, map_reg_key); + assert(ust_reg_map); + pthread_mutex_unlock(®istry->lock); + +error_remove_from_registry: + if (ret) { + ust_registry_map_del_free(registry, ua_map->key); + } +error: + rcu_read_unlock(); + if (session) { + session_put(session); + } + return ret; +} + +/* + * From an already allocated ust app channel, create the channel buffers if + * needed and send them to the application. This MUST be called with a RCU read + * side lock acquired. + * + * Called with UST app session lock held. + * + * Return 0 on success or else a negative value. Returns -ENOTCONN if + * the application exited concurrently. + */ +static int ust_app_channel_send(struct ust_app *app, + struct ltt_ust_session *usess, struct ust_app_session *ua_sess, + struct ust_app_channel *ua_chan) +{ + int ret; + + assert(app); + assert(usess); + assert(usess->active); + assert(ua_sess); + assert(ua_chan); + + /* Handle buffer type before sending the channel to the application. */ + switch (usess->buffer_type) { + case LTTNG_BUFFER_PER_UID: + { + ret = create_channel_per_uid(app, usess, ua_sess, ua_chan); + if (ret < 0) { + goto error; + } + break; + } + case LTTNG_BUFFER_PER_PID: + { + ret = create_channel_per_pid(app, usess, ua_sess, ua_chan); + if (ret < 0) { + goto error; + } + break; + } + default: + assert(0); + ret = -EINVAL; + goto error; + } + + /* Initialize ust objd object using the received handle and add it. */ + lttng_ht_node_init_ulong(&ua_chan->ust_objd_node, ua_chan->handle); + lttng_ht_add_unique_ulong(app->ust_chan_objd, &ua_chan->ust_objd_node); /* If channel is not enabled, disable it on the tracer */ if (!ua_chan->enabled) { @@ -3523,6 +4761,68 @@ error: return ret; } +/* + * From an already allocated ust app map, create the map buffers if + * needed and send them to the application. This MUST be called with a RCU read + * side lock acquired. + * + * Called with UST app session lock held. + * + * Return 0 on success or else a negative value. Returns -ENOTCONN if + * the application exited concurrently. + */ +static int ust_app_map_send(struct ust_app *app, + struct ltt_ust_session *usess, struct ust_app_session *ua_sess, + struct ust_app_map *ua_map) +{ + int ret; + + assert(app); + assert(usess); + assert(usess->active); + assert(ua_sess); + assert(ua_map); + + /* Handle buffer type before sending the map to the application. */ + switch (usess->buffer_type) { + case LTTNG_BUFFER_PER_UID: + { + ret = create_map_per_uid(app, usess, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + break; + } + case LTTNG_BUFFER_PER_PID: + { + ret = create_map_per_pid(app, usess, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + break; + } + default: + assert(0); + ret = -EINVAL; + goto error; + } + + /* Initialize ust objd object using the received handle and add it. */ + lttng_ht_node_init_ulong(&ua_map->ust_objd_node, ua_map->handle); + lttng_ht_add_unique_ulong(app->ust_map_objd, &ua_map->ust_objd_node); + + /* If map is not enabled, disable it on the tracer */ + if (!ua_map->enabled) { + ret = disable_ust_map(app, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + } + +error: + return ret; +} + /* * Create UST app channel and return it through ua_chanp if not NULL. * @@ -3573,6 +4873,63 @@ error: return ret; } +/* + * Create UST app map and return it through ua_mapp if not NULL. + * + * Called with UST app session lock and RCU read-side lock held. + * + * Return 0 on success or else a negative value. + */ +static int ust_app_map_allocate(struct ust_app_session *ua_sess, + struct ltt_ust_map *umap, + enum lttng_ust_chan_type type, struct ltt_ust_session *usess, + struct ust_app_map **ua_mapp) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app_map *ua_map; + + DBG("Allocating map id = %"PRIu64, umap->id); + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &iter); + ua_map_node = lttng_ht_iter_get_node_str(&iter); + if (ua_map_node != NULL) { + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + goto end; + } + + ua_map = alloc_ust_app_map(umap->name, ua_sess); + if (ua_map == NULL) { + /* Only malloc can fail here */ + ret = -ENOMEM; + goto error; + } + //shadow_copy_map(ua_map, umap); + ua_map->tracing_map_id = umap->id; + ua_map->coalesce_hits = umap->coalesce_hits; + ua_map->dead_app_kv_values = &umap->dead_app_kv_values; + ua_map->bitness = umap->bitness; + + /* Set map type. */ + //ua_map->attr.type = type; + ua_map->bucket_count = umap->bucket_count; + + /* Only add the map if successful on the tracer side. */ + lttng_ht_add_unique_str(ua_sess->maps, &ua_map->node); +end: + if (ua_mapp) { + *ua_mapp = ua_map; + } + + /* Everything went well. */ + return 0; + +error: + return ret; +} + /* * Create UST app event and create it on the tracer side. * @@ -3580,7 +4937,7 @@ error: * Called with ust app session mutex held. */ static -int create_ust_app_event(struct ust_app_session *ua_sess, +int create_ust_app_channel_event(struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan, struct ltt_ust_event *uevent, struct ust_app *app) { @@ -3596,7 +4953,7 @@ int create_ust_app_event(struct ust_app_session *ua_sess, shadow_copy_event(ua_event, uevent); /* Create it on the tracer side */ - ret = create_ust_event(app, ua_sess, ua_chan, ua_event); + ret = create_ust_channel_event(app, ua_sess, ua_chan, ua_event); if (ret < 0) { /* * Not found previously means that it does not exist on the @@ -3614,7 +4971,7 @@ int create_ust_app_event(struct ust_app_session *ua_sess, goto error; } - add_unique_ust_app_event(ua_chan, ua_event); + add_unique_ust_app_event(ua_chan->events, ua_event); DBG2("UST app create event completed: app = '%s' (ppid: %d)", app->name, app->ppid); @@ -3628,6 +4985,86 @@ error: return ret; } +/* + * Create UST app event and create it on the tracer side. + * + * Must be called with the RCU read side lock held. + * Called with ust app session mutex held. + */ +static +int create_ust_app_map_event(struct ust_app_session *ua_sess, + struct ust_app_map *ua_map, struct ltt_ust_event *uevent, + struct ust_app *app) +{ + int ret = 0; + uint64_t map_reg_key; + struct ust_app_event *ua_event; + struct ust_registry_session *registry; + + ua_event = alloc_ust_app_event(uevent->attr.name, &uevent->attr); + if (ua_event == NULL) { + /* Only failure mode of alloc_ust_app_event(). */ + ret = -ENOMEM; + goto end; + } + shadow_copy_event(ua_event, uevent); + + registry = get_session_registry(ua_sess); + if (!registry) { + DBG("Application session is being torn down. Abort event notify"); + ret = 0; + goto error; + } + + if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) { + map_reg_key = ua_map->tracing_map_id; + } else { + map_reg_key = ua_map->key; + } + + pthread_mutex_lock(®istry->lock); + ret = ust_registry_map_add_token_key_mapping(registry, map_reg_key, + uevent->attr.token, uevent->key); + assert(ret == 0); + pthread_mutex_unlock(®istry->lock); + + /* Create it on the tracer side */ + ret = create_ust_map_event(app, ua_sess, ua_map, uevent->key, ua_event); + if (ret < 0) { + /* + * Not found previously means that it does not exist on the + * tracer. If the application reports that the event existed, + * it means there is a bug in the sessiond or lttng-ust + * (or corruption, etc.) + */ + if (ret == -LTTNG_UST_ERR_EXIST) { + ERR("Tracer for application reported that an event being created already existed: " + "event_name = \"%s\", pid = %d, ppid = %d, uid = %d, gid = %d", + uevent->attr.name, + app->pid, app->ppid, app->uid, + app->gid); + } + + /* + * FIXME: frdeso: remove key from tokey->key mapping. + */ + goto error; + } + + add_unique_ust_app_event(ua_map->events, ua_event); + + DBG2("UST app create event completed: app = '%s', tracer token = %"PRIu64" (ppid: %d)", + app->name, uevent->attr.token, app->ppid); + +end: + return ret; + +error: + /* Valid. Calling here is already in a read side lock */ + delete_ust_app_event(-1, ua_event, app); + return ret; +} + /* * Create UST app event notifier rule and create it on the tracer side. * @@ -3672,12 +5109,12 @@ int create_ust_app_event_notifier_rule(struct lttng_trigger *trigger, DBG2("UST app create token event rule completed: app = '%s' (ppid: %d), token = %" PRIu64, app->name, app->ppid, lttng_trigger_get_tracer_token(trigger)); -end: - return ret; + goto end; error: /* The RCU read side lock is already being held by the caller. */ delete_ust_app_event_notifier_rule(-1, ua_event_notifier_rule, app); +end: return ret; } @@ -3885,7 +5322,8 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) lta->v_major = msg->major; lta->v_minor = msg->minor; lta->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_U64); - lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + lta->ust_chan_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + lta->ust_map_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); lta->ust_sessions_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); lta->notify_sock = -1; lta->token_to_event_notifier_rule_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); @@ -3992,6 +5430,7 @@ int ust_app_setup_event_notifier_group(struct ust_app *app) int event_pipe_write_fd; struct lttng_ust_object_data *event_notifier_group = NULL; enum lttng_error_code lttng_ret; + enum event_notifier_error_accounting_status event_notifier_error_accounting_status; assert(app); @@ -4040,6 +5479,14 @@ int ust_app_setup_event_notifier_group(struct ust_app *app) /* Assign handle only when the complete setup is valid. */ app->event_notifier_group.object = event_notifier_group; + + event_notifier_error_accounting_status = event_notifier_error_accounting_register_app(app); + if (event_notifier_error_accounting_status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Failed to setup event notifier error accounting for app"); + ret = -1; + goto error; + } + return ret; error: @@ -4575,24 +6022,27 @@ int ust_app_disable_channel_glb(struct ltt_ust_session *usess, } /* - * For a specific UST session, enable the channel for all registered apps. + * For a specific UST session, disable the channel for all registered apps. */ -int ust_app_enable_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) +int ust_app_disable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) { int ret = 0; struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_map_node; struct ust_app *app; struct ust_app_session *ua_sess; + struct ust_app_map *ua_map; assert(usess->active); - DBG2("UST app enabling channel %s to global domain for session id %" PRIu64, - uchan->name, usess->id); + DBG2("UST app disabling map %s from global domain for session id %" PRIu64, + umap->name, usess->id); rcu_read_lock(); /* For every registered applications */ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + struct lttng_ht_iter uiter; if (!app->compatible) { /* * TODO: In time, we should notice the caller of this error by @@ -4605,8 +6055,104 @@ int ust_app_enable_channel_glb(struct ltt_ust_session *usess, continue; } - /* Enable channel onto application */ - ret = enable_ust_app_channel(ua_sess, uchan, app); + /* Get map */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter); + ua_map_node = lttng_ht_iter_get_node_str(&uiter); + /* If the session if found for the app, the map must be there */ + assert(ua_map_node); + + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + /* The map must not be already disabled */ + assert(ua_map->enabled == 1); + + /* Disable map onto application */ + ret = disable_ust_app_map(ua_sess, ua_map, app); + if (ret < 0) { + /* XXX: We might want to report this error at some point... */ + continue; + } + } + + rcu_read_unlock(); + return ret; +} + +/* + * For a specific UST session, enable the channel for all registered apps. + */ +int ust_app_enable_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app *app; + struct ust_app_session *ua_sess; + + assert(usess->active); + DBG2("UST app enabling channel %s to global domain for session id %" PRIu64, + uchan->name, usess->id); + + rcu_read_lock(); + + /* For every registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + continue; + } + + /* Enable channel onto application */ + ret = enable_ust_app_channel(ua_sess, uchan, app); + if (ret < 0) { + /* XXX: We might want to report this error at some point... */ + continue; + } + } + + rcu_read_unlock(); + return ret; +} + +/* + * For a specific UST session, enable the map for all registered apps. + */ +int ust_app_enable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app *app; + struct ust_app_session *ua_sess; + + assert(usess->active); + DBG2("UST app enabling map %s to global domain for session id %" PRIu64, + umap->name, usess->id); + + rcu_read_lock(); + + /* For every registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + continue; + } + + /* Enable map onto application */ + ret = enable_ust_app_map(ua_sess, umap, app); if (ret < 0) { /* XXX: We might want to report this error at some point... */ continue; @@ -4620,7 +6166,7 @@ int ust_app_enable_channel_glb(struct ltt_ust_session *usess, /* * Disable an event in a channel and for a specific session. */ -int ust_app_disable_event_glb(struct ltt_ust_session *usess, +int ust_app_disable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { int ret = 0; @@ -4665,7 +6211,7 @@ int ust_app_disable_event_glb(struct ltt_ust_session *usess, ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name, uevent->filter, uevent->attr.loglevel, - uevent->exclusion); + uevent->exclusion, uevent->attr.token); if (ua_event == NULL) { DBG2("Event %s not found in channel %s for app pid %d." "Skipping", uevent->attr.name, uchan->name, app->pid); @@ -4683,6 +6229,72 @@ int ust_app_disable_event_glb(struct ltt_ust_session *usess, return ret; } +/* + * Disable an event in a map and for a specific session. + */ +int ust_app_disable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_map *ua_map; + struct ust_app_event *ua_event; + + assert(usess->active); + DBG("UST app disabling event %s for all apps in map " + "%s for session id %" PRIu64, + uevent->attr.name, umap->name, usess->id); + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + /* Next app */ + continue; + } + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter); + ua_map_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_map_node == NULL) { + DBG2("map %s not found in session id %" PRIu64 " for app pid %d." + "Skipping", umap->name, usess->id, app->pid); + continue; + } + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + ua_event = find_ust_app_event(ua_map->events, uevent->attr.name, + uevent->filter, uevent->attr.loglevel, + uevent->exclusion, uevent->attr.token); + if (ua_event == NULL) { + DBG2("Event %s not found in map %s for app pid %d." + "Skipping", uevent->attr.name, umap->name, app->pid); + continue; + } + + ret = disable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + /* XXX: Report error someday... */ + continue; + } + } + + rcu_read_unlock(); + return ret; +} + /* The ua_sess lock must be held by the caller. */ static int ust_app_channel_create(struct ltt_ust_session *usess, @@ -4759,10 +6371,67 @@ error: return ret; } +/* The ua_sess lock must be held by the caller. */ +static +int ust_app_map_create(struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, + struct ltt_ust_map *umap, struct ust_app *app, + struct ust_app_map **_ua_map) +{ + int ret = 0; + struct ust_app_map *ua_map = NULL; + + assert(ua_sess); + ASSERT_LOCKED(ua_sess->lock); + + /* + * Create map onto application and synchronize its + * configuration. + */ + ret = ust_app_map_allocate(ua_sess, umap, + LTTNG_UST_CHAN_PER_CPU, usess, + &ua_map); + if (ret < 0) { + goto error; + } + + ret = ust_app_map_send(app, usess, ua_sess, ua_map); + if (ret) { + goto error; + } + +error: + if (ret < 0) { + switch (ret) { + case -ENOTCONN: + /* + * The application's socket is not valid. Either a bad socket + * or a timeout on it. We can't inform the caller that for a + * specific app, the session failed so lets continue here. + */ + ret = 0; /* Not an error. */ + break; + case -ENOMEM: + default: + break; + } + } + + if (ret == 0 && _ua_map) { + /* + * Only return the application's map on success. Note + * that the map can still be part of the application's + * map hashtable on error. + */ + *_ua_map = ua_map; + } + return ret; +} + /* * Enable event for a specific session and channel on the tracer. */ -int ust_app_enable_event_glb(struct ltt_ust_session *usess, +int ust_app_enable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { int ret = 0; @@ -4800,68 +6469,747 @@ int ust_app_enable_event_glb(struct ltt_ust_session *usess, continue; } - pthread_mutex_lock(&ua_sess->lock); + pthread_mutex_lock(&ua_sess->lock); + + if (ua_sess->deleted) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + /* + * It is possible that the channel cannot be found is + * the channel/event creation occurs concurrently with + * an application exit. + */ + if (!ua_chan_node) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + /* Get event node */ + ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name, + uevent->filter, uevent->attr.loglevel, uevent->exclusion, + uevent->attr.token); + if (ua_event == NULL) { + DBG3("UST app enable event %s not found for app PID %d." + "Skipping app", uevent->attr.name, app->pid); + goto next_app; + } + + ret = enable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + pthread_mutex_unlock(&ua_sess->lock); + goto error; + } + next_app: + pthread_mutex_unlock(&ua_sess->lock); + } + +error: + rcu_read_unlock(); + return ret; +} + +/* + * Enable event for a specific session and map on the tracer. + */ +int ust_app_enable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_map *ua_map; + struct ust_app_event *ua_event; + + assert(usess->active); + DBG("UST app enabling event %s for all apps for session id %" PRIu64, + uevent->attr.name, usess->id); + + /* + * NOTE: At this point, this function is called only if the session and + * map passed are already created for all apps. and enabled on the + * tracer also. + */ + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (!ua_sess) { + /* The application has problem or is probably dead. */ + continue; + } + + pthread_mutex_lock(&ua_sess->lock); + + if (ua_sess->deleted) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter); + ua_map_node = lttng_ht_iter_get_node_str(&uiter); + /* + * It is possible that the map cannot be found is + * the map/event creation occurs concurrently with + * an application exit. + */ + if (!ua_map_node) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + /* Get event node */ + ua_event = find_ust_app_event(ua_map->events, uevent->attr.name, + uevent->filter, uevent->attr.loglevel, uevent->exclusion, + uevent->attr.token); + if (ua_event == NULL) { + DBG3("UST app enable event %s not found for app PID %d." + "Skipping app", uevent->attr.name, app->pid); + goto next_app; + } + + ret = enable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + pthread_mutex_unlock(&ua_sess->lock); + goto error; + } + next_app: + pthread_mutex_unlock(&ua_sess->lock); + } + +error: + rcu_read_unlock(); + return ret; +} + +/* + * For a specific existing UST session and UST channel, creates the event for + * all registered apps. + */ +int ust_app_create_channel_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *ua_chan_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + + assert(usess->active); + DBG("UST app creating event %s for all apps for session id %" PRIu64, + uevent->attr.name, usess->id); + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (!ua_sess) { + /* The application has problem or is probably dead. */ + continue; + } + + pthread_mutex_lock(&ua_sess->lock); + + if (ua_sess->deleted) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + /* If the channel is not found, there is a code flow error */ + assert(ua_chan_node); + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + ret = create_ust_app_channel_event(ua_sess, ua_chan, uevent, app); + pthread_mutex_unlock(&ua_sess->lock); + if (ret < 0) { + if (ret != -LTTNG_UST_ERR_EXIST) { + /* Possible value at this point: -ENOMEM. If so, we stop! */ + break; + } + DBG2("UST app event %s already exist on app PID %d", + uevent->attr.name, app->pid); + continue; + } + } + + rcu_read_unlock(); + return ret; +} + +static +int snapshot_key_values(struct ustctl_daemon_counter *map_handle, + struct lttng_ht *key_to_bucket_index_ht, int cpu, + const char *key_filter, struct lttng_ht *values) +{ + int ret; + struct lttng_ht_iter key_iter; + struct ust_registry_map_index_ht_entry *map_index_entry; + + /* Iterate over all the formated_key -> counter index */ + cds_lfht_for_each_entry(key_to_bucket_index_ht->ht, + &key_iter.iter, map_index_entry, node.node) { + bool overflow = 0, underflow = 0; + int64_t local_value = 0; + size_t dimension_indexes[1] = {map_index_entry->index}; + + if (key_filter && strcmp(key_filter, + map_index_entry->formated_key) != 0) { + continue; + } + + ret = ustctl_counter_read(map_handle, + dimension_indexes, cpu, &local_value, + &overflow, &underflow); + if (ret) { + ERR("Error getting counter value from the tracer: key = '%s'", + map_index_entry->formated_key); + ret = -1; + goto end; + } + + map_add_or_increment_map_values(values, + map_index_entry->formated_key, local_value, + underflow, overflow); + } + ret = 0; +end: + return ret; +} + +static +int ust_app_map_list_values_per_uid_with_bitness_and_cpu( + const struct ltt_ust_session *usess, + const struct ltt_ust_map *umap, + uint32_t app_bitness, + uint32_t cpu, + const char *key_filter, + struct lttng_ht *values) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct buffer_reg_uid *buf_reg_uid; + struct buffer_reg_map *buf_reg_map; + struct ust_registry_session *ust_reg_sess; + struct lttng_ht_node_u64 *ust_reg_map_node; + struct ust_registry_map *ust_reg_map; + + buf_reg_uid = buffer_reg_uid_find(usess->id, app_bitness, usess->uid); + if (!buf_reg_uid) { + /* + * Buffer registry entry for uid not found. Probably no app for + * this UID at the moment. + */ + DBG("No buffer registry entry found for uid: ust-sess-id = %"PRIu64", bitness = %"PRIu32", uid = %d", + usess->id, app_bitness, usess->uid); + /* + * Not an error. Leave the key value pair unchanged and return. + */ + ret = 0; + goto end; + } + + buf_reg_map = buffer_reg_map_find(umap->id, buf_reg_uid); + if (!buf_reg_uid) { + ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64, + umap->id); + ret = -1; + goto end; + } + + ust_reg_sess = buf_reg_uid->registry->reg.ust; + + /* Get the ust_reg map object from the registry */ + // FIXME: frdeso: This can be changed to ust_registry_map_find() right? + + lttng_ht_lookup(ust_reg_sess->maps, (void *) &umap->id, &iter); + ust_reg_map_node = lttng_ht_iter_get_node_u64(&iter); + if (!ust_reg_map_node) { + ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64, + umap->id); + ret = -1; + goto end; + } + ust_reg_map = caa_container_of(ust_reg_map_node, + struct ust_registry_map, node); + + ret = snapshot_key_values(buf_reg_map->daemon_counter, + ust_reg_map->key_string_to_bucket_index_ht, + cpu, key_filter, values); + if (ret) { + abort(); + } + + + ret = 0; +end: + return ret; +} + +static +int ust_app_map_list_values_per_uid(const struct ltt_ust_session *usess, + const struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content *map_content) +{ + int i, ret = 0; + enum lttng_map_query_status map_query_status; + const char *key_filter; + struct lttng_ht *values = NULL; + bool sum_cpus = lttng_map_query_get_config_sum_by_cpu(query); + enum lttng_map_query_config_buffer config_buffer; + enum lttng_map_query_config_cpu config_cpu; + int selected_cpu; + + map_query_status = lttng_map_query_get_key_filter(query, &key_filter); + if (map_query_status == LTTNG_MAP_QUERY_STATUS_NONE) { + key_filter = NULL; + } else if (map_query_status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + + config_cpu = lttng_map_query_get_config_cpu(query); + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int count; + map_query_status = lttng_map_query_get_cpu_count(query, &count); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + assert(count == 1); + + map_query_status = lttng_map_query_get_cpu_at_index(query, 0, + &selected_cpu); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + config_buffer = lttng_map_query_get_config_buffer(query); + if (config_buffer == LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET) { + unsigned int count; + uid_t selected_uid; + + map_query_status = lttng_map_query_get_uid_count(query, &count); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + assert(count == 1); + + map_query_status = lttng_map_query_get_uid_at_index(query, 0, + &selected_uid); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + + if (selected_uid != usess->uid) { + ret = 0; + goto end; + } + } + + if (sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + for (i = 0; i < umap->nr_cpu; i++) { + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + if (selected_cpu != i) { + continue; + } + } + + if (!sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + ret = ust_app_map_list_values_per_uid_with_bitness_and_cpu( + usess, umap, 32, i, key_filter, + values); + if (ret) { + abort(); + } + + ret = ust_app_map_list_values_per_uid_with_bitness_and_cpu( + usess, umap, 64, i, key_filter, + values); + if (ret) { + abort(); + } + if (!sum_cpus) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID, + sum_cpus, usess->uid, i, values); + if (ret) { + abort(); + } + + lttng_ht_destroy(values); + } + } + + if (sum_cpus) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID, + sum_cpus, usess->uid, 0, values); + if (ret) { + abort(); + } + lttng_ht_destroy(values); + } + +end: + return ret; +} + +static +int append_dead_app_kv(struct ltt_ust_map *umap, + const char *key_filter, + struct lttng_map_content *map_content) +{ + int ret; + struct lttng_ht *dead_app_kv_ht; + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_iter key_iter; + + struct lttng_ht *values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + + pthread_mutex_lock(&(umap->dead_app_kv_values.lock)); + + assert(umap->dead_app_kv_values.dead_app_kv_values_64bits); + dead_app_kv_ht = umap->dead_app_kv_values.dead_app_kv_values_64bits; + + cds_lfht_for_each_entry(dead_app_kv_ht->ht, &key_iter.iter, kv_entry, + node.node) { + if (key_filter && strcmp(key_filter, kv_entry->key) != 0) { + continue; + } + map_add_or_increment_map_values(values, kv_entry->key, + kv_entry->value, kv_entry->has_underflowed, + kv_entry->has_overflowed); + } + + assert(umap->dead_app_kv_values.dead_app_kv_values_32bits); + + dead_app_kv_ht = umap->dead_app_kv_values.dead_app_kv_values_32bits; + cds_lfht_for_each_entry(dead_app_kv_ht->ht, &key_iter.iter, kv_entry, + node.node) { + if (key_filter && strcmp(key_filter, kv_entry->key) != 0) { + continue; + } + map_add_or_increment_map_values(values, kv_entry->key, + kv_entry->value, kv_entry->has_underflowed, + kv_entry->has_overflowed); + } + + pthread_mutex_unlock(&umap->dead_app_kv_values.lock); + + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED, + true, 0, 0, values); + + lttng_ht_destroy(values); + if (ret) { + ERR("Error appending deadapp kv"); + goto end; + } + + + ret = 0; + +end: + return ret; +} + +static +int ust_app_map_list_values_per_pid_with_bitness_and_cpu( + const struct ltt_ust_session *usess, + struct ust_app *app, + struct ltt_ust_map *umap, + uint32_t app_bitness, + uint32_t cpu, + const char *key_filter, + struct lttng_ht *values) +{ + int ret = 0; + + struct lttng_ht_iter map_iter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app_map *ua_map; + struct ust_app_session *ua_sess; + struct ust_registry_session *ust_reg_sess; + struct ust_registry_map *ust_reg_map; + + if (app->bits_per_long != app_bitness) { + ret = 0; + goto end;; + } + + ua_sess = lookup_session_by_app(usess, app); + if (!ua_sess) { + /* Session not associated with this app. */ + ret = 0; + goto end;; + } + + ust_reg_sess = get_session_registry(ua_sess); + if (!ust_reg_sess) { + DBG("Application session is being torn down. Skip application."); + ret = 0; + goto end;; + } + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &map_iter); + ua_map_node = lttng_ht_iter_get_node_str(&map_iter); + + assert(ua_map_node != NULL); + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + pthread_mutex_lock(&ust_reg_sess->lock); + ust_reg_map = ust_registry_map_find(ust_reg_sess, ua_map->key); + pthread_mutex_unlock(&ust_reg_sess->lock); + assert(ust_reg_map); + + ret = snapshot_key_values(ua_map->map_handle, + ust_reg_map->key_string_to_bucket_index_ht, + cpu, key_filter, values); + if (ret) { + ERR("Error snapshoting the content of map"); + goto end; + } + +end: + return ret; +} + +static +int ust_app_map_list_values_per_pid(const struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content *map_content) +{ + enum lttng_map_query_status map_query_status; + const char *key_filter; + struct lttng_ht *values; + bool sum_cpus = lttng_map_query_get_config_sum_by_cpu(query); + bool sum_pids = lttng_map_query_get_config_sum_by_pid(query); + enum lttng_map_query_config_cpu config_cpu; + int selected_cpu, i, ret = 0; + struct lttng_ht_iter app_iter; + struct ust_app *app; + + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + + map_query_status = lttng_map_query_get_key_filter(query, &key_filter); + if (map_query_status == LTTNG_MAP_QUERY_STATUS_NONE) { + key_filter = NULL; + } else if (map_query_status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + + config_cpu = lttng_map_query_get_config_cpu(query); + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int count; + map_query_status = lttng_map_query_get_cpu_count(query, &count); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + assert(count == 1); + + map_query_status = lttng_map_query_get_cpu_at_index(query, 0, + &selected_cpu); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + /* Sum all cpus and pids on the same table. */ + if (sum_cpus && sum_pids) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + if (!sum_cpus && sum_pids) { + /* Iterate over all currently registered apps. */ + for (i = 0; i < umap->nr_cpu; i++) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + cds_lfht_for_each_entry(ust_app_ht->ht, &app_iter.iter, app, pid_n.node) { + ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu( + usess, app, umap, 32, i, key_filter, values); + if (ret) { + abort(); + } + ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu( + usess, app, umap, 64, i, key_filter, values); + if (ret) { + abort(); + } + } + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + sum_cpus, app->pid, i, values); + if (ret) { + abort(); + } + + lttng_ht_destroy(values); + } + } else { + /* Iterate over all currently registered apps. */ + cds_lfht_for_each_entry(ust_app_ht->ht, &app_iter.iter, app, pid_n.node) { + + if (sum_cpus && !sum_pids) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + for (i = 0; i < umap->nr_cpu; i++) { + + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + if (selected_cpu != i) { + continue; + } + } + + if (!sum_cpus && !sum_pids) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu( + usess, app, umap, 32, i, key_filter, values); + if (ret) { + abort(); + } + ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu( + usess, app, umap, 64, i, key_filter, values); + if (ret) { + abort(); + } + + if (!sum_cpus && !sum_pids) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + sum_cpus, app->pid, i, values); + if (ret) { + abort(); + } - if (ua_sess->deleted) { - pthread_mutex_unlock(&ua_sess->lock); - continue; + lttng_ht_destroy(values); + } + } + if (sum_cpus && !sum_pids) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + sum_cpus, app->pid, i, values); + if (ret) { + abort(); + } + + lttng_ht_destroy(values); + } } + } - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - /* - * It is possible that the channel cannot be found is - * the channel/event creation occurs concurrently with - * an application exit. - */ - if (!ua_chan_node) { - pthread_mutex_unlock(&ua_sess->lock); - continue; + if (sum_cpus && sum_pids) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + sum_cpus, 0, 0, values); + if (ret) { + abort(); } + lttng_ht_destroy(values); + } - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + /* Append dead app aggregated key-value pairs. */ + ret = append_dead_app_kv(umap, key_filter, map_content); + if (ret) { + ERR("Error appending values from dead apps map"); + goto end; + } - /* Get event node */ - ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name, - uevent->filter, uevent->attr.loglevel, uevent->exclusion); - if (ua_event == NULL) { - DBG3("UST app enable event %s not found for app PID %d." - "Skipping app", uevent->attr.name, app->pid); - goto next_app; - } +end: + return ret; +} - ret = enable_ust_app_event(ua_sess, ua_event, app); - if (ret < 0) { - pthread_mutex_unlock(&ua_sess->lock); - goto error; +int ust_app_map_list_values(const struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content **map_content) +{ + int ret; + struct lttng_map_content *local_map_content = NULL; + + local_map_content = lttng_map_content_create(usess->buffer_type); + if (!local_map_content) { + ERR("Error creating a map content list"); + ret = -1; + goto end; + } + rcu_read_lock(); + if (usess->buffer_type == LTTNG_BUFFER_PER_UID) { + ret = ust_app_map_list_values_per_uid(usess, umap, query, + local_map_content); + if (ret) { + ERR("Error adding per-uid map value"); + ret = -1; + goto end; + } + } else { + ret = ust_app_map_list_values_per_pid(usess, umap, query, + local_map_content); + if (ret) { + ERR("Error adding per-pid map value"); + ret = -1; + goto end; } - next_app: - pthread_mutex_unlock(&ua_sess->lock); } - -error: + *map_content = local_map_content; + local_map_content = NULL; + ret = 0; +end: rcu_read_unlock(); + + lttng_map_content_destroy(local_map_content); return ret; } /* - * For a specific existing UST session and UST channel, creates the event for + * For a specific existing UST session and UST map, creates the event for * all registered apps. */ -int ust_app_create_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +int ust_app_create_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) { int ret = 0; struct lttng_ht_iter iter, uiter; - struct lttng_ht_node_str *ua_chan_node; + struct lttng_ht_node_str *ua_map_node; struct ust_app *app; struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; + struct ust_app_map *ua_map; assert(usess->active); - DBG("UST app creating event %s for all apps for session id %" PRIu64, - uevent->attr.name, usess->id); + DBG("UST app creating event %s in map %s for all apps for session id %" PRIu64, + uevent->attr.name, umap->name, usess->id); rcu_read_lock(); @@ -4887,15 +7235,16 @@ int ust_app_create_event_glb(struct ltt_ust_session *usess, continue; } - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - /* If the channel is not found, there is a code flow error */ - assert(ua_chan_node); + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter); + ua_map_node = lttng_ht_iter_get_node_str(&uiter); + /* If the map is not found, there is a code flow error */ + assert(ua_map_node); - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); - ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); + ret = create_ust_app_map_event(ua_sess, ua_map, uevent, app); + assert(!ret); pthread_mutex_unlock(&ua_sess->lock); if (ret < 0) { if (ret != -LTTNG_UST_ERR_EXIST) { @@ -5190,7 +7539,7 @@ int ust_app_flush_session(struct ltt_ust_session *usess) /* Flush all per UID buffers associated to that session. */ cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { struct ust_registry_session *ust_session_reg; - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; /* Get consumer socket to use to push the metadata.*/ @@ -5202,13 +7551,13 @@ int ust_app_flush_session(struct ltt_ust_session *usess) } cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { + buf_reg_chan, node.node) { /* * The following call will print error values so the return * code is of little importance because whatever happens, we * have to try them all. */ - (void) consumer_flush_channel(socket, reg_chan->consumer_key); + (void) consumer_flush_channel(socket, buf_reg_chan->consumer_key); } ust_session_reg = reg->registry->reg.ust; @@ -5337,7 +7686,7 @@ int ust_app_clear_quiescent_session(struct ltt_ust_session *usess) */ cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { struct consumer_socket *socket; - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; /* Get associated consumer socket.*/ socket = consumer_find_socket_by_bitness( @@ -5351,7 +7700,7 @@ int ust_app_clear_quiescent_session(struct ltt_ust_session *usess) } cds_lfht_for_each_entry(reg->registry->channels->ht, - &iter.iter, reg_chan, node.node) { + &iter.iter, buf_reg_chan, node.node) { /* * The following call will print error values so * the return code is of little importance @@ -5359,7 +7708,7 @@ int ust_app_clear_quiescent_session(struct ltt_ust_session *usess) * all. */ (void) consumer_clear_quiescent_channel(socket, - reg_chan->consumer_key); + buf_reg_chan->consumer_key); } } break; @@ -5562,6 +7911,36 @@ end: return ret; } +/* The ua_sess lock must be held by the caller. */ +static +int find_or_create_ust_app_map( + struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, + struct ust_app *app, + struct ltt_ust_map *umap, + struct ust_app_map **ua_map) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_map_node; + + lttng_ht_lookup(ua_sess->maps, (void *) umap->name, &iter); + ua_map_node = lttng_ht_iter_get_node_str(&iter); + if (ua_map_node) { + *ua_map = caa_container_of(ua_map_node, + struct ust_app_map, node); + goto end; + } + + DBG("UST map id = %"PRIu64" not found. Creating it.", umap->id); + ret = ust_app_map_create(usess, ua_sess, umap, app, ua_map); + if (ret) { + goto end; + } +end: + return ret; +} + static int ust_app_channel_synchronize_event(struct ust_app_channel *ua_chan, struct ltt_ust_event *uevent, struct ust_app_session *ua_sess, @@ -5571,9 +7950,11 @@ int ust_app_channel_synchronize_event(struct ust_app_channel *ua_chan, struct ust_app_event *ua_event = NULL; ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name, - uevent->filter, uevent->attr.loglevel, uevent->exclusion); + uevent->filter, uevent->attr.loglevel, uevent->exclusion, + uevent->attr.token); if (!ua_event) { - ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); + ret = create_ust_app_channel_event(ua_sess, ua_chan, uevent, app); + if (ret < 0) { goto end; } @@ -5590,6 +7971,34 @@ end: } /* Called with RCU read-side lock held. */ +static +int ust_app_map_synchronize_event(struct ust_app_map *ua_map, + struct ltt_ust_event *uevent, struct ust_app_session *ua_sess, + struct ust_app *app) +{ + int ret = 0; + struct ust_app_event *ua_event = NULL; + + ua_event = find_ust_app_event(ua_map->events, uevent->attr.name, + uevent->filter, uevent->attr.loglevel, uevent->exclusion, + uevent->attr.token); + if (!ua_event) { + ret = create_ust_app_map_event(ua_sess, ua_map, uevent, app); + if (ret < 0) { + goto end; + } + } else { + if (ua_event->enabled != uevent->enabled) { + ret = uevent->enabled ? + enable_ust_app_event(ua_sess, ua_event, app) : + disable_ust_app_event(ua_sess, ua_event, app); + } + } + +end: + return ret; +} + static void ust_app_synchronize_event_notifier_rules(struct ust_app *app) { @@ -5635,8 +8044,8 @@ void ust_app_synchronize_event_notifier_rules(struct ust_app *app) } for (i = 0; i < count; i++) { - struct lttng_condition *condition; - struct lttng_event_rule *event_rule; + const struct lttng_condition *condition; + const struct lttng_event_rule *event_rule; struct lttng_trigger *trigger; const struct ust_app_event_notifier_rule *looked_up_event_notifier_rule; enum lttng_condition_status condition_status; @@ -5646,14 +8055,13 @@ void ust_app_synchronize_event_notifier_rules(struct ust_app *app) assert(trigger); token = lttng_trigger_get_tracer_token(trigger); - condition = lttng_trigger_get_condition(trigger); + condition = lttng_trigger_get_const_condition(trigger); - if (lttng_condition_get_type(condition) != LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { - /* Does not apply */ + if (!lttng_trigger_needs_tracer_notifier(trigger)) { continue; } - condition_status = lttng_condition_event_rule_borrow_rule_mutable(condition, &event_rule); + condition_status = lttng_condition_on_event_get_rule(condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); if (lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL) { @@ -5733,38 +8141,20 @@ end: } /* - * The caller must ensure that the application is compatible and is tracked - * by the process attribute trackers. + * Called with RCU read-side lock held. */ static -void ust_app_synchronize(struct ltt_ust_session *usess, +void ust_app_synchronize_all_channels(struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, struct ust_app *app) { int ret = 0; struct cds_lfht_iter uchan_iter; struct ltt_ust_channel *uchan; - struct ust_app_session *ua_sess = NULL; - - /* - * The application's configuration should only be synchronized for - * active sessions. - */ - assert(usess->active); - ret = find_or_create_ust_app_session(usess, app, &ua_sess, NULL); - if (ret < 0) { - /* Tracer is probably gone or ENOMEM. */ - goto error; - } + assert(usess); assert(ua_sess); - - pthread_mutex_lock(&ua_sess->lock); - if (ua_sess->deleted) { - pthread_mutex_unlock(&ua_sess->lock); - goto end; - } - - rcu_read_lock(); + assert(app); cds_lfht_for_each_entry(usess->domain_global.channels->ht, &uchan_iter, uchan, node.node) { @@ -5783,7 +8173,7 @@ void ust_app_synchronize(struct ltt_ust_session *usess, app, uchan, &ua_chan); if (ret) { /* Tracer is probably gone or ENOMEM. */ - goto error_unlock; + goto end; } if (!ua_chan) { @@ -5796,7 +8186,7 @@ void ust_app_synchronize(struct ltt_ust_session *usess, ret = ust_app_channel_synchronize_event(ua_chan, uevent, ua_sess, app); if (ret) { - goto error_unlock; + goto end; } } @@ -5805,10 +8195,106 @@ void ust_app_synchronize(struct ltt_ust_session *usess, enable_ust_app_channel(ua_sess, uchan, app) : disable_ust_app_channel(ua_sess, ua_chan, app); if (ret) { - goto error_unlock; + goto end; + } + } + } +end: + return; +} + +/* + * Called with RCU read-side lock held. + */ +static +void ust_app_synchronize_all_maps(struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, + struct ust_app *app) +{ + int ret = 0; + struct cds_lfht_iter umap_iter; + struct ltt_ust_map *umap; + + assert(usess); + assert(ua_sess); + assert(app); + + cds_lfht_for_each_entry(usess->domain_global.maps->ht, &umap_iter, + umap, node.node) { + struct ust_app_map *ua_map; + struct cds_lfht_iter uevent_iter; + struct ltt_ust_event *uevent; + + DBG("Synchronizing UST map id = %"PRIu64, umap->id); + + ret = find_or_create_ust_app_map(usess, ua_sess, + app, umap, &ua_map); + if (ret) { + /* Tracer is probably gone or ENOMEM. */ + goto end; + } + + DBG("Synchronizing all events of UST map id = %"PRIu64, umap->id); + cds_lfht_for_each_entry(umap->events->ht, &uevent_iter, uevent, + node.node) { + ret = ust_app_map_synchronize_event(ua_map, + uevent, ua_sess, app); + if (ret) { + goto end; + } + } + + if (ua_map->enabled != umap->enabled) { + if (umap->enabled) { + DBG("Map disabled on the tracer side but shouldn't"); + ret = enable_ust_app_map(ua_sess, umap, app); + } else { + DBG("Map enabled on the tracer side but shouldn't"); + ret = disable_ust_app_map(ua_sess, ua_map, app); + } + if (ret) { + goto end; } } } +end: + return; +} + +/* + * The caller must ensure that the application is compatible and is tracked + * by the process attribute trackers. + */ +static +void ust_app_synchronize(struct ltt_ust_session *usess, + struct ust_app *app) +{ + int ret = 0; + struct ust_app_session *ua_sess = NULL; + + /* + * The application's configuration should only be synchronized for + * active sessions. + */ + assert(usess->active); + + ret = find_or_create_ust_app_session(usess, app, &ua_sess, NULL); + if (ret < 0) { + /* Tracer is probably gone or ENOMEM. */ + goto error; + } + assert(ua_sess); + + + rcu_read_lock(); + + pthread_mutex_lock(&ua_sess->lock); + if (ua_sess->deleted) { + pthread_mutex_unlock(&ua_sess->lock); + goto end; + } + ust_app_synchronize_all_channels(usess, ua_sess, app); + ust_app_synchronize_all_maps(usess, ua_sess, app); /* * Create the metadata for the application. This returns gracefully if a @@ -5941,6 +8427,20 @@ void ust_app_global_update_all_event_notifier_rules(void) rcu_read_unlock(); } +void ust_app_update_event_notifier_error_count(struct lttng_trigger *trigger) +{ + uint64_t error_count = 0; + enum event_notifier_error_accounting_status status; + struct lttng_condition *condition = lttng_trigger_get_condition(trigger); + + status = event_notifier_error_accounting_get_count(trigger, &error_count); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting trigger error count."); + } + + lttng_condition_on_event_set_error_count(condition, error_count); +} + /* * Add context to a specific channel for global UST domain. */ @@ -6070,34 +8570,61 @@ static struct ust_app_session *find_session_by_objd(struct ust_app *app, ua_sess = caa_container_of(node, struct ust_app_session, ust_objd_node); error: - return ua_sess; + return ua_sess; +} + +/* + * Return a ust app channel object using the application object and the channel + * object descriptor has a key. If not found, NULL is returned. A RCU read side + * lock MUST be acquired before calling this function. + */ +static struct ust_app_channel *find_channel_by_objd(struct ust_app *app, + int objd) +{ + struct lttng_ht_node_ulong *node; + struct lttng_ht_iter iter; + struct ust_app_channel *ua_chan = NULL; + + assert(app); + + lttng_ht_lookup(app->ust_chan_objd, (void *)((unsigned long) objd), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + DBG2("UST app channel find by objd %d not found", objd); + goto error; + } + + ua_chan = caa_container_of(node, struct ust_app_channel, ust_objd_node); + +error: + return ua_chan; } /* - * Return a ust app channel object using the application object and the channel + * Return a ust app map object using the application object and the map * object descriptor has a key. If not found, NULL is returned. A RCU read side * lock MUST be acquired before calling this function. */ -static struct ust_app_channel *find_channel_by_objd(struct ust_app *app, +static struct ust_app_map *find_map_by_objd(struct ust_app *app, int objd) { struct lttng_ht_node_ulong *node; struct lttng_ht_iter iter; - struct ust_app_channel *ua_chan = NULL; + struct ust_app_map *ua_map = NULL; assert(app); - lttng_ht_lookup(app->ust_objd, (void *)((unsigned long) objd), &iter); + lttng_ht_lookup(app->ust_map_objd, (void *)((unsigned long) objd), &iter); node = lttng_ht_iter_get_node_ulong(&iter); if (node == NULL) { - DBG2("UST app channel find by objd %d not found", objd); + DBG2("UST app map find by objd %d not found", objd); goto error; } - ua_chan = caa_container_of(node, struct ust_app_channel, ust_objd_node); + ua_map = caa_container_of(node, struct ust_app_map, ust_objd_node); error: - return ua_chan; + return ua_map; } /* @@ -6119,7 +8646,7 @@ static int reply_ust_register_channel(int sock, int cobjd, struct ust_app_channel *ua_chan; struct ust_app_session *ua_sess; struct ust_registry_session *registry; - struct ust_registry_channel *chan_reg; + struct ust_registry_channel *ust_reg_chan; rcu_read_lock(); @@ -6160,30 +8687,30 @@ static int reply_ust_register_channel(int sock, int cobjd, pthread_mutex_lock(®istry->lock); - chan_reg = ust_registry_channel_find(registry, chan_reg_key); - assert(chan_reg); + ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key); + assert(ust_reg_chan); - if (!chan_reg->register_done) { + if (!ust_reg_chan->register_done) { /* * TODO: eventually use the registry event count for * this channel to better guess header type for per-pid * buffers. */ type = USTCTL_CHANNEL_HEADER_LARGE; - chan_reg->nr_ctx_fields = nr_fields; - chan_reg->ctx_fields = fields; + ust_reg_chan->nr_ctx_fields = nr_fields; + ust_reg_chan->ctx_fields = fields; fields = NULL; - chan_reg->header_type = type; + ust_reg_chan->header_type = type; } else { /* Get current already assigned values. */ - type = chan_reg->header_type; + type = ust_reg_chan->header_type; } /* Channel id is set during the object creation. */ - chan_id = chan_reg->chan_id; + chan_id = ust_reg_chan->chan_id; /* Append to metadata */ - if (!chan_reg->metadata_dumped) { - ret_code = ust_metadata_channel_statedump(registry, chan_reg); + if (!ust_reg_chan->metadata_dumped) { + ret_code = ust_metadata_channel_statedump(registry, ust_reg_chan); if (ret_code) { ERR("Error appending channel metadata (errno = %d)", ret_code); goto reply; @@ -6206,7 +8733,7 @@ reply: } /* This channel registry registration is completed. */ - chan_reg->register_done = 1; + ust_reg_chan->register_done = 1; error: pthread_mutex_unlock(®istry->lock); @@ -6216,45 +8743,21 @@ error_rcu_unlock: return ret; } -/* - * Add event to the UST channel registry. When the event is added to the - * registry, the metadata is also created. Once done, this replies to the - * application with the appropriate error code. - * - * The session UST registry lock is acquired in the function. - * - * On success 0 is returned else a negative value. - */ -static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, +static int add_event_ust_chan_registry(int sock, struct ust_app *ua, + struct ust_app_channel *ua_chan, int sobjd, int cobjd, char *name, char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel_value, char *model_emf_uri) { int ret, ret_code; uint32_t event_id = 0; uint64_t chan_reg_key; - struct ust_app *app; - struct ust_app_channel *ua_chan; struct ust_app_session *ua_sess; struct ust_registry_session *registry; - - rcu_read_lock(); - - /* Lookup application. If not found, there is a code flow error. */ - app = find_app_by_notify_sock(sock); - if (!app) { - DBG("Application socket %d is being torn down. Abort event notify", - sock); - ret = 0; - goto error_rcu_unlock; - } - - /* Lookup channel by UST object descriptor. */ - ua_chan = find_channel_by_objd(app, cobjd); - if (!ua_chan) { - DBG("Application channel is being torn down. Abort event notify"); - ret = 0; - goto error_rcu_unlock; - } + /* + * The counter index is unused for channel events. It's only used for + * map events. + */ + uint64_t counter_index = 0; assert(ua_chan->session); ua_sess = ua_chan->session; @@ -6263,7 +8766,7 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, if (!registry) { DBG("Application session is being torn down. Abort event notify"); ret = 0; - goto error_rcu_unlock; + goto error; } if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) { @@ -6279,10 +8782,10 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, * and model_emf_uri meaning any free are done inside it if needed. These * three variables MUST NOT be read/write after this. */ - ret_code = ust_registry_create_event(registry, chan_reg_key, + ret_code = ust_registry_chan_create_event(registry, chan_reg_key, sobjd, cobjd, name, sig, nr_fields, fields, loglevel_value, model_emf_uri, ua_sess->buffer_type, - &event_id, app); + &event_id, ua); sig = NULL; fields = NULL; model_emf_uri = NULL; @@ -6292,7 +8795,7 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, * application can be notified. In case of an error, it's important not to * return a negative error or else the application will get closed. */ - ret = ustctl_reply_register_event(sock, event_id, ret_code); + ret = ustctl_reply_register_event(sock, event_id, counter_index, ret_code); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { ERR("UST app reply event failed with ret %d", ret); @@ -6311,11 +8814,148 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, error: pthread_mutex_unlock(®istry->lock); -error_rcu_unlock: + return ret; +} + +static int add_event_ust_map_registry(int sock, struct ust_app *ua, + struct ust_app_map *ua_map, int sobjd, int cobjd, char *name, + char *sig, size_t nr_fields, struct ustctl_field *fields, + int loglevel_value, char *model_emf_uri, uint64_t tracer_token) +{ + int ret, ret_code; + uint64_t map_reg_key, counter_index; + struct ust_app_session *ua_sess; + struct ust_registry_session *registry; + + assert(ua_map->session); + ua_sess = ua_map->session; + + registry = get_session_registry(ua_sess); + if (!registry) { + DBG("Application session is being torn down. Abort event notify"); + ret = 0; + goto error; + } + + if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) { + map_reg_key = ua_map->tracing_map_id; + } else { + map_reg_key = ua_map->key; + } + + pthread_mutex_lock(®istry->lock); + + /* + * From this point on, this call acquires the ownership of the sig, fields + * and model_emf_uri meaning any free are done inside it if needed. These + * three variables MUST NOT be read/write after this. + */ + DBG("Registry_map_create_event on map=%"PRIu64" with token=%"PRIu64, + map_reg_key, tracer_token); + ret_code = ust_registry_map_create_event(registry, map_reg_key, + sobjd, cobjd, name, sig, nr_fields, fields, + loglevel_value, model_emf_uri, ua_sess->buffer_type, + tracer_token, &counter_index, ua); + assert(!ret_code); + + sig = NULL; + fields = NULL; + model_emf_uri = NULL; + + /* + * The return value is returned to ustctl so in case of an error, the + * application can be notified. In case of an error, it's important not to + * return a negative error or else the application will get closed. + */ + ret = ustctl_reply_register_event(sock, counter_index, counter_index, + ret_code); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app reply event failed with ret %d", ret); + } else { + DBG3("UST app reply event failed. Application died"); + } + /* + * No need to wipe the create event since the application socket will + * get close on error hence cleaning up everything by itself. + */ + goto error; + } + + DBG3("UST registry map event %s with counter index %" PRIu64 " added successfully", + name, counter_index); + +error: + pthread_mutex_unlock(®istry->lock); + return ret; +} + + +/* + * Add event to the UST channel registry. When the event is added to the + * registry, the metadata is also created. Once done, this replies to the + * application with the appropriate error code. + * + * The session UST registry lock is acquired in the function. + * + * On success 0 is returned else a negative value. + */ +static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, + char *sig, size_t nr_fields, struct ustctl_field *fields, + int loglevel_value, char *model_emf_uri, uint64_t tracer_token) +{ + int ret; + struct ust_app *app; + struct ust_app_channel *ua_chan = NULL; + struct ust_app_map *ua_map = NULL; + + rcu_read_lock(); + + /* Lookup application. If not found, there is a code flow error. */ + app = find_app_by_notify_sock(sock); + if (!app) { + DBG("Application socket %d is being torn down. Abort event notify", + sock); + ret = 0; + goto end; + } + + /* Lookup channel by UST object descriptor. */ + ua_chan = find_channel_by_objd(app, cobjd); + if (ua_chan) { + ret = add_event_ust_chan_registry(sock, app, ua_chan, sobjd, cobjd, + name, sig, nr_fields, fields, loglevel_value, + model_emf_uri); + if (ret) { + ERR("Error adding channel event to registry: event_name = '%s'", name); + } + goto found; + } + + /* Lookup map by UST object descriptor. */ + ua_map = find_map_by_objd(app, cobjd); + if (ua_map) { + ret = add_event_ust_map_registry(sock, app, ua_map, sobjd, cobjd, + name, sig, nr_fields, fields, loglevel_value, + model_emf_uri, tracer_token); + if (ret) { + ERR("Error adding map event to registry: event_name = '%s'", name); + goto end; + } + goto found; + } + + if (!ua_chan && !ua_map) { + DBG("Application event container is being torn down. Abort event notify"); + ret = 0; + goto end; + } + +found: + ret = 0; + +end: rcu_read_unlock(); - free(sig); - free(fields); - free(model_emf_uri); return ret; } @@ -6431,13 +9071,14 @@ int ust_app_recv_notify(int sock) int sobjd, cobjd, loglevel_value; char name[LTTNG_UST_SYM_NAME_LEN], *sig, *model_emf_uri; size_t nr_fields; + uint64_t tracer_token = 0; struct ustctl_field *fields; DBG2("UST app ustctl register event received"); ret = ustctl_recv_register_event(sock, &sobjd, &cobjd, name, &loglevel_value, &sig, &nr_fields, &fields, - &model_emf_uri); + &model_emf_uri, &tracer_token); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { ERR("UST app recv event failed with ret %d", ret); @@ -6454,7 +9095,7 @@ int ust_app_recv_notify(int sock) * to the this function. */ ret = add_event_ust_registry(sock, sobjd, cobjd, name, sig, nr_fields, - fields, loglevel_value, model_emf_uri); + fields, loglevel_value, model_emf_uri, tracer_token); if (ret < 0) { goto error; } @@ -6648,7 +9289,7 @@ enum lttng_error_code ust_app_snapshot_record( struct buffer_reg_uid *reg; cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; char pathname[PATH_MAX]; size_t consumer_path_offset = 0; @@ -6685,9 +9326,9 @@ enum lttng_error_code ust_app_snapshot_record( } /* Add the UST default trace dir to path. */ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { + buf_reg_chan, node.node) { status = consumer_snapshot_channel(socket, - reg_chan->consumer_key, + buf_reg_chan->consumer_key, output, 0, usess->uid, usess->gid, &trace_path[consumer_path_offset], wait, nb_packets_per_stream); @@ -6815,19 +9456,19 @@ uint64_t ust_app_get_size_one_more_packet_per_stream( struct buffer_reg_uid *reg; cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; rcu_read_lock(); cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { - if (cur_nr_packets >= reg_chan->num_subbuf) { + buf_reg_chan, node.node) { + if (cur_nr_packets >= buf_reg_chan->num_subbuf) { /* * Don't take channel into account if we * already grab all its packets. */ continue; } - tot_size += reg_chan->subbuf_size * reg_chan->stream_count; + tot_size += buf_reg_chan->subbuf_size * buf_reg_chan->stream_count; } rcu_read_unlock(); } @@ -7051,7 +9692,7 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) struct buffer_reg_uid *reg; cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; if (!reg->registry->reg.ust->metadata_key) { @@ -7069,9 +9710,9 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) /* Rotate the data channels. */ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { + buf_reg_chan, node.node) { ret = consumer_rotate_channel(socket, - reg_chan->consumer_key, + buf_reg_chan->consumer_key, usess->uid, usess->gid, usess->consumer, /* is_metadata_channel */ false); @@ -7284,7 +9925,8 @@ error: * * Return LTTNG_OK on success or else an LTTng error code. */ -enum lttng_error_code ust_app_clear_session(struct ltt_session *session) +static +enum lttng_error_code ust_app_clear_session_channels(struct ltt_session *session) { int ret; enum lttng_error_code cmd_ret = LTTNG_OK; @@ -7308,7 +9950,7 @@ enum lttng_error_code ust_app_clear_session(struct ltt_session *session) struct buffer_reg_uid *reg; cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; /* Get consumer socket to use to push the metadata.*/ @@ -7321,9 +9963,9 @@ enum lttng_error_code ust_app_clear_session(struct ltt_session *session) /* Clear the data channels. */ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { + buf_reg_chan, node.node) { ret = consumer_clear_channel(socket, - reg_chan->consumer_key); + buf_reg_chan->consumer_key); if (ret < 0) { goto error; } @@ -7427,6 +10069,228 @@ end: return cmd_ret; } +static +enum lttng_error_code ust_app_clear_session_maps_per_uid( + struct ltt_ust_session *usess, struct ltt_ust_map *umap, + uint32_t app_bitness) +{ + struct lttng_ht_iter iter; + struct buffer_reg_uid *buf_reg_uid; + struct buffer_reg_map *buf_reg_map; + struct ust_registry_session *ust_reg_sess; + struct lttng_ht_node_u64 *ust_reg_map_node; + struct ust_registry_map *ust_reg_map; + struct ust_registry_map_index_ht_entry *map_index_entry; + enum lttng_error_code status; + + buf_reg_uid = buffer_reg_uid_find(usess->id, app_bitness, usess->uid); + if (!buf_reg_uid) { + /* + * Buffer registry entry for uid not found. Probably no app for + * this UID at the moment. + */ + DBG("No buffer registry entry found for uid: ust-sess-id = %"PRIu64", bitness = %"PRIu32", uid = %d", + usess->id, app_bitness, usess->uid); + /* + * Not an error. Leave the key value pair unchanged and return. + */ + status = LTTNG_OK; + goto end; + } + + buf_reg_map = buffer_reg_map_find(umap->id, buf_reg_uid); + if (!buf_reg_uid) { + ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64, + umap->id); + status = LTTNG_ERR_UNK; + goto end; + } + + ust_reg_sess = buf_reg_uid->registry->reg.ust; + + /* Get the ust_reg map object from the registry */ + // FIXME: frdeso: This can be changed to ust_registry_map_find() right? + + lttng_ht_lookup(ust_reg_sess->maps, (void *) &umap->id, &iter); + ust_reg_map_node = lttng_ht_iter_get_node_u64(&iter); + if (!ust_reg_map_node) { + ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64, + umap->id); + status = LTTNG_ERR_UNK; + goto end; + } + ust_reg_map = caa_container_of(ust_reg_map_node, + struct ust_registry_map, node); + + cds_lfht_for_each_entry(ust_reg_map->key_string_to_bucket_index_ht->ht, + &iter.iter, map_index_entry, node.node) { + int ret; + size_t dimension_indexes[1] = {map_index_entry->index}; + + ret = ustctl_counter_clear(buf_reg_map->daemon_counter, dimension_indexes); + if (ret) { + ERR("clearing counter index %"PRIu64, map_index_entry->index); + //fixme: frdeso: convert ust errors to tools errors + status = LTTNG_ERR_UNK; + goto end; + } + } + + status = LTTNG_OK; + +end: + return status; +} + +static +enum lttng_error_code ust_app_clear_session_maps_per_pid( + struct ltt_ust_session *usess, struct ltt_ust_map *umap, + uint32_t app_bitness) +{ + struct lttng_ht_iter app_iter; + enum lttng_error_code status; + struct ust_app *app; + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_iter iter; + + cds_lfht_for_each_entry(ust_app_ht->ht, &app_iter.iter, app, pid_n.node) { + struct lttng_ht_iter map_iter, key_iter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app_map *ua_map; + struct ust_app_session *ua_sess; + struct ust_registry_session *ust_reg_sess; + struct ust_registry_map *ust_reg_map; + struct ust_registry_map_index_ht_entry *map_index_entry; + + if (app->bits_per_long != app_bitness) { + continue; + } + + ua_sess = lookup_session_by_app(usess, app); + if (!ua_sess) { + /* Session not associated with this app. */ + continue; + } + + ust_reg_sess = get_session_registry(ua_sess); + if (!ust_reg_sess) { + DBG("Application session is being torn down. Skip application."); + continue; + } + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &map_iter); + ua_map_node = lttng_ht_iter_get_node_str(&map_iter); + + assert(ua_map_node != NULL); + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + pthread_mutex_lock(&ust_reg_sess->lock); + ust_reg_map = ust_registry_map_find(ust_reg_sess, ua_map->key); + pthread_mutex_unlock(&ust_reg_sess->lock); + assert(ust_reg_map); + + /* Iterate over all the formated_key -> counter index */ + cds_lfht_for_each_entry(ust_reg_map->key_string_to_bucket_index_ht->ht, + &key_iter.iter, map_index_entry, node.node) { + + int ret; + size_t dimension_indexes[1] = {map_index_entry->index}; + + ret = ustctl_counter_clear(ua_map->map_handle, + dimension_indexes); + if (ret) { + ERR("clearing counter index %"PRIu64, map_index_entry->index); + //fixme: frdeso: convert ust errors to tools errors + status = LTTNG_ERR_UNK; + goto end; + } + } + } + + /* + * Emptying the dead app key values. + */ + pthread_mutex_lock(&umap->dead_app_kv_values.lock); + + if (app_bitness == 32) { + cds_lfht_for_each_entry(umap->dead_app_kv_values.dead_app_kv_values_32bits->ht, + &iter.iter, kv_entry, node.node) { + kv_entry->value = 0; + } + } else { + + cds_lfht_for_each_entry(umap->dead_app_kv_values.dead_app_kv_values_64bits->ht, + &iter.iter, kv_entry, node.node) { + kv_entry->value = 0; + } + } + + pthread_mutex_unlock(&umap->dead_app_kv_values.lock); + + status = LTTNG_OK; +end: + return status; +} + +static +enum lttng_error_code ust_app_clear_session_maps(struct ltt_session *session) +{ + struct ltt_ust_session *usess = session->ust_session; + enum lttng_error_code status; + struct lttng_ht_iter iter; + struct ltt_ust_map *umap; + + cds_lfht_for_each_entry(usess->domain_global.maps->ht, &iter.iter, + umap, node.node) { + + if (usess->buffer_type == LTTNG_BUFFER_PER_UID) { + status = ust_app_clear_session_maps_per_uid(session->ust_session, + umap, 32); + assert(status == LTTNG_OK); + + status = ust_app_clear_session_maps_per_uid(session->ust_session, + umap, 64); + assert(status == LTTNG_OK); + //fixme:frdeso:error handling + } else { + status = ust_app_clear_session_maps_per_pid(session->ust_session, + umap, 32); + assert(status == LTTNG_OK); + + status = ust_app_clear_session_maps_per_pid(session->ust_session, + umap, 64); + assert(status == LTTNG_OK); + //fixme:frdeso:error handling + // + // + } + + } + + return LTTNG_OK; +} + +enum lttng_error_code ust_app_clear_session(struct ltt_session *session) +{ + enum lttng_error_code cmd_ret; + + + cmd_ret = ust_app_clear_session_channels(session); + if (cmd_ret != LTTNG_OK) { + ERR("Clearing session's channels"); + goto end; + } + + cmd_ret = ust_app_clear_session_maps(session); + if (cmd_ret != LTTNG_OK) { + ERR("Clearing session's maps"); + goto end; + } +end: + return cmd_ret; +} + /* * This function skips the metadata channel as the begin/end timestamps of a * metadata packet are useless. @@ -7460,7 +10324,7 @@ enum lttng_error_code ust_app_open_packets(struct ltt_session *session) cds_list_for_each_entry ( reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; socket = consumer_find_socket_by_bitness( @@ -7471,11 +10335,11 @@ enum lttng_error_code ust_app_open_packets(struct ltt_session *session) } cds_lfht_for_each_entry(reg->registry->channels->ht, - &iter.iter, reg_chan, node.node) { + &iter.iter, buf_reg_chan, node.node) { const int open_ret = consumer_open_channel_packets( socket, - reg_chan->consumer_key); + buf_reg_chan->consumer_key); if (open_ret < 0) { ret = LTTNG_ERR_UNK; diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h index 29024b4de..5325c64fc 100644 --- a/src/bin/lttng-sessiond/ust-app.h +++ b/src/bin/lttng-sessiond/ust-app.h @@ -11,7 +11,9 @@ #include +#include #include +#include #include "trace-ust.h" #include "ust-registry.h" @@ -42,6 +44,7 @@ struct ust_app_ht_key { const struct lttng_bytecode *filter; enum lttng_ust_loglevel_type loglevel_type; const struct lttng_event_exclusion *exclusion; + uint64_t tracer_token; }; /* @@ -93,6 +96,12 @@ struct ust_app_stream_list { struct cds_list_head head; }; +/* counter list containing ust_app_counter. */ +struct ust_app_counter_list { + unsigned int count; + struct cds_list_head head; +}; + struct ust_app_ctx { int handle; struct lttng_ust_context_attr ctx; @@ -114,6 +123,7 @@ struct ust_app_event { struct ust_app_event_notifier_rule { int enabled; + uint64_t error_counter_index; int handle; struct lttng_ust_object_data *obj; /* Holds a strong reference. */ @@ -139,6 +149,13 @@ struct ust_app_stream { struct cds_list_head list; }; +struct ust_app_map_counter { + int handle; + struct lttng_ust_object_data *obj; + /* Using a list of counters to keep order. */ + struct cds_list_head list; +}; + struct ust_app_channel { int enabled; int handle; @@ -185,6 +202,43 @@ struct ust_app_channel { struct rcu_head rcu_head; }; +struct ust_app_map { + int enabled; + int handle; + /* Counters were sent to the UST tracer. */ + int is_sent; + /* + * FIXME frdeso: what is difference between key and tracing_map_id + * Unique key used to identify the map. + */ + uint64_t key; + /* Id of the tracing map set on creation. */ + uint64_t tracing_map_id; + bool coalesce_hits; + enum lttng_map_bitness bitness; + char name[LTTNG_UST_SYM_NAME_LEN]; + struct lttng_ust_object_data *obj; + struct ust_app_counter_list counters; + /* Session pointer that owns this object. */ + struct ust_app_session *session; + struct lttng_ht *events; + struct ltt_ust_map_dead_pid_kv_values *dead_app_kv_values; + + size_t bucket_count; + struct ustctl_daemon_counter *map_handle; + /* + * Node indexed by channel name in the channels' hash table of a session. + */ + struct lttng_ht_node_str node; + /* + * Node indexed by UST channel object descriptor (handle). Stored in the + * ust_objd hash table in the ust_app object. + */ + struct lttng_ht_node_ulong ust_objd_node; + /* For delayed reclaim */ + struct rcu_head rcu_head; +}; + struct ust_app_session { /* * Lock protecting this session's ust app interaction. Held @@ -207,6 +261,7 @@ struct ust_app_session { uint64_t tracing_id; uint64_t id; /* Unique session identifier */ struct lttng_ht *channels; /* Registered channels */ + struct lttng_ht *maps; /* Registered maps */ struct lttng_ht_node_u64 node; /* * Node indexed by UST session object descriptor (handle). Stored in the @@ -291,7 +346,11 @@ struct ust_app { /* * Hash table containing ust_app_channel indexed by channel objd. */ - struct lttng_ht *ust_objd; + struct lttng_ht *ust_chan_objd; + /* + * Hash table containing ust_app_map indexed by map objd. + */ + struct lttng_ht *ust_map_objd; /* * Hash table containing ust_app_session indexed by objd. */ @@ -319,6 +378,9 @@ struct ust_app { */ struct lttng_ust_object_data *object; struct lttng_pipe *event_pipe; + struct lttng_ust_object_data *counter; + struct lttng_ust_object_data **counter_cpu; + int nr_counter_cpu; } event_notifier_group; /* * Hashtable indexing the application's event notifier rule's @@ -338,16 +400,31 @@ int ust_app_stop_trace_all(struct ltt_ust_session *usess); int ust_app_destroy_trace_all(struct ltt_ust_session *usess); int ust_app_list_events(struct lttng_event **events); int ust_app_list_event_fields(struct lttng_event_field **fields); -int ust_app_create_event_glb(struct ltt_ust_session *usess, +int ust_app_create_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); int ust_app_disable_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan); int ust_app_enable_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan); -int ust_app_enable_event_glb(struct ltt_ust_session *usess, +int ust_app_enable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); -int ust_app_disable_event_glb(struct ltt_ust_session *usess, +int ust_app_disable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); +int ust_app_map_list_values(const struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content **map_content); +int ust_app_enable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); +int ust_app_disable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); +int ust_app_create_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent); +int ust_app_enable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent); +int ust_app_disable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent); + int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx); void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app); @@ -355,10 +432,13 @@ void ust_app_global_update_all(struct ltt_ust_session *usess); void ust_app_global_update_event_notifier_rules(struct ust_app *app); void ust_app_global_update_all_event_notifier_rules(void); +void ust_app_update_event_notifier_error_count(struct lttng_trigger *trigger); + void ust_app_clean_list(void); int ust_app_ht_alloc(void); struct ust_app *ust_app_find_by_pid(pid_t pid); struct ust_app_stream *ust_app_alloc_stream(void); +struct ust_app_map_counter *ust_app_alloc_map_counter(void); int ust_app_recv_registration(int sock, struct ust_register_msg *msg); int ust_app_recv_notify(int sock); void ust_app_add(struct ust_app *app); @@ -504,24 +584,62 @@ int ust_app_enable_channel_glb(struct ltt_ust_session *usess, return 0; } static inline -int ust_app_create_event_glb(struct ltt_ust_session *usess, +int ust_app_create_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { return 0; } static inline -int ust_app_disable_event_glb(struct ltt_ust_session *usess, +int ust_app_disable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { return 0; } static inline -int ust_app_enable_event_glb(struct ltt_ust_session *usess, +int ust_app_enable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { return 0; } static inline +int ust_app_disable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + return 0; +} +static inline +int ust_app_map_list_values(const struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content **map_content) +{ + return 0; +} +static inline +int ust_app_enable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + return 0; +} +static inline +int ust_app_create_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + return 0; +} +static inline +int ust_app_disable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + return 0; +} +static inline +int ust_app_enable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + return 0; +} +static inline int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx) { @@ -579,7 +697,12 @@ unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess) { return 0; } - +static inline +void ust_app_update_event_notifier_error_count( + struct lttng_trigger *lttng_trigger) +{ + return; +} static inline int ust_app_supported(void) { diff --git a/src/bin/lttng-sessiond/ust-consumer.c b/src/bin/lttng-sessiond/ust-consumer.c index cd6b53ee1..158f727f2 100644 --- a/src/bin/lttng-sessiond/ust-consumer.c +++ b/src/bin/lttng-sessiond/ust-consumer.c @@ -42,7 +42,7 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, uint64_t key, chan_reg_key; char *pathname = NULL; struct lttcomm_consumer_msg msg; - struct ust_registry_channel *chan_reg; + struct ust_registry_channel *ust_reg_chan; char shm_path[PATH_MAX] = ""; char root_shm_path[PATH_MAX] = ""; bool is_local_trace; @@ -105,9 +105,9 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, * those buffer files. */ } else { - chan_reg = ust_registry_channel_find(registry, chan_reg_key); - assert(chan_reg); - chan_id = chan_reg->chan_id; + ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key); + assert(ust_reg_chan); + chan_id = ust_reg_chan->chan_id; if (ua_sess->shm_path[0]) { strncpy(shm_path, ua_sess->shm_path, sizeof(shm_path)); shm_path[sizeof(shm_path) - 1] = '\0'; diff --git a/src/bin/lttng-sessiond/ust-ctl-internal.h b/src/bin/lttng-sessiond/ust-ctl-internal.h index 7a89dbc26..af380bc87 100644 --- a/src/bin/lttng-sessiond/ust-ctl-internal.h +++ b/src/bin/lttng-sessiond/ust-ctl-internal.h @@ -9,8 +9,9 @@ #ifndef LTTNG_UST_CTL_INTERNAL_H #define LTTNG_UST_CTL_INTERNAL_H -#include #include +#include +#include #include "lttng-ust-abi.h" diff --git a/src/bin/lttng-sessiond/ust-registry.c b/src/bin/lttng-sessiond/ust-registry.c index 1b1e65eba..384d8030e 100644 --- a/src/bin/lttng-sessiond/ust-registry.c +++ b/src/bin/lttng-sessiond/ust-registry.c @@ -5,6 +5,7 @@ * */ +#include "bin/lttng-sessiond/event-notifier-error-accounting.h" #define _LGPL_SOURCE #include #include @@ -12,6 +13,8 @@ #include #include #include +#include +#include #include "ust-registry.h" #include "ust-app.h" @@ -284,7 +287,7 @@ int validate_event_fields(size_t nr_fields, struct ustctl_field *fields, * registry. */ static struct ust_registry_event *alloc_event(int session_objd, - int channel_objd, char *name, char *sig, size_t nr_fields, + int container_objd, char *name, char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel_value, char *model_emf_uri, struct ust_app *app) { @@ -304,7 +307,7 @@ static struct ust_registry_event *alloc_event(int session_objd, } event->session_objd = session_objd; - event->channel_objd = channel_objd; + event->container_objd = container_objd; /* Allocated by ustctl. */ event->signature = sig; event->nr_fields = nr_fields; @@ -352,6 +355,33 @@ static void destroy_event_rcu(struct rcu_head *head) destroy_event(event); } +/* + * Destroy ust_registry_map_key_ht_entry function call of the call RCU. + */ +static void destroy_ust_registry_map_key_ht_entry(struct rcu_head *head) +{ + struct lttng_ht_node_u64 *node = + caa_container_of(head, struct lttng_ht_node_u64, head); + struct ust_registry_map_key_ht_entry *entry = + caa_container_of(node, struct ust_registry_map_key_ht_entry, node); + + lttng_map_key_put(entry->key); + free(entry); +} + +/* + * Destroy ust_registry_map_index_ht_entry function call of the call RCU. + */ +static void destroy_ust_registry_map_index_ht_entry(struct rcu_head *head) +{ + struct lttng_ht_node_str *node = + caa_container_of(head, struct lttng_ht_node_str, head); + struct ust_registry_map_index_ht_entry *entry = + caa_container_of(node, struct ust_registry_map_index_ht_entry, node); + + free(entry); +} + /* * Find an event using the name and signature in the given registry. RCU read * side lock MUST be acquired before calling this function and as long as the @@ -359,7 +389,7 @@ static void destroy_event_rcu(struct rcu_head *head) * * On success, the event pointer is returned else NULL. */ -struct ust_registry_event *ust_registry_find_event( +struct ust_registry_event *ust_registry_chan_find_event( struct ust_registry_channel *chan, char *name, char *sig) { struct lttng_ht_node_u64 *node; @@ -398,7 +428,7 @@ end: * * Should be called with session registry mutex held. */ -int ust_registry_create_event(struct ust_registry_session *session, +int ust_registry_chan_create_event(struct ust_registry_session *session, uint64_t chan_key, int session_objd, int channel_objd, char *name, char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel_value, char *model_emf_uri, int buffer_type, @@ -447,7 +477,7 @@ int ust_registry_create_event(struct ust_registry_session *session, DBG3("UST registry creating event with event: %s, sig: %s, id: %u, " "chan_objd: %u, sess_objd: %u, chan_id: %u", event->name, - event->signature, event->id, event->channel_objd, + event->signature, event->id, event->container_objd, event->session_objd, chan->chan_id); /* @@ -472,13 +502,13 @@ int ust_registry_create_event(struct ust_registry_session *session, ERR("UST registry create event add unique failed for event: %s, " "sig: %s, id: %u, chan_objd: %u, sess_objd: %u", event->name, event->signature, event->id, - event->channel_objd, event->session_objd); + event->container_objd, event->session_objd); ret = -EINVAL; goto error_unlock; } } else { /* Request next event id if the node was successfully added. */ - event_id = event->id = ust_registry_get_next_event_id(chan); + event_id = event->id = ust_registry_channel_get_next_event_id(chan); } *event_id_p = event_id; @@ -506,11 +536,334 @@ error_unlock: return ret; } +static +int format_event_key(const struct lttng_map_key *key, + const char *full_event_name, char **formated_key) +{ + int ret; + char _key[LTTNG_UST_KEY_TOKEN_STRING_LEN_MAX] = {0}; + enum lttng_map_key_status key_status; + unsigned int i, token_count; + char *cloned_full_event_name; + const char *provider_name, *event_name; + + assert(key); + assert(full_event_name); + + cloned_full_event_name = strdup(full_event_name); + + provider_name = strtok(cloned_full_event_name, ":"); + event_name = strtok(NULL, ":"); + + key_status = lttng_map_key_get_token_count(key, &token_count); + if (key_status != LTTNG_MAP_KEY_STATUS_OK) { + ERR("Error getting map key token count"); + ret = -1; + goto end; + } + + if (token_count == 0) { + ERR("Map key token number is zero"); + ret = -1; + goto end; + } + + for (i = 0; i < token_count; i++) { + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + switch (token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + struct lttng_map_key_token_string *str_token = + (struct lttng_map_key_token_string *) token; + DBG("Appending a string type key token: str = '%s'", str_token->string); + + strcat(_key, lttng_map_key_token_string_get_string(str_token)); + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + struct lttng_map_key_token_variable *var_token = + (struct lttng_map_key_token_variable *) token; + + switch (var_token->type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + DBG("Serializing a event name variable type key token: event_name = '%s'", + event_name); + strcat(_key, event_name); + break; + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + DBG("Serializing a provider name variable type key token: provider_name = '%s'", + provider_name); + strcat(_key, provider_name); + break; + default: + abort(); + } + break; + } + default: + abort(); + } + } + + *formated_key = strdup(_key); + + ret = 0; +end: + free(cloned_full_event_name); + return ret; +} + +static +const struct lttng_map_key *ust_registry_map_find_key_for_token( + struct ust_registry_map *map, + uint64_t tracer_token) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_u64 *key_node; + struct ust_registry_map_key_ht_entry *key_entry; + const struct lttng_map_key *key = NULL;; + + assert(map); + lttng_ht_lookup(map->tracer_token_to_map_key_ht, + (void *) &tracer_token, &iter); + + key_node = lttng_ht_iter_get_node_u64(&iter); + if (!key_node) { + goto end; + } + + /* + * It's already mapped. Return the key we allocated already. + */ + key_entry = caa_container_of(key_node, + struct ust_registry_map_key_ht_entry, node); + assert(key_entry); + + key = key_entry->key; + + DBG("Returning map key object associated to the tracer token: key = %p, tracer_token = %"PRIu64, + key_entry->key, tracer_token); + +end: + return key; +} + +int ust_registry_map_add_token_key_mapping(struct ust_registry_session *session, + uint64_t map_key, uint64_t tracer_token, + struct lttng_map_key *key) +{ + int ret; + struct ust_registry_map_key_ht_entry *key_entry; + struct ust_registry_map *map; + const struct lttng_map_key *existing_mapping = NULL; + + rcu_read_lock(); + map = ust_registry_map_find(session, map_key); + if (!map) { + ret = -EINVAL; + goto end; + } + rcu_read_unlock(); + + /* JORAJ check if the mapping already exist, we might want to *move this + * to the caller or at least provide more check if for some scenario + * (PID) this should never happen + */ + existing_mapping = ust_registry_map_find_key_for_token(map, tracer_token); + if (existing_mapping != NULL) { + assert(existing_mapping == key); + ret = 0; + goto end; + } + + key_entry = zmalloc(sizeof(struct ust_registry_map_key_ht_entry)); + if (!key_entry) { + ret = -ENOMEM; + goto end; + } + key_entry->key = key; + + /* Ensure the lifetime of the lttng_map_key object. */ + lttng_map_key_get(key); + + rcu_read_lock(); + + lttng_ht_node_init_u64(&key_entry->node, tracer_token); + lttng_ht_add_unique_u64(map->tracer_token_to_map_key_ht, + &key_entry->node); + + rcu_read_unlock(); + + + ret = 0; +end: + return ret; + +} + +static +int ust_registry_map_find_or_create_index_for_key(struct ust_registry_map *map, + const char *formated_key, uint64_t *index) +{ + int ret; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *index_node; + struct ust_registry_map_index_ht_entry *index_entry; + + assert(map); + assert(formated_key); + + /* + * First try to check if we already mapped this formated key to an + * index. + */ + lttng_ht_lookup(map->key_string_to_bucket_index_ht, + (void *) formated_key, &iter); + + index_node = lttng_ht_iter_get_node_str(&iter); + if (index_node) { + /* + * It's already mapped. Return the index we allocated already. + */ + index_entry = caa_container_of(index_node, + struct ust_registry_map_index_ht_entry, node); + assert(index_entry); + + *index = index_entry->index; + + DBG("Returning an already allocated index for formated key: key = '%s', index = %"PRIu64, + formated_key, *index); + } else { + /* + * It's not mapped. Create a new mapping, add it to the + * hashtable and return it. + */ + index_entry = zmalloc(sizeof(struct ust_registry_map_index_ht_entry)); + if (!index_entry) { + ret = -1; + goto end; + } + + index_entry->index = ust_registry_map_get_next_event_id(map); + index_entry->formated_key = strdup(formated_key); + lttng_ht_node_init_str(&index_entry->node, index_entry->formated_key); + + lttng_ht_add_unique_str(map->key_string_to_bucket_index_ht, + &index_entry->node); + + *index = index_entry->index; + DBG("Allocated counter index for new formated_key: key = '%s', index = %"PRIu64, + formated_key, *index); + } + + ret = 0; +end: + return ret; +} + +/* + * Create a ust_registry_event from the given parameters and add it to the + * registry hash table. If event_id is valid, it is set with the newly created + * event id. + * + * On success, return 0 else a negative value. The created event MUST be unique + * so on duplicate entry -EINVAL is returned. On error, event_id is untouched. + * + * Should be called with session registry mutex held. + */ +int ust_registry_map_create_event(struct ust_registry_session *session, + uint64_t map_key, int session_objd, int map_objd, char *name, + char *sig, size_t nr_fields, struct ustctl_field *fields, + int loglevel_value, char *model_emf_uri, int buffer_type, + uint64_t tracer_token, uint64_t *counter_index_p, + struct ust_app *app) +{ + int ret; + uint64_t counter_index; + struct ust_registry_map *map; + char *formated_key; + const struct lttng_map_key *key; + + assert(session); + assert(name); + assert(sig); + assert(counter_index_p); + + rcu_read_lock(); + + /* + * This should not happen but since it comes from the UST tracer, an + * external party, don't assert and simply validate values. + */ + if (session_objd < 0 || map_objd < 0) { + ret = -EINVAL; + goto error_free; + } + + map = ust_registry_map_find(session, map_key); + if (!map) { + ret = -EINVAL; + goto error_free; + } + + /* Check if we've reached the maximum possible id. */ + if (ust_registry_is_max_id(map->used_event_id)) { + ret = -ENOENT; + goto error_free; + } + + key = ust_registry_map_find_key_for_token(map, tracer_token); + if (!key) { + ERR("Tracer token %"PRIu64" not found for map id = %"PRIu32, + tracer_token, map->map_id); + ret = -EINVAL; + goto error_unlock; + } + + ret = format_event_key(key, name, &formated_key); + if (ret) { + ERR("Error formating key"); + ret = -EINVAL; + goto error_unlock; + } + + ret = ust_registry_map_find_or_create_index_for_key(map, formated_key, + &counter_index); + if (ret) { + ERR("Error finding or creating index for formated_key = '%s'", + formated_key); + free(formated_key); + ret = -EINVAL; + goto error_unlock; + } + + DBG3("UST registry allocating counter index %"PRIu64 " to event: %s, " + "signature: %s, sess_objd: %u, map_objd: %u, map_id: %u", + counter_index, name, sig, session_objd, map_objd, map->map_id); + + *counter_index_p = counter_index; + + rcu_read_unlock(); + return 0; + +error_free: + free(sig); + free(fields); + free(model_emf_uri); +error_unlock: + rcu_read_unlock(); + return ret; +} + + /* * For a given event in a registry, delete the entry and destroy the event. * This MUST be called within a RCU read side lock section. */ -void ust_registry_destroy_event(struct ust_registry_channel *chan, +void ust_registry_chan_destroy_event(struct ust_registry_channel *chan, struct ust_registry_event *event) { int ret; @@ -529,6 +882,73 @@ void ust_registry_destroy_event(struct ust_registry_channel *chan, return; } +/* + * This MUST be called within a RCU read side lock section. + */ +static void ust_registry_map_key_entry_destroy(struct lttng_ht *ht, + struct ust_registry_map_key_ht_entry *entry) +{ + int ret; + struct lttng_ht_iter iter; + + assert(ht); + assert(entry); + + /* Delete the node first. */ + iter.iter.node = &entry->node.node; + ret = lttng_ht_del(ht, &iter); + assert(!ret); + + call_rcu(&entry->node.head, destroy_ust_registry_map_key_ht_entry); + + return; +} + +/* + * This MUST be called within a RCU read side lock section. + */ +static void ust_registry_map_index_ht_entry_destroy(struct lttng_ht *ht, + struct ust_registry_map_index_ht_entry *entry) +{ + int ret; + struct lttng_ht_iter iter; + + assert(ht); + assert(entry); + + /* Delete the node first. */ + iter.iter.node = &entry->node.node; + ret = lttng_ht_del(ht, &iter); + assert(!ret); + + call_rcu(&entry->node.head, destroy_ust_registry_map_index_ht_entry); + + return; +} + +/* + * For a given event in a registry, delete the entry and destroy the event. + * This MUST be called within a RCU read side lock section. + */ +void ust_registry_map_destroy_event(struct ust_registry_map *map, + struct ust_registry_event *event) +{ + int ret; + struct lttng_ht_iter iter; + + assert(map); + assert(event); + + /* Delete the node first. */ + iter.iter.node = &event->node.node; + ret = lttng_ht_del(map->events_ht, &iter); + assert(!ret); + + call_rcu(&event->node.head, destroy_event_rcu); + + return; +} + static void destroy_enum(struct ust_registry_enum *reg_enum) { if (!reg_enum) { @@ -717,6 +1137,33 @@ void destroy_channel_rcu(struct rcu_head *head) free(chan); } +/* + * We need to execute ht_destroy outside of RCU read-side critical + * section and outside of call_rcu thread, so we postpone its execution + * using ht_cleanup_push. It is simpler than to mapge the semantic of + * the many callers of delete_ust_app_session(). + */ +static +void destroy_map_rcu(struct rcu_head *head) +{ + struct ust_registry_map *map = + caa_container_of(head, struct ust_registry_map, rcu_head); + + if (map->events_ht) { + ht_cleanup_push(map->events_ht); + } + + if (map->tracer_token_to_map_key_ht) { + ht_cleanup_push(map->tracer_token_to_map_key_ht); + } + + if (map->key_string_to_bucket_index_ht) { + ht_cleanup_push(map->key_string_to_bucket_index_ht); + } + + free(map); +} + /* * Destroy every element of the registry and free the memory. This does NOT * free the registry pointer since it might not have been allocated before so @@ -745,13 +1192,55 @@ static void destroy_channel(struct ust_registry_channel *chan, bool notif) cds_lfht_for_each_entry( chan->ht->ht, &iter.iter, event, node.node) { /* Delete the node from the ht and free it. */ - ust_registry_destroy_event(chan, event); + ust_registry_chan_destroy_event(chan, event); } rcu_read_unlock(); } call_rcu(&chan->rcu_head, destroy_channel_rcu); } +/* + * Destroy every element of the registry and free the memory. This does NOT + * free the registry pointer since it might not have been allocated before so + * it's the caller responsability. + */ +static void destroy_map(struct ust_registry_map *map) +{ + struct lttng_ht_iter iter; + struct ust_registry_event *event; + struct ust_registry_map_key_ht_entry *key_entry; + struct ust_registry_map_index_ht_entry *index_entry; + + assert(map); + + rcu_read_lock(); + if (map->events_ht) { + /* Destroy all event associated with this registry. */ + cds_lfht_for_each_entry(map->events_ht->ht, &iter.iter, event, node.node) { + /* Delete the node from the ht and free it. */ + ust_registry_map_destroy_event(map, event); + } + } + + /* Destroy all map_key entries associated with this registry. */ + cds_lfht_for_each_entry (map->tracer_token_to_map_key_ht->ht, + &iter.iter, key_entry, node.node) { + ust_registry_map_key_entry_destroy( + map->tracer_token_to_map_key_ht, + key_entry); + } + + /* Destroy all index entry associated with this registry. */ + cds_lfht_for_each_entry(map->key_string_to_bucket_index_ht->ht, + &iter.iter, index_entry, node.node) { + ust_registry_map_index_ht_entry_destroy( + map->key_string_to_bucket_index_ht, + index_entry); + } + rcu_read_unlock(); + call_rcu(&map->rcu_head, destroy_map_rcu); +} + /* * Initialize registry with default values. */ @@ -804,6 +1293,71 @@ error_alloc: return ret; } +/* + * Initialize registry map entry with default values. + */ +int ust_registry_map_add(struct ust_registry_session *session, + uint64_t key) +{ + int ret = 0; + struct ust_registry_map *map; + + assert(session); + + map = zmalloc(sizeof(*map)); + if (!map) { + PERROR("zmalloc ust registry map"); + ret = -ENOMEM; + goto error_alloc; + } + + map->events_ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!map->events_ht) { + ret = -ENOMEM; + goto error; + } + + /* Set custom match function. */ + map->events_ht->match_fct = ht_match_event; + map->events_ht->hash_fct = ht_hash_event; + + map->tracer_token_to_map_key_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!map->tracer_token_to_map_key_ht) { + ret = -ENOMEM; + goto error; + } + + map->key_string_to_bucket_index_ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!map->key_string_to_bucket_index_ht) { + ret = -ENOMEM; + goto error; + } + + /* + * FIXME frdeso: fix this comment + * Assign a map ID right now since the event notification comes + * *before* the map notify so the ID needs to be set at this point so + * the metadata can be dumped for that event. + */ + if (ust_registry_is_max_id(session->used_map_id)) { + ret = -1; + goto error; + } + map->map_id = ust_registry_get_next_map_id(session); + + rcu_read_lock(); + lttng_ht_node_init_u64(&map->node, key); + lttng_ht_add_unique_u64(session->maps, &map->node); + rcu_read_unlock(); + + return 0; + +error: + destroy_map(map); +error_alloc: + return ret; +} + /* * Find a channel in the given registry. RCU read side lock MUST be acquired * before calling this function and as long as the event reference is kept by @@ -834,6 +1388,36 @@ end: return chan; } +/* + * Find a map in the given registry. RCU read side lock MUST be acquired + * before calling this function and as long as the event reference is kept by + * the caller. + * + * On success, the pointer is returned else NULL. + */ +struct ust_registry_map *ust_registry_map_find( + struct ust_registry_session *session, uint64_t key) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + struct ust_registry_map *map = NULL; + + assert(session); + assert(session->maps); + + DBG3("UST registry map finding key %" PRIu64, key); + + lttng_ht_lookup(session->maps, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (!node) { + goto end; + } + map = caa_container_of(node, struct ust_registry_map, node); + +end: + return map; +} + /* * Remove channel using key from registry and free memory. */ @@ -863,6 +1447,35 @@ end: return; } +/* + * Remove map using key from registry and free memory. + */ +void ust_registry_map_del_free(struct ust_registry_session *session, + uint64_t key) +{ + struct lttng_ht_iter iter; + struct ust_registry_map *map; + int ret; + + assert(session); + + rcu_read_lock(); + map = ust_registry_map_find(session, key); + if (!map) { + rcu_read_unlock(); + goto end; + } + + iter.iter.node = &map->node.node; + ret = lttng_ht_del(session->maps, &iter); + assert(!ret); + rcu_read_unlock(); + destroy_map(map); + +end: + return; +} + /* * Initialize registry with default values and set the newly allocated session * pointer to sessionp. @@ -962,6 +1575,12 @@ int ust_registry_session_init(struct ust_registry_session **sessionp, goto error; } + session->maps = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!session->maps) { + lttng_ht_destroy(session->channels); + goto error; + } + ret = lttng_uuid_generate(session->uuid); if (ret) { ERR("Failed to generate UST uuid (errno = %d)", ret); diff --git a/src/bin/lttng-sessiond/ust-registry.h b/src/bin/lttng-sessiond/ust-registry.h index 12614c5d5..1e3d2fc6b 100644 --- a/src/bin/lttng-sessiond/ust-registry.h +++ b/src/bin/lttng-sessiond/ust-registry.h @@ -35,6 +35,10 @@ struct ust_registry_session { uint32_t next_channel_id; /* Once this value reaches UINT32_MAX, no more id can be allocated. */ uint32_t used_channel_id; + /* Next map ID available for a newly registered map. */ + uint32_t next_map_id; + /* Once this value reaches UINT32_MAX, no more id can be allocated. */ + uint32_t used_map_id; /* Next enumeration ID available. */ uint64_t next_enum_id; /* Universal unique identifier used by the tracer. */ @@ -97,6 +101,12 @@ struct ust_registry_session { * be accessed with a RCU read side lock acquired. */ struct lttng_ht *channels; + + /* + * Hash table containing maps sent by the UST tracer. MUST + * be accessed with a RCU read side lock acquired. + */ + struct lttng_ht *maps; /* * Unique key to identify the metadata on the consumer side. */ @@ -162,6 +172,45 @@ struct ust_registry_channel { struct rcu_head rcu_head; }; +struct ust_registry_map_key_ht_entry { + struct lttng_map_key *key; + struct lttng_ht_node_u64 node; +}; + +struct ust_registry_map_index_ht_entry { + uint64_t index; + char *formated_key; + struct lttng_ht_node_str node; +}; + +struct ust_registry_map { + uint64_t key; + /* Id set when replying to a register map. */ + uint32_t map_id; + + /* Indicates if this map registry has already been registered. */ + unsigned int register_done; + + /* + * Hash table containing events sent by the UST tracer. MUST be accessed + * with a RCU read side lock acquired. + */ + struct lttng_ht *events_ht; + /* Next event ID available for a newly registered event. */ + uint32_t next_event_id; + /* Once this value reaches UINT32_MAX, no more id can be allocated. */ + uint32_t used_event_id; + + /* tracer_token -> ust_registry_map_key_ht_entry */ + struct lttng_ht *tracer_token_to_map_key_ht; + /* format key -> ust_registry_map_index_ht_entry */ + struct lttng_ht *key_string_to_bucket_index_ht; + + struct lttng_ht_node_u64 node; + /* For delayed reclaim */ + struct rcu_head rcu_head; +}; + /* * Event registered from a UST tracer sent to the session daemon. This is * indexed and matched by . @@ -170,7 +219,7 @@ struct ust_registry_event { int id; /* Both objd are set by the tracer. */ int session_objd; - int channel_objd; + int container_objd; /* Name of the event returned by the tracer. */ char name[LTTNG_UST_SYM_NAME_LEN]; char *signature; @@ -220,7 +269,7 @@ static inline int ust_registry_is_max_id(uint32_t id) * Return a unique channel ID. If max is reached, the used_event_id counter is * returned. */ -static inline uint32_t ust_registry_get_next_event_id( +static inline uint32_t ust_registry_channel_get_next_event_id( struct ust_registry_channel *r) { if (ust_registry_is_max_id(r->used_event_id)) { @@ -231,6 +280,26 @@ static inline uint32_t ust_registry_get_next_event_id( return r->next_event_id++; } +/* + * Return next available event id and increment the used counter. The + * ust_registry_is_max_id function MUST be called before in order to validate + * if the maximum number of IDs have been reached. If not, it is safe to call + * this function. + * + * Return a unique map ID. If max is reached, the used_event_id counter is + * returned. + */ +static inline uint32_t ust_registry_map_get_next_event_id( + struct ust_registry_map *r) +{ + if (ust_registry_is_max_id(r->used_event_id)) { + return r->used_event_id; + } + + r->used_event_id++; + return r->next_event_id++; +} + /* * Return next available channel id and increment the used counter. The * ust_registry_is_max_id function MUST be called before in order to validate @@ -251,6 +320,26 @@ static inline uint32_t ust_registry_get_next_chan_id( return r->next_channel_id++; } +/* + * Return next available map id and increment the used counter. The + * ust_registry_is_max_id function MUST be called before in order to validate + * if the maximum number of IDs have been reached. If not, it is safe to call + * this function. + * + * Return a unique map ID. If max is reached, the used_map_id counter + * is returned. + */ +static inline uint32_t ust_registry_get_next_map_id( + struct ust_registry_session *r) +{ + if (ust_registry_is_max_id(r->used_map_id)) { + return r->used_map_id; + } + + r->used_map_id++; + return r->next_map_id++; +} + /* * Return registry event count. This is read atomically. */ @@ -262,6 +351,7 @@ static inline uint32_t ust_registry_get_event_count( #ifdef HAVE_LIBLTTNG_UST_CTL +/* Channels */ void ust_registry_channel_destroy(struct ust_registry_session *session, struct ust_registry_channel *chan); struct ust_registry_channel *ust_registry_channel_find( @@ -271,6 +361,19 @@ int ust_registry_channel_add(struct ust_registry_session *session, void ust_registry_channel_del_free(struct ust_registry_session *session, uint64_t key, bool notif); +/* Maps */ +void ust_registry_map_destroy(struct ust_registry_session *session, + struct ust_registry_map *map); +struct ust_registry_map *ust_registry_map_find( + struct ust_registry_session *session, uint64_t key); +int ust_registry_map_add(struct ust_registry_session *session, + uint64_t key); +void ust_registry_map_del_free(struct ust_registry_session *session, + uint64_t key); +int ust_registry_map_add_token_key_mapping(struct ust_registry_session *session, + uint64_t map_key, uint64_t tracer_token, + struct lttng_map_key *key); + int ust_registry_session_init(struct ust_registry_session **sessionp, struct ust_app *app, uint32_t bits_per_long, @@ -290,14 +393,24 @@ int ust_registry_session_init(struct ust_registry_session **sessionp, uid_t tracing_uid); void ust_registry_session_destroy(struct ust_registry_session *session); -int ust_registry_create_event(struct ust_registry_session *session, +int ust_registry_chan_create_event(struct ust_registry_session *session, uint64_t chan_key, int session_objd, int channel_objd, char *name, char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel_value, char *model_emf_uri, int buffer_type, uint32_t *event_id_p, struct ust_app *app); -struct ust_registry_event *ust_registry_find_event( +struct ust_registry_event *ust_registry_chan_find_event( struct ust_registry_channel *chan, char *name, char *sig); -void ust_registry_destroy_event(struct ust_registry_channel *chan, +void ust_registry_chan_destroy_event(struct ust_registry_channel *chan, + struct ust_registry_event *event); + +int ust_registry_map_create_event(struct ust_registry_session *session, + uint64_t map_key, int session_objd, int map_objd, char *name, + char *sig, size_t nr_fields, struct ustctl_field *fields, + int loglevel_value, char *model_emf_uri, int buffer_type, + uint64_t tracer_token, uint64_t *counter_index_p, struct ust_app *app); +struct ust_registry_event *ust_registry_map_find_event( + struct ust_registry_map *map, char *name, char *sig); +void ust_registry_map_destroy_event(struct ust_registry_map *map, struct ust_registry_event *event); /* app can be NULL for registry shared across applications. */ diff --git a/src/bin/lttng/Makefile.am b/src/bin/lttng/Makefile.am index 50ab92988..6c262622f 100644 --- a/src/bin/lttng/Makefile.am +++ b/src/bin/lttng/Makefile.am @@ -14,9 +14,13 @@ lttng_SOURCES = command.h conf.c conf.h commands/start.c \ commands/list.c commands/create.c commands/destroy.c \ commands/stop.c commands/enable_events.c \ commands/disable_events.c commands/enable_channels.c \ + commands/add_map.c \ + commands/enable_map.c \ + commands/disable_map.c \ commands/disable_channels.c commands/add_context.c \ commands/set_session.c commands/version.c \ commands/view.c \ + commands/view_map.c \ commands/snapshot.c \ commands/save.c \ commands/load.c \ @@ -44,4 +48,5 @@ lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ $(top_builddir)/src/common/string-utils/libstring-utils.la \ $(top_builddir)/src/common/filter/libfilter.la \ $(top_builddir)/src/common/argpar/libargpar.la \ - $(POPT_LIBS) + $(POPT_LIBS) \ + -lm diff --git a/src/bin/lttng/command.h b/src/bin/lttng/command.h index bf0045210..6b8c5c504 100644 --- a/src/bin/lttng/command.h +++ b/src/bin/lttng/command.h @@ -60,10 +60,14 @@ DECL_COMMAND(enable_events); DECL_COMMAND(disable_events); DECL_COMMAND(enable_channels); DECL_COMMAND(disable_channels); +DECL_COMMAND(add_map); +DECL_COMMAND(enable_map); +DECL_COMMAND(disable_map); DECL_COMMAND(add_context); DECL_COMMAND(set_session); DECL_COMMAND(version); DECL_COMMAND(view); +DECL_COMMAND(view_map); DECL_COMMAND(enable_consumer); DECL_COMMAND(disable_consumer); DECL_COMMAND(snapshot); diff --git a/src/bin/lttng/commands/add_map.c b/src/bin/lttng/commands/add_map.c new file mode 100644 index 000000000..d9e4ff4c9 --- /dev/null +++ b/src/bin/lttng/commands/add_map.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include + +#include + +#include "common/argpar/argpar.h" +#include "common/utils.h" + +#include "../command.h" +#include "../utils.h" + +#define LTTNG_MAP_DEFAULT_SIZE 4096 + +enum { + OPT_HELP, + OPT_SESSION, + OPT_USERSPACE, + OPT_KERNEL, + OPT_MAX_KEY_COUNT, + OPT_PER_PID, + OPT_PER_UID, + OPT_OVERFLOW, + OPT_BITNESS, + OPT_COALESCE_HITS, +}; + +static const struct argpar_opt_descr add_map_opt_descrs[] = { + + { OPT_HELP, 'h', "help", false }, + { OPT_SESSION, 's', "session", true }, + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + { OPT_MAX_KEY_COUNT, '\0', "max-key-count", true}, + { OPT_PER_PID, '\0', "per-pid", false}, + { OPT_PER_UID, '\0', "per-uid", false}, + { OPT_OVERFLOW, '\0', "overflow", false}, + { OPT_BITNESS, '\0', "bitness", true}, + { OPT_COALESCE_HITS, '\0', "coalesce-hits", false}, + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +int cmd_add_map(int argc, const char **argv) +{ + int ret, i; + enum lttng_error_code error_code_ret; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + bool opt_userspace = false, opt_kernel = false, opt_buffers_uid = false, + opt_buffers_pid = false, opt_overflow = false, opt_coalesce_hits = false; + char *opt_session_name = NULL, *session_name = NULL, *opt_max_key_count = NULL, *opt_bitness = NULL; + const char *opt_map_name = NULL;; + enum lttng_map_bitness bitness_type; + enum lttng_map_boundary_policy boundary_policy; + enum lttng_map_status status; + uint64_t dimensions_sizes[1] = {0}; + unsigned long long bitness; + struct lttng_map *map; + struct lttng_domain dom = {0}; + struct lttng_handle *handle = NULL; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + add_map_opt_descrs, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = CMD_SUCCESS; + goto end; + case OPT_SESSION: + if (!assign_string(&opt_session_name, item_opt->arg, + "-s/--session")) { + goto error; + } + break; + case OPT_USERSPACE: + opt_userspace = true; + break; + case OPT_KERNEL: + opt_kernel = true; + break; + case OPT_MAX_KEY_COUNT: + if (!assign_string(&opt_max_key_count, item_opt->arg, + "--max-key-count")) { + goto error; + } + break; + case OPT_PER_PID: + opt_buffers_pid = true; + break; + case OPT_PER_UID: + opt_buffers_uid = true; + break; + case OPT_OVERFLOW: + opt_overflow = true; + break; + case OPT_BITNESS: + if (!assign_string(&opt_bitness, item_opt->arg, + "--bitness")) { + goto error; + } + break; + case OPT_COALESCE_HITS: + opt_coalesce_hits = true; + break; + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (opt_map_name) { + ERR("Unexpected argument: %s", item_non_opt->arg); + goto error; + } + + opt_map_name = item_non_opt->arg; + } + } + + if (!opt_map_name) { + ERR("Missing map name"); + goto error; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + } else { + session_name = opt_session_name; + } + + /* Check that one and only one domain option was provided. */ + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace, false); + if (ret) { + goto error; + } + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + if (opt_buffers_uid || opt_buffers_pid) { + ERR("Buffer type not supported for kernel domain"); + goto error; + } + dom.buf_type = LTTNG_BUFFER_GLOBAL; + } else { + dom.type = LTTNG_DOMAIN_UST; + + if (opt_buffers_uid && opt_buffers_pid) { + ERR("Only one domain can be specified"); + goto error; + } + if (opt_buffers_pid) { + dom.buf_type = LTTNG_BUFFER_PER_PID; + } else { + /* Defaults to per UID */ + dom.buf_type = LTTNG_BUFFER_PER_UID; + } + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + if (opt_max_key_count) { + unsigned long long max_key_count; + if (utils_parse_unsigned_long_long(opt_max_key_count, &max_key_count) != 0) { + ERR("Failed to parse `%s` as an integer.", opt_max_key_count); + goto error; + } + + dimensions_sizes[0] = max_key_count; + } else { + dimensions_sizes[0] = LTTNG_MAP_DEFAULT_SIZE; + } + + if (opt_bitness) { + if (utils_parse_unsigned_long_long(opt_bitness, &bitness) != 0) { + ERR("Failed to parse `%s` as an integer.", opt_bitness); + goto error; + } + switch (bitness) { + case 32: + bitness_type = LTTNG_MAP_BITNESS_32BITS; + break; + case 64: + bitness_type = LTTNG_MAP_BITNESS_64BITS; + break; + default: + ERR("Bitness %llu not supported", bitness); + goto error; + } + + } else { + bitness_type = LTTNG_MAP_BITNESS_64BITS; + } + + + if (opt_overflow) { + boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + } else { + boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + } + + status = lttng_map_create(opt_map_name, 1, dimensions_sizes, dom.type, + dom.buf_type, bitness_type, boundary_policy, + opt_coalesce_hits, &map); + if (status != LTTNG_MAP_STATUS_OK) { + ERR("Creating map \"%s\"", opt_map_name); + goto error; + } + + error_code_ret = lttng_add_map(handle, map); + if (error_code_ret != LTTNG_OK) { + ERR("Adding map \"%s\": %s", opt_map_name, + lttng_strerror(error_code_ret)); + lttng_map_destroy(map); + goto error; + } + + MSG("Map %s created.", opt_map_name); + ret = CMD_SUCCESS; + + lttng_map_destroy(map); + + goto end; + +error: + ret = CMD_ERROR; +end: + argpar_parse_ret_fini(&argpar_parse_ret); + free(opt_session_name); + free(opt_max_key_count); + free(opt_bitness); + lttng_destroy_handle(handle); + return ret; +} diff --git a/src/bin/lttng/commands/add_trigger.c b/src/bin/lttng/commands/add_trigger.c index b3b1dade1..f42720df6 100644 --- a/src/bin/lttng/commands/add_trigger.c +++ b/src/bin/lttng/commands/add_trigger.c @@ -19,6 +19,15 @@ /* For lttng_event_rule_type_str(). */ #include #include +#include "lttng/event-rule/kernel-function.h" +#include "lttng/event-rule/kernel-probe.h" +#include "lttng/event-rule/syscall.h" +#include +#include "lttng/event-rule/userspace-probe.h" +#include "lttng/kernel-function.h" +#include "lttng/kernel-probe.h" +#include "lttng/log-level-rule.h" +#include "lttng/map-key-internal.h" #include "common/filter/filter-ast.h" #include "common/filter/filter-ir.h" #include "common/dynamic-array.h" @@ -69,6 +78,10 @@ enum { OPT_URL, OPT_PATH, + OPT_SESSION_NAME, + OPT_MAP_NAME, + OPT_KEY, + OPT_CAPTURE, }; @@ -299,6 +312,98 @@ end: return ret; } +static int parse_kernel_function_opts(const char *source, + struct lttng_kernel_function_location **location) +{ + int ret = 0; + int match; + char s_hex[19]; + char name[LTTNG_SYMBOL_NAME_LEN]; + char *symbol_name = NULL; + uint64_t offset; + + /* Check for symbol+offset. */ + match = sscanf(source, + "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "[^'+']+%18s", + name, s_hex); + if (match == 2) { + if (*s_hex == '\0') { + ERR("Kernel function symbol offset is missing."); + goto error; + } + + symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!symbol_name) { + PERROR("Failed to copy kernel function location symbol name."); + goto error; + } + offset = strtoul(s_hex, NULL, 0); + + *location = lttng_kernel_function_location_symbol_create( + symbol_name, offset); + if (!*location) { + ERR("Failed to create symbol kernel function location."); + goto error; + } + + goto end; + } + + /* Check for symbol. */ + if (isalpha(name[0]) || name[0] == '_') { + match = sscanf(source, + "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "s", + name); + if (match == 1) { + symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!symbol_name) { + ERR("Failed to copy kernel function location symbol name."); + goto error; + } + + *location = lttng_kernel_function_location_symbol_create( + symbol_name, 0); + if (!*location) { + ERR("Failed to create symbol kernel function location."); + goto error; + } + + goto end; + } + } + + /* Check for address. */ + match = sscanf(source, "%18s", s_hex); + if (match > 0) { + uint64_t address; + + if (*s_hex == '\0') { + ERR("Invalid kernel function location address."); + goto error; + } + + address = strtoul(s_hex, NULL, 0); + *location = lttng_kernel_function_location_address_create(address); + if (!*location) { + ERR("Failed to create symbol kernel function location."); + goto error; + } + + goto end; + } + +error: + /* No match */ + ret = -1; + *location = NULL; + +end: + free(symbol_name); + return ret; +} + static struct lttng_event_expr *ir_op_load_expr_to_event_expr( const struct ir_load_expression *load_expr, @@ -537,10 +642,12 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) char *error = NULL; int consumed_args = -1; struct lttng_kernel_probe_location *kernel_probe_location = NULL; + struct lttng_kernel_function_location *kernel_function_location = NULL; struct lttng_userspace_probe_location *userspace_probe_location = NULL; struct parse_event_rule_res res = { 0 }; struct lttng_event_expr *event_expr = NULL; struct filter_parser_ctx *parser_ctx = NULL; + struct lttng_log_level_rule *log_level_rule = NULL; /* Was the -a/--all flag provided? */ bool all_events = false; @@ -627,14 +734,18 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) /* Event rule types */ case OPT_FUNCTION: if (!assign_event_rule_type(&event_rule_type, - LTTNG_EVENT_RULE_TYPE_KRETPROBE)) { + LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION)) { + goto error; + } + + if (!assign_string(&source, item_opt->arg, "source")) { goto error; } break; case OPT_PROBE: if (!assign_event_rule_type(&event_rule_type, - LTTNG_EVENT_RULE_TYPE_KPROBE)) { + LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE)) { goto error; } @@ -645,7 +756,7 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) break; case OPT_USERSPACE_PROBE: if (!assign_event_rule_type(&event_rule_type, - LTTNG_EVENT_RULE_TYPE_UPROBE)) { + LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE)) { goto error; } @@ -817,9 +928,9 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) /* Validate event rule type against domain. */ switch (event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_KPROBE: - case LTTNG_EVENT_RULE_TYPE_KRETPROBE: - case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: case LTTNG_EVENT_RULE_TYPE_SYSCALL: if (domain_type != LTTNG_DOMAIN_KERNEL) { ERR("Event type not available for user-space tracing."); @@ -938,15 +1049,20 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) } if (loglevel_only) { - event_rule_status = lttng_event_rule_tracepoint_set_log_level( - res.er, - loglevel); + log_level_rule = lttng_log_level_rule_exactly_create(loglevel); } else { - event_rule_status = lttng_event_rule_tracepoint_set_log_level_range_lower_bound( - res.er, - loglevel); + log_level_rule = lttng_log_level_rule_at_least_as_severe_as_create(loglevel); + } + + if (log_level_rule == NULL) { + ERR("Failed to create log level rule object."); + goto error; } + event_rule_status = + lttng_event_rule_tracepoint_set_log_level_rule( + res.er, log_level_rule); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to set log level on event fule."); goto error; @@ -955,16 +1071,11 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) break; } - case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: { int ret; enum lttng_event_rule_status event_rule_status; - res.er = lttng_event_rule_kprobe_create(); - if (!res.er) { - ERR("Failed to create kprobe event rule."); - goto error; - } ret = parse_kernel_probe_opts(source, &kernel_probe_location); if (ret) { @@ -972,22 +1083,49 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) goto error; } - event_rule_status = lttng_event_rule_kprobe_set_name(res.er, tracepoint_name); + assert(kernel_probe_location); + res.er = lttng_event_rule_kernel_probe_create(kernel_probe_location); + if (!res.er) { + ERR("Failed to create kprobe event rule."); + goto error; + } + + event_rule_status = lttng_event_rule_kernel_probe_set_event_name(res.er, tracepoint_name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to set kprobe event rule's name to '%s'.", tracepoint_name); goto error; } - assert(kernel_probe_location); - event_rule_status = lttng_event_rule_kprobe_set_location(res.er, kernel_probe_location); + break; + } + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + { + int ret; + enum lttng_event_rule_status event_rule_status; + + + ret = parse_kernel_function_opts(source, &kernel_function_location); + if (ret) { + ERR("Failed to parse kernel function location."); + goto error; + } + + assert(kernel_function_location); + res.er = lttng_event_rule_kernel_function_create(kernel_function_location); + if (!res.er) { + ERR("Failed to create kfunction event rule."); + goto error; + } + + event_rule_status = lttng_event_rule_kernel_function_set_event_name(res.er, tracepoint_name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set kprobe event rule's location."); + ERR("Failed to set kfunction event rule's name to '%s'.", tracepoint_name); goto error; } break; } - case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: { int ret; enum lttng_event_rule_status event_rule_status; @@ -999,20 +1137,13 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) goto error; } - res.er = lttng_event_rule_uprobe_create(); + res.er = lttng_event_rule_userspace_probe_create(userspace_probe_location); if (!res.er) { ERR("Failed to create userspace probe event rule."); goto error; } - event_rule_status = lttng_event_rule_uprobe_set_location( - res.er, userspace_probe_location); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set user space probe event rule's location."); - goto error; - } - - event_rule_status = lttng_event_rule_uprobe_set_name( + event_rule_status = lttng_event_rule_userspace_probe_set_event_name( res.er, tracepoint_name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to set user space probe event rule's name to '%s'.", @@ -1080,6 +1211,7 @@ end: strutils_free_null_terminated_array_of_strings(exclusion_list); lttng_kernel_probe_location_destroy(kernel_probe_location); lttng_userspace_probe_location_destroy(userspace_probe_location); + lttng_log_level_rule_destroy(log_level_rule); return res; } @@ -1096,7 +1228,7 @@ struct lttng_condition *handle_condition_event(int *argc, const char ***argv) goto error; } - c = lttng_condition_event_rule_create(res.er); + c = lttng_condition_on_event_create(res.er); lttng_event_rule_destroy(res.er); res.er = NULL; if (!c) { @@ -1112,9 +1244,12 @@ struct lttng_condition *handle_condition_event(int *argc, const char ***argv) assert(expr); assert(*expr); - status = lttng_condition_event_rule_append_capture_descriptor( + status = lttng_condition_on_event_append_capture_descriptor( c, *expr); if (status != LTTNG_CONDITION_STATUS_OK) { + if (status == LTTNG_CONDITION_STATUS_UNSUPPORTED) { + ERR("The capture feature is unsupported by the event-rule type"); + } goto error; } @@ -1764,6 +1899,139 @@ end: return action; } +static const struct argpar_opt_descr incr_value_action_opt_descrs[] = { + { OPT_SESSION_NAME, 's', "session", true }, + { OPT_MAP_NAME, 'm', "map", true }, + { OPT_KEY, '\0', "key", true }, + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +struct lttng_action *handle_action_incr_value(int *argc, + const char ***argv) +{ + struct lttng_action *action = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + struct lttng_map_key *key = NULL; + char *session_name_arg = NULL, *map_name_arg = NULL; + char *key_arg = NULL; + char *error = NULL; + enum lttng_action_status action_status; + + state = argpar_state_create(*argc, *argv, incr_value_action_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_SESSION_NAME: + if (!assign_string(&session_name_arg, item_opt->arg, "--session/-s")) { + goto error; + } + break; + case OPT_MAP_NAME: + if (!assign_string(&map_name_arg, item_opt->arg, "--map/-m")) { + goto error; + } + break; + case OPT_KEY: + if (!assign_string(&key_arg, item_opt->arg, "--key")) { + goto error; + } + break; + default: + abort(); + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + ERR("Missing session name."); + goto error; + } + + if (!map_name_arg) { + ERR("Missing map name."); + goto error; + } + + if (!key_arg) { + ERR("Missing key"); + goto error; + } + + key = lttng_map_key_parse_from_string(key_arg); + if (!key) { + ERR("Error parsing key argument"); + goto error; + } + + action = lttng_action_incr_value_create(); + if (!action) { + ERR("Failed to allocate incr-value action."); + goto error; + } + + action_status = lttng_action_incr_value_set_session_name(action, + session_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set action incr-value's session name."); + goto error; + } + + action_status = lttng_action_incr_value_set_map_name(action, + map_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set action incr-value's map name."); + goto error; + } + + action_status = lttng_action_incr_value_set_key(action, key); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set action incr-value's key"); + goto error; + } + + goto end; + +error: + lttng_action_destroy(action); + action = NULL; + +end: + lttng_map_key_destroy(key); + free(session_name_arg); + free(map_name_arg); + free(key_arg); + return action; +} + struct action_descr { const char *name; struct lttng_action *(*handler) (int *argc, const char ***argv); @@ -1776,6 +2044,7 @@ struct action_descr action_descrs[] = { { "stop-session", handle_action_stop_session }, { "rotate-session", handle_action_rotate_session }, { "snapshot-session", handle_action_snapshot_session }, + { "incr-value", handle_action_incr_value }, }; static @@ -1834,6 +2103,30 @@ struct argpar_opt_descr add_trigger_options[] = { ARGPAR_OPT_DESCR_SENTINEL, }; +static +bool action_is_tracer_executed(const struct lttng_action *action) +{ + bool is_tracer_executed; + switch (lttng_action_get_type(action)) { + case LTTNG_ACTION_TYPE_NOTIFY: + case LTTNG_ACTION_TYPE_START_SESSION: + case LTTNG_ACTION_TYPE_STOP_SESSION: + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + is_tracer_executed = false; + goto end; + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + is_tracer_executed = true; + goto end; + case LTTNG_ACTION_TYPE_GROUP: + default: + abort(); + } + +end: + return is_tracer_executed; +} + static void lttng_actions_destructor(void *p) { @@ -2021,6 +2314,18 @@ int cmd_add_trigger(int argc, const char **argv) enum lttng_action_status status; action = lttng_dynamic_pointer_array_steal_pointer(&actions, i); + if (action_is_tracer_executed(action)) { + if (fire_every_str || fire_once_after_str) { + /* + * Firing policy with tracer-executed actions + * (`incr-value`) is not supported at the + * moment. It's not clear how the tracers will + * handle the different policies efficiently. + */ + ERR("Can't use --fire-once-after or --fire-every with tracer executed action (incr-value)"); + goto error; + } + } status = lttng_action_group_add_action(action_group, action); if (status != LTTNG_ACTION_STATUS_OK) { diff --git a/src/bin/lttng/commands/disable_map.c b/src/bin/lttng/commands/disable_map.c new file mode 100644 index 000000000..5985b539c --- /dev/null +++ b/src/bin/lttng/commands/disable_map.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include + +#include + +#include "common/argpar/argpar.h" + +#include "../command.h" +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_KERNEL, + OPT_SESSION, + OPT_USERSPACE, +}; + +static const +struct argpar_opt_descr disable_map_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_SESSION, 's', "session", true }, + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +int cmd_disable_map(int argc, const char **argv) +{ + int ret, i; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + const char *opt_map_name = NULL; + enum lttng_error_code error_code_ret; + bool opt_userspace = false, opt_kernel = false; + char *opt_session_name = NULL, *session_name = NULL; + struct lttng_domain dom = {0}; + struct lttng_handle *handle; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + disable_map_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + case OPT_SESSION: + if (!assign_string(&opt_session_name, item_opt->arg, + "-s/--session")) { + goto error; + } + break; + case OPT_USERSPACE: + opt_userspace = true; + break; + case OPT_KERNEL: + opt_kernel = true; + break; + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (opt_map_name) { + ERR("Unexpected argument: %s", item_non_opt->arg); + goto error; + } + + opt_map_name = item_non_opt->arg; + } + } + + if (!opt_map_name) { + ERR("Missing `name` argument."); + goto error; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + } else { + session_name = opt_session_name; + } + + /* Check that one and only one domain option was provided. */ + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace, false); + if (ret) { + goto error; + } + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + dom.buf_type = LTTNG_BUFFER_GLOBAL; + } else { + dom.type=LTTNG_DOMAIN_UST; + dom.buf_type = LTTNG_BUFFER_PER_UID; + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + error_code_ret = lttng_disable_map(handle, opt_map_name); + if (error_code_ret != LTTNG_OK) { + ERR("Error disabling map \"%s\"", opt_map_name); + goto error; + } + + MSG("Disabled map `%s`.", opt_map_name); + + ret = 0; + goto end; + +error: + ret = 1; + +end: + argpar_parse_ret_fini(&argpar_parse_ret); + + return ret; +} diff --git a/src/bin/lttng/commands/enable_map.c b/src/bin/lttng/commands/enable_map.c new file mode 100644 index 000000000..f246fb99a --- /dev/null +++ b/src/bin/lttng/commands/enable_map.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include + +#include + +#include "common/argpar/argpar.h" + +#include "../command.h" +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_KERNEL, + OPT_SESSION, + OPT_USERSPACE, +}; + +static const +struct argpar_opt_descr enable_map_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_SESSION, 's', "session", true }, + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +int cmd_enable_map(int argc, const char **argv) +{ + int ret, i; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + const char *opt_map_name = NULL; + enum lttng_error_code error_code_ret; + bool opt_userspace = false, opt_kernel = false; + char *opt_session_name = NULL, *session_name = NULL; + struct lttng_domain dom = {0}; + struct lttng_handle *handle; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + enable_map_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + case OPT_SESSION: + if (!assign_string(&opt_session_name, item_opt->arg, + "-s/--session")) { + goto error; + } + break; + case OPT_USERSPACE: + opt_userspace = true; + break; + case OPT_KERNEL: + opt_kernel = true; + break; + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (opt_map_name) { + ERR("Unexpected argument: %s", item_non_opt->arg); + goto error; + } + + opt_map_name = item_non_opt->arg; + } + } + + if (!opt_map_name) { + ERR("Missing `name` argument."); + goto error; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + } else { + session_name = opt_session_name; + } + + /* Check that one and only one domain option was provided. */ + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace, false); + if (ret) { + goto error; + } + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + dom.buf_type = LTTNG_BUFFER_GLOBAL; + } else { + dom.type=LTTNG_DOMAIN_UST; + dom.buf_type = LTTNG_BUFFER_PER_UID; + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + error_code_ret = lttng_enable_map(handle, opt_map_name); + if (error_code_ret != LTTNG_OK) { + ERR("Error enabling map \"%s\"", opt_map_name); + goto error; + } + + MSG("Enabled map `%s`.", opt_map_name); + + ret = 0; + goto end; + +error: + ret = 1; + +end: + argpar_parse_ret_fini(&argpar_parse_ret); + + return ret; +} diff --git a/src/bin/lttng/commands/list.c b/src/bin/lttng/commands/list.c index 454017794..77a49e172 100644 --- a/src/bin/lttng/commands/list.c +++ b/src/bin/lttng/commands/list.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../command.h" @@ -28,6 +29,7 @@ static int opt_jul; static int opt_log4j; static int opt_python; static char *opt_channel; +static char *opt_map; static int opt_domain; static int opt_fields; static int opt_syscall; @@ -63,6 +65,7 @@ static struct poptOption long_options[] = { {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, {"channel", 'c', POPT_ARG_STRING, &opt_channel, 0, 0, 0}, + {"map", 'm', POPT_ARG_STRING, &opt_map, 0, 0, 0}, {"domain", 'd', POPT_ARG_VAL, &opt_domain, 1, 0, 0}, {"fields", 'f', POPT_ARG_VAL, &opt_fields, 1, 0, 0}, {"syscall", 'S', POPT_ARG_VAL, &opt_syscall, 1, 0, 0}, @@ -1367,6 +1370,58 @@ skip_stats_printing: return; } +static void print_map(const struct lttng_map *map) +{ + const char *map_name; + enum lttng_map_status map_status; + enum lttng_buffer_type buffer_type; + enum lttng_map_bitness bitness; + enum lttng_map_boundary_policy boundary_policy; + uint64_t bucket_count; + bool is_enabled; + + bitness = lttng_map_get_bitness(map); + is_enabled = lttng_map_get_is_enabled(map); + buffer_type = lttng_map_get_buffer_type(map); + + boundary_policy = lttng_map_get_boundary_policy(map); + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to get map name"); + goto end; + } + + map_status = lttng_map_get_dimension_length(map, 0, &bucket_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to bucket count"); + goto end; + } + + MSG("- %s (%s)", map_name, is_enabled ? "enabled": "disabled"); + MSG("%sAttributes:", indent4); + MSG("%sBitness: %s", indent6, bitness == LTTNG_MAP_BITNESS_32BITS ? "32" : "64"); + _MSG("%sCounter type: ", indent6); + switch (buffer_type) { + case LTTNG_BUFFER_PER_PID: + MSG("per-pid"); + break; + case LTTNG_BUFFER_PER_UID: + MSG("per-uid"); + break; + case LTTNG_BUFFER_GLOBAL: + MSG("global"); + break; + default: + abort(); + } + MSG("%sBoundary policy: %s", indent6, + boundary_policy == LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW ? "OVERFLOW" : ""); + MSG("%sBucket count: %"PRIu64, indent6, bucket_count); +end: + return; +} + /* * Machine interface * Print a list of channel @@ -1508,6 +1563,68 @@ error_channels: return ret; } +/* + * List map(s) of session and domain. + * + * If map_name is NULL, all maps are listed. + */ +static int list_maps(const char *desired_map_name) +{ + struct lttng_map_list *map_list = NULL; + enum lttng_map_status map_status; + enum lttng_error_code ret_code; + unsigned int i, map_count; + int ret = CMD_SUCCESS; + + DBG("Listing map(s) (%s)", desired_map_name ? : ""); + + ret_code = lttng_list_maps(handle, &map_list); + if (ret_code != LTTNG_OK) { + ERR("Error getting map list"); + ret = CMD_ERROR; + goto end; + } + + map_status = lttng_map_list_get_count(map_list, &map_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting map list element count"); + ret = CMD_ERROR; + goto end; + } + + MSG("Maps:\n-------------"); + for (i = 0; i < map_count; i++) { + const struct lttng_map *map = NULL; + const char *map_name = NULL; + map = lttng_map_list_get_at_index(map_list, i); + if (!map) { + ret = CMD_ERROR; + ERR("Error getting map from list: index = %u", i); + goto end; + } + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting map name"); + ret = CMD_ERROR; + goto end; + } + + if (desired_map_name != NULL) { + if (strncmp(map_name, desired_map_name, NAME_MAX) == 0) { + print_map(map); + break; + } + } else { + print_map(map); + } + } +end: + // Should be in its own patch. + lttng_map_list_destroy(map_list); + return ret; +} + static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr) { switch (process_attr) { @@ -2460,6 +2577,11 @@ int cmd_list(int argc, const char **argv) goto end; } + ret = list_maps(opt_map); + if (ret) { + goto end; + } + if (lttng_opt_mi) { /* Close domain and domain element */ ret = mi_lttng_close_multi_element(writer, 2); @@ -2560,6 +2682,11 @@ int cmd_list(int argc, const char **argv) goto end; } + ret = list_maps(opt_map); + if (ret) { + goto end; + } + next_domain: if (lttng_opt_mi) { /* Close domain element */ diff --git a/src/bin/lttng/commands/list_triggers.c b/src/bin/lttng/commands/list_triggers.c index a6f021193..e9ad3d661 100644 --- a/src/bin/lttng/commands/list_triggers.c +++ b/src/bin/lttng/commands/list_triggers.c @@ -14,8 +14,20 @@ #include "common/mi-lttng.h" /* For lttng_condition_type_str(). */ #include "lttng/condition/condition-internal.h" +#include "lttng/condition/on-event.h" +#include "lttng/condition/on-event-internal.h" /* For lttng_domain_type_str(). */ #include "lttng/domain-internal.h" +#include "lttng/event-rule/event-rule-internal.h" +#include "lttng/event-rule/kernel-probe.h" +#include "lttng/event-rule/kernel-probe-internal.h" +#include "lttng/event-rule/syscall.h" +#include "lttng/event-rule/tracepoint.h" +#include "lttng/event-rule/userspace-probe.h" +#include "lttng/map-key.h" +#include "lttng/map-key-internal.h" +#include "lttng/trigger/trigger-internal.h" +#include "lttng/kernel-probe.h" #ifdef LTTNG_EMBED_HELP static const char help_msg[] = @@ -43,6 +55,7 @@ void print_event_rule_tracepoint(const struct lttng_event_rule *event_rule) const char *pattern; const char *filter; int log_level; + const struct lttng_log_level_rule *log_level_rule = NULL; unsigned int exclusions_count; int i; @@ -65,20 +78,33 @@ void print_event_rule_tracepoint(const struct lttng_event_rule *event_rule) assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); } - event_rule_status = lttng_event_rule_tracepoint_get_log_level( - event_rule, &log_level); + event_rule_status = lttng_event_rule_tracepoint_get_log_level_rule( + event_rule, &log_level_rule); if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { - enum lttng_loglevel_type log_level_type; + enum lttng_log_level_rule_status llr_status; const char *log_level_op; - event_rule_status = lttng_event_rule_tracepoint_get_log_level_type( - event_rule, &log_level_type); - assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); - assert(log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE || - log_level_type == LTTNG_EVENT_LOGLEVEL_SINGLE); + switch (lttng_log_level_rule_get_type(log_level_rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &log_level); + log_level_op = "=="; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + log_level_op = "<="; + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &log_level); + break; + default: + abort(); + } - log_level_op = (log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE ? "<=" : "=="); + assert(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + /* TODO: here the raw level value must be printed since the + * value could have no known string equivalent, the string + * representation is only a "convenience". + */ _MSG(", log level %s %s", log_level_op, mi_lttng_loglevel_string( log_level, domain_type)); @@ -160,21 +186,21 @@ end: } static -void print_event_rule_kprobe(const struct lttng_event_rule *event_rule) +void print_event_rule_kernel_probe(const struct lttng_event_rule *event_rule) { enum lttng_event_rule_status event_rule_status; const char *name; const struct lttng_kernel_probe_location *location; - assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KPROBE); + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE); - event_rule_status = lttng_event_rule_kprobe_get_name(event_rule, &name); + event_rule_status = lttng_event_rule_kernel_probe_get_event_name(event_rule, &name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to get kprobe event rule's name."); goto end; } - event_rule_status = lttng_event_rule_kprobe_get_location( + event_rule_status = lttng_event_rule_kernel_probe_get_location( event_rule, &location); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to get kprobe event rule's location."); @@ -192,22 +218,22 @@ end: } static -void print_event_rule_uprobe(const struct lttng_event_rule *event_rule) +void print_event_rule_userspace_probe(const struct lttng_event_rule *event_rule) { enum lttng_event_rule_status event_rule_status; const char *name; const struct lttng_userspace_probe_location *location; enum lttng_userspace_probe_location_type userspace_probe_location_type; - assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_UPROBE); + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE); - event_rule_status = lttng_event_rule_uprobe_get_name(event_rule, &name); + event_rule_status = lttng_event_rule_userspace_probe_get_event_name(event_rule, &name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to get uprobe event rule's name."); goto end; } - event_rule_status = lttng_event_rule_uprobe_get_location( + event_rule_status = lttng_event_rule_userspace_probe_get_location( event_rule, &location); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to get uprobe event rule's location."); @@ -280,11 +306,11 @@ void print_event_rule(const struct lttng_event_rule *event_rule) case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: print_event_rule_tracepoint(event_rule); break; - case LTTNG_EVENT_RULE_TYPE_KPROBE: - print_event_rule_kprobe(event_rule); + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: + print_event_rule_kernel_probe(event_rule); break; - case LTTNG_EVENT_RULE_TYPE_UPROBE: - print_event_rule_uprobe(event_rule); + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: + print_event_rule_userspace_probe(event_rule); break; case LTTNG_EVENT_RULE_TYPE_SYSCALL: print_event_rule_syscall(event_rule); @@ -295,16 +321,180 @@ void print_event_rule(const struct lttng_event_rule *event_rule) } static -void print_condition_event_rule_hit(const struct lttng_condition *condition) +void print_one_event_expr(const struct lttng_event_expr *event_expr) +{ + enum lttng_event_expr_type type; + + type = lttng_event_expr_get_type(event_expr); + + switch (type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: { + const char *name; + + name = lttng_event_expr_event_payload_field_get_name(event_expr); + _MSG("%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: { + const char *name; + + name = lttng_event_expr_channel_context_field_get_name(event_expr); + _MSG("$ctx.%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: { + const char *provider_name; + const char *type_name; + + provider_name = + lttng_event_expr_app_specific_context_field_get_provider_name( + event_expr); + type_name = + lttng_event_expr_app_specific_context_field_get_type_name( + event_expr); + + _MSG("$app.%s:%s", provider_name, type_name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: { + unsigned int index; + const struct lttng_event_expr *parent_expr; + enum lttng_event_expr_status status; + + parent_expr = lttng_event_expr_array_field_element_get_parent_expr( + event_expr); + assert(parent_expr != NULL); + + print_one_event_expr(parent_expr); + + status = lttng_event_expr_array_field_element_get_index( + event_expr, &index); + assert(status == LTTNG_EVENT_EXPR_STATUS_OK); + + _MSG("[%u]", index); + + break; + } + + default: + abort(); + } +} + +static +void print_condition_on_event(const struct lttng_condition *condition) { const struct lttng_event_rule *event_rule; enum lttng_condition_status condition_status; + unsigned int cap_desc_count, i; + uint64_t error_count; condition_status = - lttng_condition_event_rule_get_rule(condition, &event_rule); + lttng_condition_on_event_get_rule(condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); print_event_rule(event_rule); + + condition_status + = lttng_condition_on_event_get_capture_descriptor_count( + condition, &cap_desc_count); + assert(condition_status == LTTNG_CONDITION_STATUS_OK); + + error_count = lttng_condition_on_event_get_error_count(condition); + MSG(" tracer notifications discarded: %ld", error_count); + + if (cap_desc_count > 0) { + MSG(" captures:"); + + for (i = 0; i < cap_desc_count; i++) { + const struct lttng_event_expr *cap_desc = + lttng_condition_on_event_get_capture_descriptor_at_index( + condition, i); + + _MSG(" - "); + print_one_event_expr(cap_desc); + MSG(""); + } + } +} + +static +void print_map_key(const struct lttng_map_key *key) +{ + unsigned int i, token_count; + enum lttng_map_key_status key_status; + + _MSG(" key: `"); + key_status = lttng_map_key_get_token_count(key, &token_count); + assert(key_status == LTTNG_MAP_KEY_STATUS_OK); + + for (i = 0; i < token_count; i++) { + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + assert(token); + + switch (token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + const struct lttng_map_key_token_string *str_token; + str_token = (typeof(str_token)) token; + _MSG("%s", str_token->string); + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + const struct lttng_map_key_token_variable *var_token; + var_token = (typeof(var_token)) token; + + switch (var_token->type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + _MSG("${EVENT_NAME}"); + break; + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + _MSG("${PROVIDER_NAME}"); + break; + default: + abort(); + } + + break; + } + default: + abort(); + } + } + MSG("`"); +} + +static +void print_one_incr_value_action(const struct lttng_action *action) +{ + enum lttng_action_status action_status; + const char *session_name, *map_name; + const struct lttng_map_key *key; + + action_status = lttng_action_incr_value_get_session_name( + action, &session_name); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + action_status = lttng_action_incr_value_get_map_name( + action, &map_name); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + action_status = lttng_action_incr_value_get_key( + action, &key); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + MSG("increment value:"); + MSG(" session: `%s`", session_name); + MSG(" map: `%s`", map_name); + print_map_key(key); } static @@ -318,6 +508,9 @@ void print_one_action(const struct lttng_action *action) assert(action_type != LTTNG_ACTION_TYPE_GROUP); switch (action_type) { + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + print_one_incr_value_action(action); + break; case LTTNG_ACTION_TYPE_NOTIFY: MSG("notify"); break; @@ -447,8 +640,8 @@ void print_one_trigger(const struct lttng_trigger *trigger) condition_type = lttng_condition_get_type(condition); MSG(" condition: %s", lttng_condition_type_str(condition_type)); switch (condition_type) { - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: - print_condition_event_rule_hit(condition); + case LTTNG_CONDITION_TYPE_ON_EVENT: + print_condition_on_event(condition); break; default: MSG(" (condition type not handled in %s)", __func__); diff --git a/src/bin/lttng/commands/view_map.c b/src/bin/lttng/commands/view_map.c new file mode 100644 index 000000000..7d8dac546 --- /dev/null +++ b/src/bin/lttng/commands/view_map.c @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "../command.h" + +#include "common/argpar/argpar.h" + +enum { + OPT_HELP, + OPT_SESSION, + OPT_LIST_OPTIONS, + OPT_USERSPACE, + OPT_KERNEL, + OPT_KEY, +}; + +static const +struct argpar_opt_descr view_map_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_SESSION, 's', "session", true }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + /* Domains */ + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + { OPT_KEY, '\0', "key", true }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +bool assign_domain_type(enum lttng_domain_type *dest, + enum lttng_domain_type src) +{ + bool ret; + + if (*dest == LTTNG_DOMAIN_NONE || *dest == src) { + *dest = src; + ret = true; + } else { + ERR("Multiple domains specified."); + ret = false; + } + + return ret; +} + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +static +int compare_key_value_by_key(const void *a, const void *b) +{ + const struct lttng_map_key_value_pair *kv_a = + *((const struct lttng_map_key_value_pair **) a); + const struct lttng_map_key_value_pair *kv_b = + *((const struct lttng_map_key_value_pair **) b); + const char *key_a, *key_b; + enum lttng_map_status map_status; + + map_status = lttng_map_key_value_pair_get_key(kv_a, &key_a); + assert(map_status == LTTNG_MAP_STATUS_OK); + + map_status = lttng_map_key_value_pair_get_key(kv_b, &key_b); + assert(map_status == LTTNG_MAP_STATUS_OK); + + return strcmp(key_a, key_b); +} + +static +void print_one_map_key_value_pair(const struct lttng_map_key_value_pair *kv_pair, + size_t key_len, size_t val_len) +{ + const char *key = NULL; + int64_t value; + enum lttng_map_status status; + bool has_overflowed, has_underflowed; + + status = lttng_map_key_value_pair_get_key(kv_pair, &key); + if (status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to get key-value pair's key."); + goto end; + } + + status = lttng_map_key_value_pair_get_value(kv_pair, &value); + if (status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to get value-value pair's value."); + goto end; + } + + has_overflowed = lttng_map_key_value_pair_get_has_overflowed(kv_pair); + has_underflowed = lttng_map_key_value_pair_get_has_underflowed(kv_pair); + + /* Ensure the padding is nice using the `%*s` delimiter. */ + MSG("| %*s | %*"PRId64" | %d | %d |", (int) -key_len, key, + (int) val_len, value, has_underflowed, has_overflowed); + +end: + return; +} + +static +void print_line(size_t key_len, size_t val_len) +{ + int i; + + _MSG("+"); + for (i = 0; i < (int) key_len + 2; i++) { + _MSG("-"); + } + _MSG("+"); + for (i = 0; i < (int) val_len + 2; i++) { + _MSG("-"); + } + MSG("+----+----+"); +} + +static +size_t number_of_digit(int64_t val) +{ + size_t ret = 0; + + if (val == 0) { + ret = 1; + goto end; + } + + if (val < 0) { + /* Account for the minus sign. */ + ret++; + val = llabs(val); + } + + /* + * SOURCE: + * https://stackoverflow.com/questions/1068849/how-do-i-determine-the-number-of-digits-of-an-integer-in-c + * If the log10() call becomes too expensive, we could use a + * recursive approach to count the digits. + */ + ret += floor(log10(val)) + 1; + +end: + return ret; +} + +static +void print_map_section_identifier(const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + switch (lttng_map_key_value_pair_list_get_type(kv_pair_list)) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL: + _MSG("Kernel global map"); + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED: + _MSG("Per-PID dead app aggregated map"); + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + _MSG("PID: %"PRIu64, lttng_map_key_value_pair_list_get_identifer( + kv_pair_list)); + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + _MSG("UID: %"PRIu64, lttng_map_key_value_pair_list_get_identifer( + kv_pair_list)); + break; + default: + break; + } + _MSG(", CPU: "); + if (lttng_map_key_value_pair_list_get_summed_all_cpu(kv_pair_list)) { + MSG("ALL"); + } else { + MSG("%"PRIu64, lttng_map_key_value_pair_list_get_cpu(kv_pair_list)); + } +} + +static +void print_map_table_header(size_t max_key_len, size_t max_val_len) +{ + print_line(max_key_len, max_val_len); + /* Ensure the padding is nice using the `%*s` delimiter. */ + MSG("| %*s | %*s | %s | %s |", (int) -max_key_len, "key", + (int) max_val_len, "val", "uf", "of"); +} + +static +enum lttng_error_code print_one_map_section( + const struct lttng_map_key_value_pair_list *kv_pair_list, + enum lttng_buffer_type buffer_type) +{ + enum lttng_error_code ret; + enum lttng_map_status map_status; + size_t longest_key_len = strlen("key"), longest_val_len = strlen("val"); + unsigned int i, key_value_pair_count; + struct lttng_dynamic_pointer_array sorted_kv_pair_list; + + lttng_dynamic_pointer_array_init(&sorted_kv_pair_list, NULL); + + map_status = lttng_map_key_value_pair_list_get_count(kv_pair_list, + &key_value_pair_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to get key-value pair count."); + goto error; + } + + for (i = 0; i < key_value_pair_count; i++) { + const char *cur_key; + int64_t cur_val; + const struct lttng_map_key_value_pair *pair = + lttng_map_key_value_pair_list_get_at_index( + kv_pair_list, i); + + /* Add all key value pairs to the sorting array. */ + lttng_dynamic_pointer_array_add_pointer(&sorted_kv_pair_list, + (void *) pair); + + /* Keep track of the longest key. */ + lttng_map_key_value_pair_get_key(pair, &cur_key); + longest_key_len = max(longest_key_len, strlen(cur_key)); + + /* Keep track of the longest value. */ + lttng_map_key_value_pair_get_value(pair, &cur_val); + longest_val_len = max(longest_val_len, number_of_digit(cur_val)); + } + + qsort(sorted_kv_pair_list.array.buffer.data, + lttng_dynamic_pointer_array_get_count(&sorted_kv_pair_list), + sizeof(struct lttng_map_key_value_pair *), + compare_key_value_by_key); + + print_map_section_identifier(kv_pair_list); + if (key_value_pair_count == 0) { + MSG(" No value in the map"); + ret = LTTNG_OK; + goto end; + } else { + print_map_table_header(longest_key_len, longest_val_len); + + for (i = 0; i < key_value_pair_count; i++) { + print_line(longest_key_len, longest_val_len); + + print_one_map_key_value_pair( + lttng_dynamic_pointer_array_get_pointer( + &sorted_kv_pair_list, i), + longest_key_len, longest_val_len); + } + + print_line(longest_key_len, longest_val_len); + } + + ret = LTTNG_OK; + goto end; + +error: + ret = LTTNG_ERR_MAP_VALUES_LIST_FAIL; +end: + lttng_dynamic_pointer_array_reset(&sorted_kv_pair_list); + + return ret; +} + +static +enum lttng_error_code print_one_map(struct lttng_handle *handle, + const struct lttng_map *map, const char *key_filter) +{ + enum lttng_error_code ret; + enum lttng_map_status map_status; + struct lttng_map_content *map_content = NULL; + unsigned int i, map_content_section_count; + enum lttng_buffer_type buffer_type; + struct lttng_map_query *map_query = NULL; + enum lttng_map_query_config_buffer query_buffer_config; + enum lttng_map_query_config_app_bitness query_bitness_config; + enum lttng_map_query_status query_status; + const char *map_name = NULL; + enum lttng_map_bitness map_bitness; + + map_status = lttng_map_get_name(map, &map_name); + assert(map_status == LTTNG_MAP_STATUS_OK); + + map_bitness = lttng_map_get_bitness(map); + + switch (lttng_map_get_buffer_type(map)) { + case LTTNG_BUFFER_GLOBAL: + query_buffer_config = LTTNG_MAP_QUERY_CONFIG_BUFFER_KERNEL_GLOBAL; + query_bitness_config = LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_KERNEL; + break; + case LTTNG_BUFFER_PER_UID: + query_buffer_config = LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL; + query_bitness_config = LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + break; + case LTTNG_BUFFER_PER_PID: + query_buffer_config = LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL; + query_bitness_config = LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + break; + default: + ret = LTTNG_ERR_INVALID; + goto end; + } + + map_query = lttng_map_query_create(LTTNG_MAP_QUERY_CONFIG_CPU_ALL, + query_buffer_config, query_bitness_config); + if (!map_query) { + ret = LTTNG_ERR_NOMEM; + ERR("Creating map query"); + goto end; + } + + if (key_filter) { + query_status = lttng_map_query_add_key_filter(map_query, key_filter); + assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + query_status = lttng_map_query_set_sum_by_cpu(map_query, true); + assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + + /* + * We don't want to aggregate all uid (or pid) together for the lttng + * view-map command. + */ + if (query_buffer_config == LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL) { + //query_status = lttng_map_query_set_sum_by_uid(map_query, false); + //assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + } else if (query_buffer_config == LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL) { + query_status = lttng_map_query_set_sum_by_pid(map_query, false); + assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + /* + * If we have 32bits and 64bits maps, we want to aggregate both maps in + * a single table in the lttng view-map command. + */ + if (query_bitness_config == LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL) { + //query_status = lttng_map_query_set_sum_by_app_bitness(map_query, true); + //assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + /* Fetch the key value pair_list from the sessiond */ + ret = lttng_list_map_content(handle, map, map_query, &map_content); + if (ret != LTTNG_OK) { + ERR("Error listing map key value pair_list: %s.", + lttng_strerror(-ret)); + goto end; + } + + map_status = lttng_map_content_get_count(map_content, + &map_content_section_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + ERR("Failed to get map content section count."); + goto end; + } + + if (map_content_section_count == 0) { + DBG("Map %s is not found", map_name); + goto end; + } + + MSG("Session: '%s', map: '%s', map bitness: %d", handle->session_name, + map_name, map_bitness); + + buffer_type = lttng_map_content_get_buffer_type(map_content); + + for (i = 0; i < map_content_section_count; i++) { + const struct lttng_map_key_value_pair_list *kv_pair_list = + lttng_map_content_get_at_index(map_content, i); + + assert(kv_pair_list); + ret = print_one_map_section(kv_pair_list, buffer_type); + if (ret != LTTNG_OK) { + ERR("Error printing map section"); + goto end; + } + } + + + ret = LTTNG_OK; + goto end; +end: + lttng_map_content_destroy(map_content); + return ret; +} + +static +int view_map(struct lttng_handle *handle, const char *desired_map_name, + const char *key_filter) +{ + enum lttng_error_code ret; + struct lttng_map_list *map_list = NULL; + enum lttng_map_status map_status; + bool desired_map_found = false; + unsigned int i, map_count; + + DBG("Listing map(s) (%s)", desired_map_name ? : ""); + /* + * Query the sessiond for a list of all the maps that match the + * provided map name and domain (if any). + */ + ret = lttng_list_maps(handle, &map_list); + if (ret != LTTNG_OK) { + ERR("Error getting map list"); + goto end; + } + + map_status = lttng_map_list_get_count(map_list, &map_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting map list element count"); + ret = -1; + goto end; + } + + for (i = 0; i < map_count; i++) { + const struct lttng_map *map = NULL; + const char *map_name = NULL; + map = lttng_map_list_get_at_index(map_list, i); + if (!map) { + ERR("Error getting map from list: index = %u", i); + goto end; + } + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting map name"); + ret = -1; + goto end; + } + + + if (desired_map_name != NULL) { + if (strncmp(map_name, desired_map_name, NAME_MAX) == 0) { + desired_map_found = true; + ret = print_one_map(handle, map, key_filter); + if (ret != LTTNG_OK) { + ret = -1; + goto end; + } + } + } + } + + if (desired_map_name && !desired_map_found) { + DBG("Map %s in domain: %s (session %s)", desired_map_name, + lttng_strerror(-ret), handle->session_name); + ret = LTTNG_ERR_MAP_NOT_FOUND; + goto end; + } + +end: + lttng_map_list_destroy(map_list); + return ret; +} + +int cmd_view_map(int argc, const char **argv) +{ + struct argpar_parse_ret argpar_parse_ret = { 0 }; + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + const char *opt_map_name = NULL;; + char *opt_session_name = NULL, *session_name = NULL; + char *opt_key_filter = NULL; + struct lttng_domain domain; + struct lttng_domain *domains = NULL; + struct lttng_handle *handle; + + int ret, i; + + memset(&domain, 0, sizeof(domain)); + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + view_map_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = CMD_SUCCESS; + goto end; + case OPT_SESSION: + if (!assign_string(&opt_session_name, item_opt->arg, + "-s/--session")) { + goto error; + } + break; + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, + view_map_options); + ret = CMD_SUCCESS; + goto end; + case OPT_USERSPACE: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_UST)) { + goto error; + } + break; + + case OPT_KERNEL: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_KERNEL)) { + goto error; + } + break; + case OPT_KEY: + if (!assign_string(&opt_key_filter, item_opt->arg, + "--key")) { + goto error; + } + break; + + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (opt_map_name) { + ERR("Unexpected argument: %s", item_non_opt->arg); + goto error; + } + opt_map_name = item_non_opt->arg; + } + } + + if (!opt_map_name) { + ERR("Map name must be provided"); + goto error; + } + + if (!opt_session_name) { + DBG("No session name provided, print maps of the default session"); + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + } else { + session_name = opt_session_name; + } + + domain.type = domain_type; + handle = lttng_create_handle(session_name, &domain); + if (handle == NULL) { + ret = CMD_FATAL; + goto end; + } + + if (domain.type != LTTNG_DOMAIN_NONE) { + /* Print maps of the given domain. */ + ret = view_map(handle, opt_map_name, opt_key_filter); + if (ret != LTTNG_OK) { + goto error; + } + } else { + int i, nb_domain; + bool found_one_map = false; + + /* We want all domain(s) */ + nb_domain = lttng_list_domains(session_name, &domains); + if (nb_domain < 0) { + ret = CMD_ERROR; + ERR("%s", lttng_strerror(nb_domain)); + goto end; + } + + for (i = 0; i < nb_domain; i++) { + /* Clean handle before creating a new one */ + if (handle) { + lttng_destroy_handle(handle); + } + + handle = lttng_create_handle(session_name, &domains[i]); + if (handle == NULL) { + ret = CMD_FATAL; + goto end; + } + + ret = view_map(handle, opt_map_name, opt_key_filter); + + if (ret == LTTNG_OK) { + found_one_map = true; + } else if (ret == LTTNG_ERR_MAP_NOT_FOUND) { + DBG("Map not found in the current domain"); + continue; + } else { + goto error; + } + } + + if (!found_one_map) { + ERR("Map %s not found on any of the domains", opt_map_name); + goto error; + + } + } + + ret = CMD_SUCCESS; + goto end; +error: + ret = CMD_ERROR; + +end: + if (!opt_session_name && session_name) { + free(session_name); + } + + if (opt_key_filter) { + free(opt_key_filter); + } + + argpar_parse_ret_fini(&argpar_parse_ret); + return ret; +} diff --git a/src/bin/lttng/lttng.c b/src/bin/lttng/lttng.c index 437a9cc4c..61ed24773 100644 --- a/src/bin/lttng/lttng.c +++ b/src/bin/lttng/lttng.c @@ -73,6 +73,9 @@ static struct cmd_struct commands[] = { { "disable-event", cmd_disable_events}, { "enable-channel", cmd_enable_channels}, { "enable-event", cmd_enable_events}, + { "add-map", cmd_add_map}, + { "enable-map", cmd_enable_map}, + { "disable-map", cmd_disable_map}, { "help", NULL}, { "list", cmd_list}, { "list-triggers", cmd_list_triggers}, @@ -93,6 +96,7 @@ static struct cmd_struct commands[] = { { "untrack", cmd_untrack}, { "version", cmd_version}, { "view", cmd_view}, + { "view-map", cmd_view_map}, { NULL, NULL} /* Array closure */ }; @@ -286,6 +290,10 @@ static void show_basic_help(void) puts(" disable-channel " CONFIG_CMD_DESCR_DISABLE_CHANNEL); puts(" enable-channel " CONFIG_CMD_DESCR_ENABLE_CHANNEL); puts(""); + puts("Maps:"); + puts(" add-map " CONFIG_CMD_DESCR_ADD_MAP); + puts(" remove-map " CONFIG_CMD_DESCR_ADD_MAP); + puts(""); puts("Event rules:"); puts(" disable-event " CONFIG_CMD_DESCR_DISABLE_EVENT); puts(" enable-event " CONFIG_CMD_DESCR_ENABLE_EVENT); diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 4b549aa08..339a55492 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -36,6 +36,7 @@ EXTRA_DIST = mi-lttng-4.0.xsd libcommon_la_SOURCES = \ actions/action.c \ actions/group.c \ + actions/incr-value.c \ actions/notify.c \ actions/rotate-session.c \ actions/snapshot-session.c \ @@ -45,7 +46,7 @@ libcommon_la_SOURCES = \ common.h \ conditions/buffer-usage.c \ conditions/condition.c \ - conditions/event-rule.c \ + conditions/on-event.c \ conditions/session-consumed-size.c \ conditions/session-rotation.c \ context.c context.h \ @@ -62,16 +63,23 @@ libcommon_la_SOURCES = \ event-expr-to-bytecode.c event-expr-to-bytecode.h \ event-field-value.c \ event-rule/event-rule.c \ - event-rule/kprobe.c \ + event-rule/kernel-function.c \ + event-rule/kernel-probe.c \ event-rule/syscall.c \ - event-rule/uprobe.c \ + event-rule/userspace-probe.c \ event-rule/tracepoint.c \ filter.c filter.h \ fd-handle.c fd-handle.h \ fs-handle.c fs-handle.h fs-handle-internal.h \ futex.c futex.h \ + kernel-function.c \ kernel-probe.c \ + index-allocator.c index-allocator.h \ location.c \ + log-level-rule.c \ + map.c \ + map-query.c \ + map-key/map-key.c \ mi-lttng.c mi-lttng.h \ notification.c \ optional.h \ @@ -80,6 +88,7 @@ libcommon_la_SOURCES = \ pipe.c pipe.h \ readwrite.c readwrite.h \ runas.c runas.h \ + shm.c shm.h \ session-descriptor.c \ snapshot.c snapshot.h \ spawn-viewer.c spawn-viewer.h \ diff --git a/src/common/actions/action.c b/src/common/actions/action.c index 95a0c0f4d..2bf5e4877 100644 --- a/src/common/actions/action.c +++ b/src/common/actions/action.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ const char *lttng_action_type_string(enum lttng_action_type action_type) return "START_SESSION"; case LTTNG_ACTION_TYPE_STOP_SESSION: return "STOP_SESSION"; + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + return "INCREMENT_VALUE"; default: return "???"; } @@ -182,6 +185,10 @@ ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view, create_from_payload_cb = lttng_action_stop_session_create_from_payload; break; + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + create_from_payload_cb = + lttng_action_incr_value_create_from_payload; + break; case LTTNG_ACTION_TYPE_GROUP: create_from_payload_cb = lttng_action_group_create_from_payload; break; diff --git a/src/common/actions/group.c b/src/common/actions/group.c index 4ac239c9d..f7bbfd5a9 100644 --- a/src/common/actions/group.c +++ b/src/common/actions/group.c @@ -353,3 +353,11 @@ const struct lttng_action *lttng_action_group_get_at_index( end: return action; } + +struct lttng_action *lttng_action_group_get_mutable_at_index( + struct lttng_action *group, + unsigned int index) +{ + return (struct lttng_action *) lttng_action_group_get_at_index( + (const struct lttng_action *) group, index); +} diff --git a/src/common/actions/incr-value.c b/src/common/actions/incr-value.c new file mode 100644 index 000000000..0c4ddc2d3 --- /dev/null +++ b/src/common/actions/incr-value.c @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define IS_INCR_VALUE_ACTION(action) \ + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_INCREMENT_VALUE) + +struct lttng_action_incr_value { + struct lttng_action parent; + + /* Owned by this. */ + struct lttng_map_key *key; + /* Owned by this. */ + char *session_name; + /* Owned by this. */ + char *map_name; + + LTTNG_OPTIONAL(uint64_t) action_tracer_token; +}; + +struct lttng_action_incr_value_comm { + /* Includes the trailing \0. */ + uint32_t session_name_len; + /* Includes the trailing \0. */ + uint32_t map_name_len; + + /* + * Variable data: + * + * - struct lttng_map_key object with variable data + * - session name (null terminated) + * - map name (null terminated) + */ + char data[]; +} LTTNG_PACKED; + +static struct lttng_action_incr_value *action_incr_value_from_action( + struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_incr_value, parent); +} + +static const struct lttng_action_incr_value * +action_incr_value_from_action_const(const struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_incr_value, parent); +} + +static bool lttng_action_incr_value_validate(struct lttng_action *action) +{ + bool valid; + struct lttng_action_incr_value *action_incr_value; + + if (!action) { + valid = false; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + /* Non null key is mandatory. */ + if (!action_incr_value->key) { + valid = false; + goto end; + } + + /* A non-empty session name is mandatory. */ + if (!action_incr_value->session_name || + strlen(action_incr_value->session_name) == 0) { + valid = false; + goto end; + } + + /* A non-empty map name is mandatory. */ + if (!action_incr_value->map_name || + strlen(action_incr_value->map_name) == 0) { + valid = false; + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_incr_value_is_equal( + const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + const struct lttng_action_incr_value *a, *b; + + a = action_incr_value_from_action_const(_a); + b = action_incr_value_from_action_const(_b); + + /* Action is not valid if this is not true. */ + assert(a->key); + assert(b->key); + assert(a->session_name); + assert(b->session_name); + assert(a->map_name); + assert(b->map_name); + + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + if (strcmp(a->map_name, b->map_name)) { + goto end; + } + + is_equal = lttng_map_key_is_equal(a->key, b->key); + +end: + return is_equal; +} + +static int lttng_action_incr_value_serialize( + struct lttng_action *action, struct lttng_payload *payload) +{ + struct lttng_action_incr_value *action_incr_value; + struct lttng_action_incr_value_comm comm; + size_t session_name_len, map_name_len; + int ret; + + assert(action); + assert(payload); + + action_incr_value = action_incr_value_from_action(action); + + DBG("Serializing increment value action"); + + session_name_len = strlen(action_incr_value->session_name) + 1; + comm.session_name_len = session_name_len; + + map_name_len = strlen(action_incr_value->map_name) + 1; + comm.map_name_len = map_name_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_map_key_serialize(action_incr_value->key, payload); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + action_incr_value->session_name, session_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + action_incr_value->map_name, map_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +static void lttng_action_incr_value_destroy(struct lttng_action *action) +{ + struct lttng_action_incr_value *action_incr_value; + + if (!action) { + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + lttng_map_key_destroy(action_incr_value->key); + + free(action_incr_value->session_name); + free(action_incr_value->map_name); + free(action_incr_value); + +end: + return; +} + +ssize_t lttng_action_incr_value_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len, consumed_key_len, ret; + struct lttng_map_key *key = NULL; + const struct lttng_action_incr_value_comm *comm; + const char *session_name, *map_name; + struct lttng_action *action; + enum lttng_action_status status; + + action = lttng_action_incr_value_create(); + if (!action) { + consumed_len = -1; + goto error; + } + + comm = (typeof(comm)) view->buffer.data; + consumed_len = sizeof(struct lttng_action_incr_value_comm); + + { + struct lttng_payload_view key_view = + lttng_payload_view_from_view(view, consumed_len, + view->buffer.size - consumed_len); + + if (!lttng_payload_view_is_valid(&key_view)) { + consumed_len = -1; + goto end; + } + ret = lttng_map_key_create_from_payload(&key_view, &key); + if (ret <= 0) { + consumed_len = -1; + goto error; + } + consumed_key_len = ret; + } + + consumed_len += consumed_key_len; + session_name = (const char *) &comm->data + consumed_key_len; + + if (!lttng_buffer_view_contains_string( + &view->buffer, session_name, comm->session_name_len)) { + consumed_len = -1; + goto error; + } + + consumed_len += comm->session_name_len; + + map_name = (const char *) &comm->data + consumed_key_len + comm->session_name_len; + + if (!lttng_buffer_view_contains_string( + &view->buffer, map_name, comm->map_name_len)) { + consumed_len = -1; + goto error; + } + + consumed_len += comm->map_name_len; + + status = lttng_action_incr_value_set_key(action, key); + /* Ownership is passed to the action. */ + lttng_map_key_put(key); + + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto error; + } + + status = lttng_action_incr_value_set_session_name( + action, session_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto error; + } + + status = lttng_action_incr_value_set_map_name( + action, map_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto error; + } + + *p_action = action; + action = NULL; + goto end; + +error: + lttng_action_incr_value_destroy(action); + consumed_len = -1; + +end: + return consumed_len; +} + +struct lttng_action *lttng_action_incr_value_create(void) +{ + struct lttng_action *action; + + action = zmalloc(sizeof(struct lttng_action_incr_value)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_INCREMENT_VALUE, + lttng_action_incr_value_validate, + lttng_action_incr_value_serialize, + lttng_action_incr_value_is_equal, + lttng_action_incr_value_destroy); + +end: + return action; +} + +enum lttng_action_status lttng_action_incr_value_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !session_name || + strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + free(action_incr_value->session_name); + + action_incr_value->session_name = strdup(session_name); + if (!action_incr_value->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_incr_value_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action_const(action); + + *session_name = action_incr_value->session_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_incr_value_set_map_name( + struct lttng_action *action, const char *map_name) +{ + struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !map_name || + strlen(map_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + free(action_incr_value->map_name); + + action_incr_value->map_name = strdup(map_name); + if (!action_incr_value->map_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_incr_value_get_map_name( + const struct lttng_action *action, const char **map_name) +{ + const struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !map_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action_const(action); + + *map_name = action_incr_value->map_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +LTTNG_HIDDEN +enum lttng_action_status lttng_action_incr_value_borrow_key_mutable( + const struct lttng_action *action, struct lttng_map_key **key) +{ + const struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !key) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action_const(action); + + *key = action_incr_value->key; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_incr_value_get_key( + const struct lttng_action *action, + const struct lttng_map_key **key) +{ + struct lttng_map_key *mutable_key = NULL; + enum lttng_action_status status = + lttng_action_incr_value_borrow_key_mutable( + action, &mutable_key); + + *key = mutable_key; + return status; +} + +enum lttng_action_status +lttng_action_incr_value_set_key( + struct lttng_action *action, + struct lttng_map_key *key) +{ + struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !key) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + /* Take a reference to the key. */ + lttng_map_key_get(key); + action_incr_value->key = key; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +LTTNG_HIDDEN +enum lttng_action_status lttng_action_incr_value_set_tracer_token( + struct lttng_action *action, uint64_t token) +{ + struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + LTTNG_OPTIONAL_SET(&action_incr_value->action_tracer_token, token); + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +LTTNG_HIDDEN +uint64_t lttng_action_incr_value_get_tracer_token( + const struct lttng_action *action) +{ + const struct lttng_action_incr_value *action_incr_value; + + assert(action); + assert(IS_INCR_VALUE_ACTION(action)); + + action_incr_value = action_incr_value_from_action_const(action); + + return LTTNG_OPTIONAL_GET(action_incr_value->action_tracer_token); +} + diff --git a/src/common/conditions/condition.c b/src/common/conditions/condition.c index e0b85e429..4948dbd3f 100644 --- a/src/common/conditions/condition.c +++ b/src/common/conditions/condition.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -170,8 +170,8 @@ ssize_t lttng_condition_create_from_payload( case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: create_from_payload = lttng_condition_session_rotation_completed_create_from_payload; break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: - create_from_payload = lttng_condition_event_rule_create_from_payload; + case LTTNG_CONDITION_TYPE_ON_EVENT: + create_from_payload = lttng_condition_on_event_create_from_payload; break; default: ERR("Attempted to create condition of unknown type (%i)", @@ -230,7 +230,7 @@ const char *lttng_condition_type_str(enum lttng_condition_type type) case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: return "session rotation completed"; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: return "event rule hit"; default: diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/on-event.c similarity index 54% rename from src/common/conditions/event-rule.c rename to src/common/conditions/on-event.c index dcc31ef86..699de467d 100644 --- a/src/common/conditions/event-rule.c +++ b/src/common/conditions/on-event.c @@ -10,12 +10,14 @@ #include #include #include +#include #include -#include -#include -#include +#include +#include #include #include +#include +#include #include #include #include @@ -23,38 +25,38 @@ #define IS_EVENT_RULE_CONDITION(condition) \ (lttng_condition_get_type(condition) == \ - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) + LTTNG_CONDITION_TYPE_ON_EVENT) static bool is_event_rule_evaluation(const struct lttng_evaluation *evaluation) { enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); - return type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; + return type == LTTNG_CONDITION_TYPE_ON_EVENT; } -static bool lttng_condition_event_rule_validate( +static bool lttng_condition_on_event_validate( const struct lttng_condition *condition); -static int lttng_condition_event_rule_serialize( +static int lttng_condition_on_event_serialize( const struct lttng_condition *condition, struct lttng_payload *payload); -static bool lttng_condition_event_rule_is_equal( +static bool lttng_condition_on_event_is_equal( const struct lttng_condition *_a, const struct lttng_condition *_b); -static void lttng_condition_event_rule_destroy( +static void lttng_condition_on_event_destroy( struct lttng_condition *condition); -static bool lttng_condition_event_rule_validate( +static bool lttng_condition_on_event_validate( const struct lttng_condition *condition) { bool valid = false; - struct lttng_condition_event_rule *event_rule; + struct lttng_condition_on_event *event_rule; if (!condition) { goto end; } event_rule = container_of( - condition, struct lttng_condition_event_rule, parent); + condition, struct lttng_condition_on_event, parent); if (!event_rule->rule) { ERR("Invalid event rule condition: a rule must be set."); goto end; @@ -196,12 +198,12 @@ end: static struct lttng_capture_descriptor * -lttng_condition_event_rule_get_internal_capture_descriptor_at_index( +lttng_condition_on_event_get_internal_capture_descriptor_at_index( const struct lttng_condition *condition, unsigned int index) { - const struct lttng_condition_event_rule *event_rule_cond = + const struct lttng_condition_on_event *event_rule_cond = container_of(condition, - const struct lttng_condition_event_rule, + const struct lttng_condition_on_event, parent); struct lttng_capture_descriptor *desc = NULL; unsigned int count; @@ -211,7 +213,7 @@ lttng_condition_event_rule_get_internal_capture_descriptor_at_index( goto end; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition, &count); if (status != LTTNG_CONDITION_STATUS_OK) { goto end; @@ -227,13 +229,14 @@ end: return desc; } -static int lttng_condition_event_rule_serialize( +static int lttng_condition_on_event_serialize( const struct lttng_condition *condition, struct lttng_payload *payload) { int ret; - struct lttng_condition_event_rule *event_rule; + struct lttng_condition_on_event *event_rule; enum lttng_condition_status status; + uint64_t error_count, error_counter_index; /* Used for iteration and communication (size matters). */ uint32_t i, capture_descr_count; @@ -244,7 +247,7 @@ static int lttng_condition_event_rule_serialize( DBG("Serializing event rule condition"); event_rule = container_of( - condition, struct lttng_condition_event_rule, parent); + condition, struct lttng_condition_on_event, parent); DBG("Serializing event rule condition's event rule"); ret = lttng_event_rule_serialize(event_rule->rule, payload); @@ -252,7 +255,27 @@ static int lttng_condition_event_rule_serialize( goto end; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + error_counter_index = lttng_condition_on_event_get_error_counter_index( + condition); + DBG("Serializing event rule condition's error counter index: %" PRIu64, + error_counter_index); + ret = lttng_dynamic_buffer_append(&payload->buffer, &error_counter_index, + sizeof(error_counter_index)); + if (ret) { + goto end; + } + + error_count = lttng_condition_on_event_get_error_count( + condition); + DBG("Serializing event rule condition's error count: %" PRIu64, + error_count); + ret = lttng_dynamic_buffer_append(&payload->buffer, &error_count, + sizeof(error_count)); + if (ret) { + goto end; + } + + status = lttng_condition_on_event_get_capture_descriptor_count( condition, &capture_descr_count); if (status != LTTNG_CONDITION_STATUS_OK) { ret = -1; @@ -269,7 +292,7 @@ static int lttng_condition_event_rule_serialize( for (i = 0; i < capture_descr_count; i++) { const struct lttng_capture_descriptor *desc = - lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + lttng_condition_on_event_get_internal_capture_descriptor_at_index( condition, i); DBG("Serializing event rule condition's capture descriptor %" PRIu32, @@ -295,13 +318,13 @@ bool capture_descriptors_are_equal( size_t i; enum lttng_condition_status status; - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition_a, &capture_descr_count_a); if (status != LTTNG_CONDITION_STATUS_OK) { goto not_equal; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition_b, &capture_descr_count_b); if (status != LTTNG_CONDITION_STATUS_OK) { goto not_equal; @@ -313,11 +336,11 @@ bool capture_descriptors_are_equal( for (i = 0; i < capture_descr_count_a; i++) { const struct lttng_event_expr *expr_a = - lttng_condition_event_rule_get_capture_descriptor_at_index( + lttng_condition_on_event_get_capture_descriptor_at_index( condition_a, i); const struct lttng_event_expr *expr_b = - lttng_condition_event_rule_get_capture_descriptor_at_index( + lttng_condition_on_event_get_capture_descriptor_at_index( condition_b, i); @@ -335,15 +358,15 @@ end: return is_equal; } -static bool lttng_condition_event_rule_is_equal( +static bool lttng_condition_on_event_is_equal( const struct lttng_condition *_a, const struct lttng_condition *_b) { bool is_equal = false; - struct lttng_condition_event_rule *a, *b; + struct lttng_condition_on_event *a, *b; - a = container_of(_a, struct lttng_condition_event_rule, parent); - b = container_of(_b, struct lttng_condition_event_rule, parent); + a = container_of(_a, struct lttng_condition_on_event, parent); + b = container_of(_b, struct lttng_condition_on_event, parent); /* Both event rules must be set or both must be unset. */ if ((a->rule && !b->rule) || (!a->rule && b->rule)) { @@ -362,13 +385,13 @@ end: return is_equal; } -static void lttng_condition_event_rule_destroy( +static void lttng_condition_on_event_destroy( struct lttng_condition *condition) { - struct lttng_condition_event_rule *event_rule; + struct lttng_condition_on_event *event_rule; event_rule = container_of( - condition, struct lttng_condition_event_rule, parent); + condition, struct lttng_condition_on_event, parent); lttng_event_rule_put(event_rule->rule); lttng_dynamic_pointer_array_reset(&event_rule->capture_descriptors); @@ -386,32 +409,35 @@ void destroy_capture_descriptor(void *ptr) free(desc); } -struct lttng_condition *lttng_condition_event_rule_create( +struct lttng_condition *lttng_condition_on_event_create( struct lttng_event_rule *rule) { struct lttng_condition *parent = NULL; - struct lttng_condition_event_rule *condition = NULL; + struct lttng_condition_on_event *condition = NULL; if (!rule) { goto end; } - condition = zmalloc(sizeof(struct lttng_condition_event_rule)); + condition = zmalloc(sizeof(struct lttng_condition_on_event)); if (!condition) { return NULL; } lttng_condition_init(&condition->parent, - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); - condition->parent.validate = lttng_condition_event_rule_validate, - condition->parent.serialize = lttng_condition_event_rule_serialize, - condition->parent.equal = lttng_condition_event_rule_is_equal, - condition->parent.destroy = lttng_condition_event_rule_destroy, + LTTNG_CONDITION_TYPE_ON_EVENT); + condition->parent.validate = lttng_condition_on_event_validate, + condition->parent.serialize = lttng_condition_on_event_serialize, + condition->parent.equal = lttng_condition_on_event_is_equal, + condition->parent.destroy = lttng_condition_on_event_destroy, lttng_event_rule_get(rule); condition->rule = rule; rule = NULL; + LTTNG_OPTIONAL_SET(&condition->error_count, 0); + LTTNG_OPTIONAL_SET(&condition->error_counter_index, 0); + lttng_dynamic_pointer_array_init(&condition->capture_descriptors, destroy_capture_descriptor); @@ -576,7 +602,7 @@ end: } LTTNG_HIDDEN -ssize_t lttng_condition_event_rule_create_from_payload( +ssize_t lttng_condition_on_event_create_from_payload( struct lttng_payload_view *view, struct lttng_condition **_condition) { @@ -584,6 +610,7 @@ ssize_t lttng_condition_event_rule_create_from_payload( size_t offset = 0; ssize_t event_rule_length; uint32_t i, capture_descr_count; + uint64_t error_counter_index, error_count; struct lttng_condition *condition = NULL; struct lttng_event_rule *event_rule = NULL; @@ -604,16 +631,33 @@ ssize_t lttng_condition_event_rule_create_from_payload( goto error; } - /* Create condition (no capture descriptors yet) at this point. */ - condition = lttng_condition_event_rule_create(event_rule); + offset += event_rule_length; + + /* Error counter index. */ + error_counter_index = uint_from_buffer(&view->buffer, sizeof(uint64_t), + &offset); + if (error_counter_index == UINT64_C(-1)) { + goto error; + } + + /* Error count. */ + error_count = uint_from_buffer(&view->buffer, sizeof(uint64_t), &offset); + if (error_count == UINT64_C(-1)) { + goto error; + } + + /* Create condition (no capture descriptors yet) at this point */ + condition = lttng_condition_on_event_create(event_rule); if (!condition) { goto error; } + lttng_condition_on_event_set_error_count(condition, error_count); + lttng_condition_on_event_set_error_counter_index(condition, + error_counter_index); /* Capture descriptor count. */ assert(event_rule_length >= 0); - offset += (size_t) event_rule_length; capture_descr_count = uint_from_buffer(&view->buffer, sizeof(uint32_t), &offset); if (capture_descr_count == UINT32_C(-1)) { goto error; @@ -630,7 +674,7 @@ ssize_t lttng_condition_event_rule_create_from_payload( } /* Move ownership of `expr` to `condition`. */ - status = lttng_condition_event_rule_append_capture_descriptor( + status = lttng_condition_on_event_append_capture_descriptor( condition, expr); if (status != LTTNG_CONDITION_STATUS_OK) { /* `expr` not moved: destroy it. */ @@ -654,11 +698,11 @@ end: } LTTNG_HIDDEN -enum lttng_condition_status lttng_condition_event_rule_borrow_rule_mutable( +enum lttng_condition_status lttng_condition_on_event_borrow_rule_mutable( const struct lttng_condition *condition, struct lttng_event_rule **rule) { - struct lttng_condition_event_rule *event_rule; + struct lttng_condition_on_event *event_rule; enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !rule) { @@ -667,7 +711,7 @@ enum lttng_condition_status lttng_condition_event_rule_borrow_rule_mutable( } event_rule = container_of( - condition, struct lttng_condition_event_rule, parent); + condition, struct lttng_condition_on_event, parent); if (!event_rule->rule) { status = LTTNG_CONDITION_STATUS_UNSET; goto end; @@ -678,30 +722,71 @@ end: return status; } -enum lttng_condition_status lttng_condition_event_rule_get_rule( +enum lttng_condition_status lttng_condition_on_event_get_rule( const struct lttng_condition *condition, const struct lttng_event_rule **rule) { struct lttng_event_rule *mutable_rule = NULL; const enum lttng_condition_status status = - lttng_condition_event_rule_borrow_rule_mutable( + lttng_condition_on_event_borrow_rule_mutable( condition, &mutable_rule); *rule = mutable_rule; return status; } +void lttng_condition_on_event_set_error_counter_index( + struct lttng_condition *condition, uint64_t error_counter_index) +{ + struct lttng_condition_on_event *on_event_cond = + container_of(condition, + struct lttng_condition_on_event, parent); + + LTTNG_OPTIONAL_SET(&on_event_cond->error_counter_index, error_counter_index); +} + +uint64_t lttng_condition_on_event_get_error_counter_index( + const struct lttng_condition *condition) +{ + struct lttng_condition_on_event *on_event_cond = + container_of(condition, + struct lttng_condition_on_event, parent); + + return LTTNG_OPTIONAL_GET(on_event_cond->error_counter_index); +} + +void lttng_condition_on_event_set_error_count(struct lttng_condition *condition, + uint64_t error_count) +{ + struct lttng_condition_on_event *on_event_cond = + container_of(condition, + struct lttng_condition_on_event, parent); + + LTTNG_OPTIONAL_SET(&on_event_cond->error_count, error_count); +} + +uint64_t lttng_condition_on_event_get_error_count( + const struct lttng_condition *condition) +{ + struct lttng_condition_on_event *on_event_cond = + container_of(condition, + struct lttng_condition_on_event, parent); + + return LTTNG_OPTIONAL_GET(on_event_cond->error_count); +} + enum lttng_condition_status -lttng_condition_event_rule_append_capture_descriptor( +lttng_condition_on_event_append_capture_descriptor( struct lttng_condition *condition, struct lttng_event_expr *expr) { int ret; enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - struct lttng_condition_event_rule *event_rule_cond = + struct lttng_condition_on_event *on_event_cond = container_of(condition, - struct lttng_condition_event_rule, parent); + struct lttng_condition_on_event, parent); struct lttng_capture_descriptor *descriptor = NULL; + const struct lttng_event_rule *rule = NULL; /* Only accept l-values. */ if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !expr || @@ -710,6 +795,29 @@ lttng_condition_event_rule_append_capture_descriptor( goto end; } + status = lttng_condition_on_event_get_rule(condition, &rule); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + switch(lttng_event_rule_get_type(rule)) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + /* Supported */ + status = LTTNG_CONDITION_STATUS_OK; + break; + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + status = LTTNG_CONDITION_STATUS_INVALID; + break; + default: + status = LTTNG_CONDITION_STATUS_UNSUPPORTED; + break; + } + + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + descriptor = malloc(sizeof(*descriptor)); if (descriptor == NULL) { status = LTTNG_CONDITION_STATUS_ERROR; @@ -720,7 +828,7 @@ lttng_condition_event_rule_append_capture_descriptor( descriptor->bytecode = NULL; ret = lttng_dynamic_pointer_array_add_pointer( - &event_rule_cond->capture_descriptors, descriptor); + &on_event_cond->capture_descriptors, descriptor); if (ret) { status = LTTNG_CONDITION_STATUS_ERROR; goto end; @@ -734,13 +842,13 @@ end: } enum lttng_condition_status -lttng_condition_event_rule_get_capture_descriptor_count( +lttng_condition_on_event_get_capture_descriptor_count( const struct lttng_condition *condition, unsigned int *count) { enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - const struct lttng_condition_event_rule *event_rule_cond = + const struct lttng_condition_on_event *event_rule_cond = container_of(condition, - const struct lttng_condition_event_rule, + const struct lttng_condition_on_event, parent); if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !count) { @@ -756,13 +864,13 @@ end: } const struct lttng_event_expr * -lttng_condition_event_rule_get_capture_descriptor_at_index( +lttng_condition_on_event_get_capture_descriptor_at_index( const struct lttng_condition *condition, unsigned int index) { const struct lttng_event_expr *expr = NULL; const struct lttng_capture_descriptor *desc = NULL; - desc = lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + desc = lttng_condition_on_event_get_internal_capture_descriptor_at_index( condition, index); if (desc == NULL) { goto end; @@ -775,6 +883,7 @@ end: LTTNG_HIDDEN ssize_t lttng_evaluation_event_rule_create_from_payload( + const struct lttng_condition_on_event *condition, struct lttng_payload_view *view, struct lttng_evaluation **_evaluation) { @@ -785,6 +894,8 @@ ssize_t lttng_evaluation_event_rule_create_from_payload( const struct lttng_payload_view header_view = lttng_payload_view_from_view( view, 0, sizeof(*header)); + uint32_t capture_payload_size; + const char *capture_payload = NULL; if (!_evaluation) { ret = -1; @@ -802,7 +913,7 @@ ssize_t lttng_evaluation_event_rule_create_from_payload( /* Map the originating trigger's name. */ offset += sizeof(*header); { - struct lttng_payload_view current_view = + const struct lttng_payload_view current_view = lttng_payload_view_from_view(view, offset, header->trigger_name_length); @@ -822,13 +933,40 @@ ssize_t lttng_evaluation_event_rule_create_from_payload( } offset += header->trigger_name_length; + { + const struct lttng_payload_view current_view = + lttng_payload_view_from_view(view, offset, -1); + + if (current_view.buffer.size < sizeof(capture_payload_size)) { + ret = -1; + goto error; + } - evaluation = lttng_evaluation_event_rule_create(trigger_name); + memcpy(&capture_payload_size, current_view.buffer.data, + sizeof(capture_payload_size)); + } + offset += sizeof(capture_payload_size); + + if (capture_payload_size > 0) { + const struct lttng_payload_view current_view = + lttng_payload_view_from_view(view, offset, -1); + + if (current_view.buffer.size < capture_payload_size) { + ret = -1; + goto error; + } + + capture_payload = current_view.buffer.data; + } + + evaluation = lttng_evaluation_event_rule_create(condition, trigger_name, + capture_payload, capture_payload_size, true); if (!evaluation) { ret = -1; goto error; } + offset += capture_payload_size; *_evaluation = evaluation; evaluation = NULL; ret = offset; @@ -845,6 +983,7 @@ static int lttng_evaluation_event_rule_serialize( int ret = 0; struct lttng_evaluation_event_rule *hit; struct lttng_evaluation_event_rule_comm comm; + uint32_t capture_payload_size; hit = container_of( evaluation, struct lttng_evaluation_event_rule, parent); @@ -860,6 +999,68 @@ static int lttng_evaluation_event_rule_serialize( ret = lttng_dynamic_buffer_append( &payload->buffer, hit->name, comm.trigger_name_length); + if (ret) { + goto end; + } + + capture_payload_size = (uint32_t) hit->capture_payload.size; + ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_payload_size, + sizeof(capture_payload_size)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, hit->capture_payload.data, + hit->capture_payload.size); + if (ret) { + goto end; + } + +end: + return ret; +} + +static +bool msgpack_str_is_equal(const struct msgpack_object *obj, const char *str) +{ + bool is_equal = true; + + assert(obj->type == MSGPACK_OBJECT_STR); + + if (obj->via.str.size != strlen(str)) { + is_equal = false; + goto end; + } + + if (strncmp(obj->via.str.ptr, str, obj->via.str.size) != 0) { + is_equal = false; + goto end; + } + +end: + return is_equal; +} + +static +const msgpack_object *get_msgpack_map_obj(const struct msgpack_object *map_obj, + const char *name) +{ + const msgpack_object *ret = NULL; + size_t i; + + assert(map_obj->type == MSGPACK_OBJECT_MAP); + + for (i = 0; i < map_obj->via.map.size; i++) { + const struct msgpack_object_kv *kv = &map_obj->via.map.ptr[i]; + + assert(kv->key.type == MSGPACK_OBJECT_STR); + + if (msgpack_str_is_equal(&kv->key, name)) { + ret = &kv->val; + goto end; + } + } + end: return ret; } @@ -872,34 +1073,342 @@ static void lttng_evaluation_event_rule_destroy( hit = container_of( evaluation, struct lttng_evaluation_event_rule, parent); free(hit->name); + lttng_dynamic_buffer_reset(&hit->capture_payload); + lttng_event_field_value_destroy(hit->captured_values); free(hit); } +static +int event_field_value_from_obj(const msgpack_object *obj, + struct lttng_event_field_value **field_val) +{ + int ret = 0; + + assert(obj); + assert(field_val); + + switch (obj->type) { + case MSGPACK_OBJECT_NIL: + /* Unavailable. */ + *field_val = NULL; + goto end; + case MSGPACK_OBJECT_POSITIVE_INTEGER: + *field_val = lttng_event_field_value_uint_create( + obj->via.u64); + break; + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + *field_val = lttng_event_field_value_int_create( + obj->via.i64); + break; + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: + *field_val = lttng_event_field_value_real_create( + obj->via.f64); + break; + case MSGPACK_OBJECT_STR: + *field_val = lttng_event_field_value_string_create_with_size( + obj->via.str.ptr, obj->via.str.size); + break; + case MSGPACK_OBJECT_ARRAY: + { + size_t i; + + *field_val = lttng_event_field_value_array_create(); + if (!*field_val) { + goto error; + } + + for (i = 0; i < obj->via.array.size; i++) { + const msgpack_object *elem_obj = &obj->via.array.ptr[i]; + struct lttng_event_field_value *elem_field_val; + + ret = event_field_value_from_obj(elem_obj, + &elem_field_val); + if (ret) { + goto error; + } + + if (elem_field_val) { + ret = lttng_event_field_value_array_append( + *field_val, elem_field_val); + } else { + ret = lttng_event_field_value_array_append_unavailable( + *field_val); + } + + if (ret) { + lttng_event_field_value_destroy(elem_field_val); + goto error; + } + } + + break; + } + case MSGPACK_OBJECT_MAP: + { + /* + * As of this version, the only valid map object is + * for an enumeration value, for example: + * + * type: enum + * value: 177 + * labels: + * - Labatt 50 + * - Molson Dry + * - Carling Black Label + */ + const msgpack_object *inner_obj; + size_t label_i; + + inner_obj = get_msgpack_map_obj(obj, "type"); + if (!inner_obj) { + ERR("Missing `type` entry in map object."); + goto error; + } + + if (inner_obj->type != MSGPACK_OBJECT_STR) { + ERR("Map object's `type` entry is not a string (it's a %d).", + inner_obj->type); + goto error; + } + + if (!msgpack_str_is_equal(inner_obj, "enum")) { + ERR("Map object's `type` entry: expecting `enum`."); + goto error; + } + + inner_obj = get_msgpack_map_obj(obj, "value"); + if (!inner_obj) { + ERR("Missing `value` entry in map object."); + goto error; + } + + if (inner_obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + *field_val = lttng_event_field_value_enum_uint_create( + inner_obj->via.u64); + } else if (inner_obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) { + *field_val = lttng_event_field_value_enum_int_create( + inner_obj->via.i64); + } else { + ERR("Map object's `value` entry is not an integer (it's a %d).", + inner_obj->type); + goto error; + } + + if (!*field_val) { + goto error; + } + + inner_obj = get_msgpack_map_obj(obj, "labels"); + if (!inner_obj) { + /* No labels */ + goto end; + } + + if (inner_obj->type != MSGPACK_OBJECT_ARRAY) { + ERR("Map object's `labels` entry is not an array (it's a %d).", + inner_obj->type); + goto error; + } + + for (label_i = 0; label_i < inner_obj->via.array.size; + label_i++) { + int iret; + const msgpack_object *elem_obj = + &inner_obj->via.array.ptr[label_i]; + + if (elem_obj->type != MSGPACK_OBJECT_STR) { + ERR("Map object's `labels` entry's type is not a string (it's a %d).", + elem_obj->type); + goto error; + } + + iret = lttng_event_field_value_enum_append_label_with_size( + *field_val, elem_obj->via.str.ptr, + elem_obj->via.str.size); + if (iret) { + goto error; + } + } + + break; + } + default: + ERR("Unexpected object type %d.", obj->type); + goto error; + } + + if (!*field_val) { + goto error; + } + + goto end; + +error: + lttng_event_field_value_destroy(*field_val); + *field_val = NULL; + ret = -1; + +end: + return ret; +} + +static +struct lttng_event_field_value *event_field_value_from_capture_payload( + const struct lttng_condition_on_event *condition, + const char *capture_payload, size_t capture_payload_size) +{ + struct lttng_event_field_value *ret = NULL; + msgpack_unpacked unpacked; + msgpack_unpack_return unpack_return; + const msgpack_object *root_obj; + const msgpack_object_array *root_array_obj; + size_t i; + size_t count; + + assert(condition); + assert(capture_payload); + + /* Initialize value. */ + msgpack_unpacked_init(&unpacked); + + /* Decode. */ + unpack_return = msgpack_unpack_next(&unpacked, capture_payload, + capture_payload_size, NULL); + if (unpack_return != MSGPACK_UNPACK_SUCCESS) { + ERR("msgpack_unpack_next() failed to decode the " + "MessagePack-encoded capture payload " + "(size %zu); returned %d.", + capture_payload_size, unpack_return); + goto error; + } + + /* Get root array. */ + root_obj = &unpacked.data; + + if (root_obj->type != MSGPACK_OBJECT_ARRAY) { + ERR("Expecting an array as the root object; got type %d.", + root_obj->type); + goto error; + } + + root_array_obj = &root_obj->via.array; + + /* Create an empty root array event field value. */ + ret = lttng_event_field_value_array_create(); + if (!ret) { + goto error; + } + + /* + * For each capture descriptor in the condition object: + * + * 1. Get its corresponding captured field value MessagePack + * object. + * + * 2. Create a corresponding event field value. + * + * 3. Append it to `ret` (the root array event field value). + */ + count = lttng_dynamic_pointer_array_get_count( + &condition->capture_descriptors); + assert(count > 0); + + for (i = 0; i < count; i++) { + const struct lttng_capture_descriptor *capture_descriptor = + lttng_condition_on_event_get_internal_capture_descriptor_at_index( + &condition->parent, i); + const msgpack_object *elem_obj; + struct lttng_event_field_value *elem_field_val; + int iret; + + assert(capture_descriptor); + + elem_obj = &root_array_obj->ptr[i]; + iret = event_field_value_from_obj(elem_obj, + &elem_field_val); + if (iret) { + goto error; + } + + if (elem_field_val) { + iret = lttng_event_field_value_array_append(ret, + elem_field_val); + } else { + iret = lttng_event_field_value_array_append_unavailable( + ret); + } + + if (iret) { + lttng_event_field_value_destroy(elem_field_val); + goto error; + } + } + + goto end; + +error: + lttng_event_field_value_destroy(ret); + ret = NULL; + +end: + msgpack_unpacked_destroy(&unpacked); + return ret; +} + LTTNG_HIDDEN struct lttng_evaluation *lttng_evaluation_event_rule_create( - const char *trigger_name) + const struct lttng_condition_on_event *condition, + const char *trigger_name, + const char *capture_payload, size_t capture_payload_size, + bool decode_capture_payload) { struct lttng_evaluation_event_rule *hit; struct lttng_evaluation *evaluation = NULL; hit = zmalloc(sizeof(struct lttng_evaluation_event_rule)); if (!hit) { - goto end; + goto error; } hit->name = strdup(trigger_name); if (!hit->name) { - goto end; + goto error; } - hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; + lttng_dynamic_buffer_init(&hit->capture_payload); + + if (capture_payload) { + const int ret = lttng_dynamic_buffer_append( + &hit->capture_payload, capture_payload, + capture_payload_size); + if (ret) { + ERR("Failed to initialize capture payload of event rule evaluation"); + goto error; + } + + if (decode_capture_payload) { + hit->captured_values = + event_field_value_from_capture_payload( + condition, + capture_payload, + capture_payload_size); + if (!hit->captured_values) { + ERR("Failed to decode the capture payload: size = %zu", + capture_payload_size); + goto error; + } + } + } + + hit->parent.type = LTTNG_CONDITION_TYPE_ON_EVENT; hit->parent.serialize = lttng_evaluation_event_rule_serialize; hit->parent.destroy = lttng_evaluation_event_rule_destroy; evaluation = &hit->parent; hit = NULL; -end: +error: if (hit) { lttng_evaluation_event_rule_destroy(&hit->parent); } @@ -907,6 +1416,36 @@ end: return evaluation; } +enum lttng_evaluation_status lttng_evaluation_get_captured_values( + const struct lttng_evaluation *evaluation, + const struct lttng_event_field_value **field_val) +{ + struct lttng_evaluation_event_rule *hit; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + /* + * Event rule is currently the only type that can provide captured + * values. + */ + if (!evaluation || !is_event_rule_evaluation(evaluation) || + !field_val) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + hit = container_of(evaluation, struct lttng_evaluation_event_rule, + parent); + if (!hit->captured_values) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + *field_val = hit->captured_values; + +end: + return status; +} + enum lttng_evaluation_status lttng_evaluation_event_rule_get_trigger_name( const struct lttng_evaluation *evaluation, const char **name) { @@ -927,7 +1466,7 @@ end: LTTNG_HIDDEN enum lttng_error_code -lttng_condition_event_rule_generate_capture_descriptor_bytecode( +lttng_condition_on_event_generate_capture_descriptor_bytecode( struct lttng_condition *condition) { enum lttng_error_code ret; @@ -939,7 +1478,7 @@ lttng_condition_event_rule_generate_capture_descriptor_bytecode( goto end; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition, &capture_count); if (status != LTTNG_CONDITION_STATUS_OK) { ret = LTTNG_ERR_FATAL; @@ -948,7 +1487,7 @@ lttng_condition_event_rule_generate_capture_descriptor_bytecode( for (i = 0; i < capture_count; i++) { struct lttng_capture_descriptor *local_capture_desc = - lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + lttng_condition_on_event_get_internal_capture_descriptor_at_index( condition, i); if (local_capture_desc == NULL) { @@ -975,12 +1514,12 @@ end: LTTNG_HIDDEN const struct lttng_bytecode * -lttng_condition_event_rule_get_capture_bytecode_at_index( +lttng_condition_on_event_get_capture_bytecode_at_index( const struct lttng_condition *condition, unsigned int index) { - const struct lttng_condition_event_rule *event_rule_cond = + const struct lttng_condition_on_event *on_event_cond = container_of(condition, - const struct lttng_condition_event_rule, + const struct lttng_condition_on_event, parent); struct lttng_capture_descriptor *desc = NULL; struct lttng_bytecode *bytecode = NULL; @@ -991,7 +1530,7 @@ lttng_condition_event_rule_get_capture_bytecode_at_index( goto end; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition, &count); if (status != LTTNG_CONDITION_STATUS_OK) { goto end; @@ -1002,7 +1541,7 @@ lttng_condition_event_rule_get_capture_bytecode_at_index( } desc = lttng_dynamic_pointer_array_get_pointer( - &event_rule_cond->capture_descriptors, index); + &on_event_cond->capture_descriptors, index); if (desc == NULL) { goto end; } diff --git a/src/common/config/config-session-abi.h b/src/common/config/config-session-abi.h index 75ff303d4..85ff4ed14 100644 --- a/src/common/config/config-session-abi.h +++ b/src/common/config/config-session-abi.h @@ -11,6 +11,8 @@ extern const char * const config_element_all; extern const char * const config_element_channel; extern const char * const config_element_channels; +extern const char * const config_element_map; +extern const char * const config_element_maps; extern const char * const config_element_domain; extern const char * const config_element_domains; extern const char * const config_element_event; @@ -99,6 +101,15 @@ extern const char * const config_element_rotation_timer_interval; extern const char * const config_element_rotation_size; extern const char * const config_element_rotation_schedule; +extern const char * const config_element_bitness; +extern const char * const config_element_boundary_policy; +extern const char * const config_element_coalesce_hits; +extern const char * const config_element_dimensions; +extern const char * const config_element_dimension; +extern const char * const config_element_dimension_size; + +extern const char * const config_boundary_policy_overflow; + extern const char * const config_domain_type_kernel; extern const char * const config_domain_type_ust; extern const char * const config_domain_type_jul; diff --git a/src/common/config/session-config.c b/src/common/config/session-config.c index 49c06a353..70668470c 100644 --- a/src/common/config/session-config.c +++ b/src/common/config/session-config.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,8 @@ const char * const config_xml_false = "false"; const char * const config_element_channel = "channel"; const char * const config_element_channels = "channels"; +const char * const config_element_map = "map"; +const char * const config_element_maps = "maps"; const char * const config_element_domain = "domain"; const char * const config_element_domains = "domains"; const char * const config_element_event = "event"; @@ -183,6 +186,15 @@ const char * const config_overwrite_mode_overwrite = "OVERWRITE"; const char * const config_output_type_splice = "SPLICE"; const char * const config_output_type_mmap = "MMAP"; +LTTNG_HIDDEN const char * const config_element_bitness = "bitness"; +LTTNG_HIDDEN const char * const config_element_boundary_policy = "boundary_policy"; +LTTNG_HIDDEN const char * const config_element_coalesce_hits = "coalesce_hits"; +LTTNG_HIDDEN const char * const config_element_dimensions = "dimensions"; +LTTNG_HIDDEN const char * const config_element_dimension = "dimension"; +LTTNG_HIDDEN const char * const config_element_dimension_size = "size"; + +LTTNG_HIDDEN const char * const config_boundary_policy_overflow = "OVERFLOW"; + const char * const config_loglevel_type_all = "ALL"; const char * const config_loglevel_type_range = "RANGE"; const char * const config_loglevel_type_single = "SINGLE"; @@ -3054,14 +3066,299 @@ end: return ret; } +static +int process_channel_node(xmlNodePtr channel_node, struct lttng_handle *handle, + struct lttng_domain *domain, + enum lttng_domain_type original_domain) +{ + int ret; + struct lttng_channel *channel = NULL; + xmlNodePtr contexts_node = NULL; + xmlNodePtr events_node = NULL; + xmlNodePtr channel_attr_node; + + /* + * Channels of the "agent" types cannot be created directly. + * They are meant to be created implicitly through the + * activation of events in their domain. However, a user + * can override the default channel configuration attributes + * by creating the underlying UST channel _before_ enabling + * an agent domain event. + * + * Hence, the channel's type is substituted before the creation + * and restored by the time the events are created. + */ + switch (domain->type) { + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + domain->type = LTTNG_DOMAIN_UST; + default: + break; + } + + channel = lttng_channel_create(domain); + if (!channel) { + ret = -1; + goto end; + } + + for (channel_attr_node = xmlFirstElementChild(channel_node); + channel_attr_node; channel_attr_node = + xmlNextElementSibling(channel_attr_node)) { + ret = process_channel_attr_node(channel_attr_node, + channel, &contexts_node, &events_node); + if (ret) { + goto end; + } + } + + ret = lttng_enable_channel(handle, channel); + if (ret < 0) { + goto end; + } + + /* Restore the original channel domain-> */ + domain->type = original_domain; + + ret = process_events_node(events_node, handle, channel->name); + if (ret) { + goto end; + } + + ret = process_contexts_node(contexts_node, handle, + channel->name); + if (ret) { + goto end; + } + +end: + lttng_channel_destroy(channel); + return ret; +} + +static int process_dimension_node(xmlNodePtr dimension_node, + struct lttng_dynamic_array *dimension_sizes) +{ + int ret; + xmlNodePtr node; + xmlChar *size_str = NULL; + uint64_t size; + + assert(strcmp((const char *) dimension_node->name, + config_element_dimension) == 0); + assert(dimension_sizes->element_size == sizeof(uint64_t)); + + for (node = xmlFirstElementChild(dimension_node); node; + node = xmlNextElementSibling(node)) { + if (strcmp((const char *) node->name, + config_element_dimension_size) == 0) { + assert(!size_str); + size_str = xmlNodeGetContent(node); + if (!size_str) { + ERR("Failed to get dimension size node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(size_str, &size); + assert(!ret); + + ret = lttng_dynamic_array_add_element(dimension_sizes, &size); + if (ret) { + goto end; + } + } else { + assert(false); + } + } + + ret = 0; + +end: + xmlFree(size_str); + return ret; +} + +/* `dimensions_sizes` must be initialized to hold uint64_t elements. */ + +static int process_dimensions_node(xmlNodePtr dimensions_node, + struct lttng_dynamic_array *dimension_sizes) +{ + xmlNodePtr dimension_node; + int ret = 0; + + assert(strcmp((const char *) dimensions_node->name, + config_element_dimensions) == 0); + assert(dimension_sizes->element_size == sizeof(uint64_t)); + assert(lttng_dynamic_array_get_count(dimension_sizes) == 0); + + for (dimension_node = xmlFirstElementChild(dimensions_node); + dimension_node; dimension_node = xmlNextElementSibling( + dimension_node)) { + ret = process_dimension_node(dimension_node, dimension_sizes); + if (ret) { + goto end; + } + } + + assert(lttng_dynamic_array_get_count(dimension_sizes) > 0); + +end: + return ret; +} + +static int process_map_node(xmlNodePtr map_node, struct lttng_handle *handle) +{ + int ret; + xmlNodePtr node; + xmlChar *name = NULL; + xmlChar *enabled_str = NULL; + int enabled; + xmlChar *bitness_str = NULL; + enum lttng_map_bitness bitness = LTTNG_MAP_BITNESS_32BITS; + xmlChar *boundary_policy_str = NULL; + enum lttng_map_boundary_policy boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + xmlChar *coalesce_hits_str = NULL; + int coalesce_hits; + enum lttng_map_status map_status; + struct lttng_map *map = NULL; + struct lttng_dynamic_array dimension_sizes; + enum lttng_error_code error_code; + + assert(strcmp((const char *) map_node->name, config_element_map) == 0); + + lttng_dynamic_array_init(&dimension_sizes, sizeof(uint64_t), NULL); + + for (node = xmlFirstElementChild(map_node); node; + node = xmlNextElementSibling(node)) { + if (strcmp((const char *) node->name, config_element_name) == + 0) { + assert(!name); + name = xmlNodeGetContent(node); + if (!name) { + ERR("Failed to get map name node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + } else if (strcmp((const char *) node->name, + config_element_enabled) == 0) { + assert(!enabled_str); + enabled_str = xmlNodeGetContent(node); + if (!enabled_str) { + ERR("Failed to get map enabled node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_bool(enabled_str, &enabled); + assert(!ret); + } else if (strcmp((const char *) node->name, + config_element_bitness) == 0) { + assert(!bitness_str); + bitness_str = xmlNodeGetContent(node); + if (!bitness_str) { + ERR("Failed to get map bitness node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + if (strcmp((const char *) bitness_str, "32") == 0) { + bitness = LTTNG_MAP_BITNESS_32BITS; + } else { + assert(strcmp((const char *) bitness_str, + "64") == 0); + bitness = LTTNG_MAP_BITNESS_64BITS; + } + } else if (strcmp((const char *) node->name, + config_element_boundary_policy) == + 0) { + assert(!boundary_policy_str); + boundary_policy_str = xmlNodeGetContent(node); + if (!boundary_policy_str) { + ERR("Failed to get map boundary policy node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + assert(strcmp((const char *) boundary_policy_str, + config_boundary_policy_overflow) == + 0); + boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + } else if (strcmp((const char *) node->name, + config_element_coalesce_hits) == 0) { + assert(!coalesce_hits_str); + coalesce_hits_str = xmlNodeGetContent(node); + if (!coalesce_hits_str) { + ERR("Failed to get map coalesce hits node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_bool(coalesce_hits_str, &coalesce_hits); + assert(!ret); + } else if (strcmp((const char *) node->name, + config_element_dimensions) == 0) { + ret = process_dimensions_node(node, &dimension_sizes); + if (ret) { + goto end; + } + } else { + assert(false); + } + } + + assert(name); + map_status = lttng_map_create((const char *) name, + lttng_dynamic_array_get_count(&dimension_sizes), + (uint64_t *) dimension_sizes.buffer.data, + handle->domain.type, handle->domain.buf_type, bitness, + boundary_policy, coalesce_hits, &map); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to create map."); + ret = -LTTNG_ERR_UNK; + goto end; + } + + error_code = lttng_add_map(handle, map); + if (error_code != LTTNG_OK) { + ERR("Adding map \"%s\": %s", (const char *) name, + lttng_strerror(error_code)); + ret = error_code; + goto end; + } + + // FIXME: disabling the map after creating leaves a window of time + // where it is enabled, does it matter? + if (!enabled) { + ret = lttng_disable_map(handle, (const char *) name); + if (ret) { + goto end; + } + } + + ret = 0; + +end: + xmlFree(name); + xmlFree(enabled_str); + xmlFree(bitness_str); + xmlFree(boundary_policy_str); + xmlFree(coalesce_hits_str); + lttng_dynamic_array_reset(&dimension_sizes); + lttng_map_destroy(map); + + return ret; +} + static int process_domain_node(xmlNodePtr domain_node, const char *session_name) { int ret; struct lttng_domain domain = { 0 }; struct lttng_handle *handle = NULL; - struct lttng_channel *channel = NULL; xmlNodePtr channels_node = NULL; + xmlNodePtr maps_node = NULL; xmlNodePtr trackers_node = NULL; xmlNodePtr pid_tracker_node = NULL; xmlNodePtr vpid_tracker_node = NULL; @@ -3094,76 +3391,39 @@ int process_domain_node(xmlNodePtr domain_node, const char *session_name) } } - if (!channels_node) { - goto end; - } - - /* create all channels */ - for (node = xmlFirstElementChild(channels_node); node; - node = xmlNextElementSibling(node)) { - const enum lttng_domain_type original_domain = domain.type; - xmlNodePtr contexts_node = NULL; - xmlNodePtr events_node = NULL; - xmlNodePtr channel_attr_node; - - /* - * Channels of the "agent" types cannot be created directly. - * They are meant to be created implicitly through the - * activation of events in their domain. However, a user - * can override the default channel configuration attributes - * by creating the underlying UST channel _before_ enabling - * an agent domain event. - * - * Hence, the channel's type is substituted before the creation - * and restored by the time the events are created. - */ - switch (domain.type) { - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_PYTHON: - domain.type = LTTNG_DOMAIN_UST; - default: - break; - } - - channel = lttng_channel_create(&domain); - if (!channel) { - ret = -1; - goto end; - } + if (channels_node) { + /* create all channels */ + for (node = xmlFirstElementChild(channels_node); node; + node = xmlNextElementSibling(node)) { + const enum lttng_domain_type original_domain = domain.type; - for (channel_attr_node = xmlFirstElementChild(node); - channel_attr_node; channel_attr_node = - xmlNextElementSibling(channel_attr_node)) { - ret = process_channel_attr_node(channel_attr_node, - channel, &contexts_node, &events_node); + ret = process_channel_node(node, handle, &domain, + original_domain); if (ret) { goto end; } } + } - ret = lttng_enable_channel(handle, channel); - if (ret < 0) { - goto end; - } - - /* Restore the original channel domain. */ - domain.type = original_domain; - - ret = process_events_node(events_node, handle, channel->name); - if (ret) { - goto end; + /* get the maps node */ + for (node = xmlFirstElementChild(domain_node); node; + node = xmlNextElementSibling(node)) { + if (!strcmp((const char *) node->name, config_element_maps)) { + maps_node = node; + break; } + } - ret = process_contexts_node(contexts_node, handle, - channel->name); - if (ret) { - goto end; + if (maps_node) { + /* create all maps */ + for (node = xmlFirstElementChild(maps_node); node; + node = xmlNextElementSibling(node)) { + ret = process_map_node(node, handle); + if (ret) { + goto end; + } } - - lttng_channel_destroy(channel); } - channel = NULL; /* get the trackers node */ for (node = xmlFirstElementChild(domain_node); node; @@ -3254,7 +3514,6 @@ int process_domain_node(xmlNodePtr domain_node, const char *session_name) } end: - lttng_channel_destroy(channel); lttng_destroy_handle(handle); return ret; } diff --git a/src/common/config/session.xsd b/src/common/config/session.xsd index 986fb2dda..c8f8ea0fb 100644 --- a/src/common/config/session.xsd +++ b/src/common/config/session.xsd @@ -254,6 +254,48 @@ by its signed 32-bit representation when converted to msec. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -429,6 +471,7 @@ by its signed 32-bit representation when converted to msec. + diff --git a/src/common/error.c b/src/common/error.c index e03bee350..66859670a 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -241,6 +241,20 @@ static const char *error_string_array[] = { [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) ] = "Operation does not apply to the process attribute tracker's tracking policy", [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD) ] = "Failed to create an event notifier group notification file descriptor", [ ERROR_INDEX(LTTNG_ERR_INVALID_CAPTURE_EXPRESSION) ] = "Invalid capture expression", + [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION) ] = "Failed to create event notifier", + [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING) ] = "Failed to initialize event notifier error accounting", + [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL) ] = "No index available in event notifier error accounting", + [ ERROR_INDEX(LTTNG_ERR_INVALID_MAP) ] = "Invalid map", + [ ERROR_INDEX(LTTNG_ERR_MAP_NOT_FOUND) ] = "Map name not found", + [ ERROR_INDEX(LTTNG_ERR_UST_MAP_ENABLE_FAIL) ] = "Enable UST map failed", + [ ERROR_INDEX(LTTNG_ERR_UST_MAP_DISABLE_FAIL) ] = "Disable UST map failed", + [ ERROR_INDEX(LTTNG_ERR_UST_MAP_NOT_FOUND) ] = "UST map not found", + [ ERROR_INDEX(LTTNG_ERR_UST_MAP_EXIST) ] = "UST map already exists", + [ ERROR_INDEX(LTTNG_ERR_KERNEL_MAP_ENABLE_FAIL) ] = "Enable Kernel map failed", + [ ERROR_INDEX(LTTNG_ERR_KERNEL_MAP_DISABLE_FAIL) ] = "Disable Kernel map failed", + [ ERROR_INDEX(LTTNG_ERR_KERNEL_MAP_NOT_FOUND) ] = "Kernel map not found", + [ ERROR_INDEX(LTTNG_ERR_KERNEL_MAP_EXIST) ] = "Kernel map already exists", + [ ERROR_INDEX(LTTNG_ERR_MAP_VALUES_LIST_FAIL) ] = "Listing map values failed", /* Last element */ [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code" diff --git a/src/common/evaluation.c b/src/common/evaluation.c index d8a68a784..c51ec2bd9 100644 --- a/src/common/evaluation.c +++ b/src/common/evaluation.c @@ -5,11 +5,12 @@ * */ +#include #include #include #include #include -#include +#include #include #include #include @@ -49,6 +50,7 @@ end: LTTNG_HIDDEN ssize_t lttng_evaluation_create_from_payload( + const struct lttng_condition *condition, struct lttng_payload_view *src_view, struct lttng_evaluation **evaluation) { @@ -115,8 +117,14 @@ ssize_t lttng_evaluation_create_from_payload( } evaluation_size += ret; break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: - ret = lttng_evaluation_event_rule_create_from_payload(&evaluation_view, evaluation); + case LTTNG_CONDITION_TYPE_ON_EVENT: + assert(condition); + assert(condition->type == LTTNG_CONDITION_TYPE_ON_EVENT); + ret = lttng_evaluation_event_rule_create_from_payload( + container_of(condition, + const struct lttng_condition_on_event, + parent), + &evaluation_view, evaluation); if (ret < 0) { goto end; } diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c index 3a5be731b..6071bb8dc 100644 --- a/src/common/event-rule/event-rule.c +++ b/src/common/event-rule/event-rule.c @@ -14,10 +14,11 @@ #include #include #include -#include +#include +#include #include #include -#include +#include #include enum lttng_event_rule_type lttng_event_rule_get_type( @@ -41,9 +42,9 @@ enum lttng_domain_type lttng_event_rule_get_domain_type( break; } case LTTNG_EVENT_RULE_TYPE_SYSCALL: - case LTTNG_EVENT_RULE_TYPE_KPROBE: - case LTTNG_EVENT_RULE_TYPE_KRETPROBE: - case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: domain_type = LTTNG_DOMAIN_KERNEL; break; case LTTNG_EVENT_RULE_TYPE_UNKNOWN: @@ -172,14 +173,14 @@ ssize_t lttng_event_rule_create_from_payload( create_from_payload = lttng_event_rule_tracepoint_create_from_payload; break; - case LTTNG_EVENT_RULE_TYPE_KPROBE: - create_from_payload = lttng_event_rule_kprobe_create_from_payload; + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: + create_from_payload = lttng_event_rule_kernel_probe_create_from_payload; break; - case LTTNG_EVENT_RULE_TYPE_KRETPROBE: - /* TODO */ + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + create_from_payload = lttng_event_rule_kernel_function_create_from_payload; break; - case LTTNG_EVENT_RULE_TYPE_UPROBE: - create_from_payload = lttng_event_rule_uprobe_create_from_payload; + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: + create_from_payload = lttng_event_rule_userspace_probe_create_from_payload; break; case LTTNG_EVENT_RULE_TYPE_SYSCALL: create_from_payload = @@ -315,11 +316,11 @@ const char *lttng_event_rule_type_str(enum lttng_event_rule_type type) return "tracepoint"; case LTTNG_EVENT_RULE_TYPE_SYSCALL: return "syscall"; - case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: return "probe"; - case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: return "function"; - case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: return "userspace-probe"; default: abort(); diff --git a/src/common/event-rule/kernel-function.c b/src/common/event-rule/kernel-function.c new file mode 100644 index 000000000..c5dd61938 --- /dev/null +++ b/src/common/event-rule/kernel-function.c @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_KERNEL_FUNCTION_EVENT_RULE(rule) \ + (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION) + +#if (LTTNG_SYMBOL_NAME_LEN == 256) +#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" +#endif + +static void lttng_event_rule_kernel_function_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_kernel_function *kfunction; + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + + lttng_kernel_function_location_destroy(kfunction->location); + free(kfunction->name); + free(kfunction); +} + +static bool lttng_event_rule_kernel_function_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_kernel_function *kfunction; + + if (!rule) { + goto end; + } + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + + /* Required field. */ + if (!kfunction->name) { + ERR("Invalid name event rule: a name must be set."); + goto end; + } + + /* Required field. */ + if(!kfunction->location) { + ERR("Invalid name event rule: a location must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static int lttng_event_rule_kernel_function_serialize( + const struct lttng_event_rule *rule, + struct lttng_payload *payload) +{ + int ret; + size_t name_len, header_offset, size_before_location; + struct lttng_event_rule_kernel_function *kfunction; + struct lttng_event_rule_kernel_function_comm kfunction_comm; + struct lttng_event_rule_kernel_function_comm *header; + + if (!rule || !IS_KERNEL_FUNCTION_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + header_offset = payload->buffer.size; + + DBG("Serializing kfunction event rule."); + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + + name_len = strlen(kfunction->name) + 1; + kfunction_comm.name_len = name_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &kfunction_comm, sizeof(kfunction_comm)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, kfunction->name, name_len); + if (ret) { + goto end; + } + + size_before_location = payload->buffer.size; + + ret = lttng_kernel_function_location_serialize(kfunction->location, payload); + if (ret < 0) { + goto end; + } + + /* Update the header regarding the function size. */ + header = (struct lttng_event_rule_kernel_function_comm*) ( + (char *) payload->buffer.data + header_offset); + header->location_len = payload->buffer.size - size_before_location; + + ret = 0; + +end: + return ret; +} + +static bool lttng_event_rule_kernel_function_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + bool is_equal = false; + struct lttng_event_rule_kernel_function *a, *b; + + a = container_of(_a, struct lttng_event_rule_kernel_function, parent); + b = container_of(_b, struct lttng_event_rule_kernel_function, parent); + + /* Quick checks */ + if (!!a->name != !!b->name) { + goto end; + } + + /* Long check */ + assert(a->name); + assert(b->name); + if (strcmp(a->name, b->name)) { + goto end; + } + + is_equal = lttng_kernel_function_location_is_equal( + a->location, b->location); +end: + return is_equal; +} + +static enum lttng_error_code lttng_event_rule_kernel_function_generate_filter_bytecode( + struct lttng_event_rule *rule, + const struct lttng_credentials *creds) +{ + /* Nothing to do. */ + return LTTNG_OK; +} + +static const char *lttng_event_rule_kernel_function_get_filter( + const struct lttng_event_rule *rule) +{ + /* Not supported. */ + return NULL; +} + +static const struct lttng_bytecode * +lttng_event_rule_kernel_function_get_filter_bytecode(const struct lttng_event_rule *rule) +{ + /* Not supported. */ + return NULL; +} + +static enum lttng_event_rule_generate_exclusions_status +lttng_event_rule_kernel_function_generate_exclusions(const struct lttng_event_rule *rule, + struct lttng_event_exclusion **exclusions) +{ + /* Not supported. */ + *exclusions = NULL; + return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE; +} + +static unsigned long +lttng_event_rule_kernel_function_hash( + const struct lttng_event_rule *rule) +{ + unsigned long hash; + struct lttng_event_rule_kernel_function *krule = + container_of(rule, typeof(*krule), parent); + + hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION, + lttng_ht_seed); + hash ^= hash_key_str(krule->name, lttng_ht_seed); + hash ^= lttng_kernel_function_location_hash(krule->location); + + return hash; +} + +static +int kernel_function_set_location( + struct lttng_event_rule_kernel_function *kfunction, + const struct lttng_kernel_function_location *location) +{ + int ret; + struct lttng_kernel_function_location *location_copy = NULL; + + if (!kfunction || !location || kfunction->location) { + ret = -1; + goto end; + } + + location_copy = lttng_kernel_function_location_copy(location); + if (!location_copy) { + ret = -1; + goto end; + } + + kfunction->location = location_copy; + location_copy = NULL; + ret = 0; +end: + lttng_kernel_function_location_destroy(location_copy); + return ret; +} + +struct lttng_event_rule *lttng_event_rule_kernel_function_create( + const struct lttng_kernel_function_location *location) +{ + struct lttng_event_rule *rule = NULL; + struct lttng_event_rule_kernel_function *krule; + + krule = zmalloc(sizeof(struct lttng_event_rule_kernel_function)); + if (!krule) { + goto end; + } + + rule = &krule->parent; + lttng_event_rule_init(&krule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION); + krule->parent.validate = lttng_event_rule_kernel_function_validate; + krule->parent.serialize = lttng_event_rule_kernel_function_serialize; + krule->parent.equal = lttng_event_rule_kernel_function_is_equal; + krule->parent.destroy = lttng_event_rule_kernel_function_destroy; + krule->parent.generate_filter_bytecode = + lttng_event_rule_kernel_function_generate_filter_bytecode; + krule->parent.get_filter = lttng_event_rule_kernel_function_get_filter; + krule->parent.get_filter_bytecode = + lttng_event_rule_kernel_function_get_filter_bytecode; + krule->parent.generate_exclusions = + lttng_event_rule_kernel_function_generate_exclusions; + krule->parent.hash = lttng_event_rule_kernel_function_hash; + + if (kernel_function_set_location(krule, location)) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + +end: + return rule; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kernel_function_create_from_payload( + struct lttng_payload_view *view, + struct lttng_event_rule **_event_rule) +{ + ssize_t ret, offset = 0; + enum lttng_event_rule_status status; + const struct lttng_event_rule_kernel_function_comm *kfunction_comm; + const char *name; + struct lttng_buffer_view current_buffer_view; + struct lttng_event_rule *rule = NULL; + struct lttng_kernel_function_location *location = NULL; + + if (!_event_rule) { + ret = -1; + goto end; + } + + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, sizeof(*kfunction_comm)); + if (!lttng_buffer_view_is_valid(¤t_buffer_view)) { + ERR("Failed to initialize from malformed event rule kfunction: buffer too short to contain header."); + ret = -1; + goto end; + } + + kfunction_comm = (typeof(kfunction_comm)) current_buffer_view.data; + + /* Skip to payload */ + offset += current_buffer_view.size; + + { + /* Map the name. */ + struct lttng_payload_view current_payload_view = + lttng_payload_view_from_view(view, offset, + kfunction_comm->name_len); + + if (!lttng_payload_view_is_valid(¤t_payload_view)) { + ret = -1; + goto end; + } + + name = current_payload_view.buffer.data; + if (!lttng_buffer_view_contains_string( + ¤t_payload_view.buffer, name, + kfunction_comm->name_len)) { + ret = -1; + goto end; + } + } + + /* Skip after the name. */ + offset += kfunction_comm->name_len; + + /* Map the kernel function location. */ + { + struct lttng_payload_view current_payload_view = + lttng_payload_view_from_view(view, offset, + kfunction_comm->location_len); + + if (!lttng_payload_view_is_valid(¤t_payload_view)) { + ret = -1; + goto end; + } + + ret = lttng_kernel_function_location_create_from_payload( + ¤t_payload_view, &location); + if (ret < 0) { + ret = -1; + goto end; + } + } + + if (ret != kfunction_comm->location_len) { + ret = -1; + goto end; + } + + /* Skip after the location */ + offset += kfunction_comm->location_len; + + rule = lttng_event_rule_kernel_function_create(location); + if (!rule) { + ERR("Failed to create event rule kfunction."); + ret = -1; + goto end; + } + + status = lttng_event_rule_kernel_function_set_event_name(rule, name); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule kfunction name."); + ret = -1; + goto end; + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + lttng_kernel_function_location_destroy(location); + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_kernel_function_get_location( + const struct lttng_event_rule *rule, + const struct lttng_kernel_function_location **location) +{ + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + struct lttng_event_rule_kernel_function *kfunction; + + if (!rule || !IS_KERNEL_FUNCTION_EVENT_RULE(rule) || !location) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + *location = kfunction->location; + + if (!*location) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_kernel_function_set_event_name( + struct lttng_event_rule *rule, const char *name) +{ + char *name_copy = NULL; + struct lttng_event_rule_kernel_function *kfunction; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_KERNEL_FUNCTION_EVENT_RULE(rule) || !name || + strlen(name) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + free(kfunction->name); + + kfunction->name = name_copy; + name_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_kernel_function_get_event_name( + const struct lttng_event_rule *rule, const char **name) +{ + struct lttng_event_rule_kernel_function *kfunction; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_KERNEL_FUNCTION_EVENT_RULE(rule) || !name) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + if (!kfunction->name) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *name = kfunction->name; +end: + return status; +} diff --git a/src/common/event-rule/kprobe.c b/src/common/event-rule/kernel-probe.c similarity index 66% rename from src/common/event-rule/kprobe.c rename to src/common/event-rule/kernel-probe.c index 12e6010ac..f53e9245b 100644 --- a/src/common/event-rule/kprobe.c +++ b/src/common/event-rule/kernel-probe.c @@ -16,41 +16,42 @@ #include #include #include +#include #include -#include +#include #include #include #include #define IS_KPROBE_EVENT_RULE(rule) \ - (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KPROBE) + (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE) #if (LTTNG_SYMBOL_NAME_LEN == 256) #define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" #endif -static void lttng_event_rule_kprobe_destroy(struct lttng_event_rule *rule) +static void lttng_event_rule_kernel_probe_destroy(struct lttng_event_rule *rule) { - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); lttng_kernel_probe_location_destroy(kprobe->location); free(kprobe->name); free(kprobe); } -static bool lttng_event_rule_kprobe_validate( +static bool lttng_event_rule_kernel_probe_validate( const struct lttng_event_rule *rule) { bool valid = false; - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; if (!rule) { goto end; } - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); /* Required field. */ if (!kprobe->name) { @@ -69,15 +70,15 @@ end: return valid; } -static int lttng_event_rule_kprobe_serialize( +static int lttng_event_rule_kernel_probe_serialize( const struct lttng_event_rule *rule, struct lttng_payload *payload) { int ret; size_t name_len, header_offset, size_before_location; - struct lttng_event_rule_kprobe *kprobe; - struct lttng_event_rule_kprobe_comm kprobe_comm; - struct lttng_event_rule_kprobe_comm *header; + struct lttng_event_rule_kernel_probe *kprobe; + struct lttng_event_rule_kernel_probe_comm kprobe_comm; + struct lttng_event_rule_kernel_probe_comm *header; if (!rule || !IS_KPROBE_EVENT_RULE(rule)) { ret = -1; @@ -87,7 +88,7 @@ static int lttng_event_rule_kprobe_serialize( header_offset = payload->buffer.size; DBG("Serializing kprobe event rule."); - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); name_len = strlen(kprobe->name) + 1; kprobe_comm.name_len = name_len; @@ -111,7 +112,7 @@ static int lttng_event_rule_kprobe_serialize( } /* Update the header regarding the probe size. */ - header = (struct lttng_event_rule_kprobe_comm*) ( + header = (struct lttng_event_rule_kernel_probe_comm*) ( (char *) payload->buffer.data + header_offset); header->location_len = payload->buffer.size - size_before_location; @@ -121,14 +122,14 @@ end: return ret; } -static bool lttng_event_rule_kprobe_is_equal(const struct lttng_event_rule *_a, +static bool lttng_event_rule_kernel_probe_is_equal(const struct lttng_event_rule *_a, const struct lttng_event_rule *_b) { bool is_equal = false; - struct lttng_event_rule_kprobe *a, *b; + struct lttng_event_rule_kernel_probe *a, *b; - a = container_of(_a, struct lttng_event_rule_kprobe, parent); - b = container_of(_b, struct lttng_event_rule_kprobe, parent); + a = container_of(_a, struct lttng_event_rule_kernel_probe, parent); + b = container_of(_b, struct lttng_event_rule_kernel_probe, parent); /* Quick checks */ if (!!a->name != !!b->name) { @@ -148,7 +149,7 @@ end: return is_equal; } -static enum lttng_error_code lttng_event_rule_kprobe_generate_filter_bytecode( +static enum lttng_error_code lttng_event_rule_kernel_probe_generate_filter_bytecode( struct lttng_event_rule *rule, const struct lttng_credentials *creds) { @@ -156,7 +157,7 @@ static enum lttng_error_code lttng_event_rule_kprobe_generate_filter_bytecode( return LTTNG_OK; } -static const char *lttng_event_rule_kprobe_get_filter( +static const char *lttng_event_rule_kernel_probe_get_filter( const struct lttng_event_rule *rule) { /* Not supported. */ @@ -164,14 +165,14 @@ static const char *lttng_event_rule_kprobe_get_filter( } static const struct lttng_bytecode * -lttng_event_rule_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule) +lttng_event_rule_kernel_probe_get_filter_bytecode(const struct lttng_event_rule *rule) { /* Not supported. */ return NULL; } static enum lttng_event_rule_generate_exclusions_status -lttng_event_rule_kprobe_generate_exclusions(const struct lttng_event_rule *rule, +lttng_event_rule_kernel_probe_generate_exclusions(const struct lttng_event_rule *rule, struct lttng_event_exclusion **exclusions) { /* Not supported. */ @@ -180,14 +181,14 @@ lttng_event_rule_kprobe_generate_exclusions(const struct lttng_event_rule *rule, } static unsigned long -lttng_event_rule_kprobe_hash( +lttng_event_rule_kernel_probe_hash( const struct lttng_event_rule *rule) { unsigned long hash; - struct lttng_event_rule_kprobe *krule = + struct lttng_event_rule_kernel_probe *krule = container_of(rule, typeof(*krule), parent); - hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KPROBE, + hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE, lttng_ht_seed); hash ^= hash_key_str(krule->name, lttng_ht_seed); hash ^= lttng_kernel_probe_location_hash(krule->location); @@ -195,47 +196,80 @@ lttng_event_rule_kprobe_hash( return hash; } -struct lttng_event_rule *lttng_event_rule_kprobe_create(void) +static +int kernel_probe_set_location( + struct lttng_event_rule_kernel_probe *kprobe, + const struct lttng_kernel_probe_location *location) +{ + int ret; + struct lttng_kernel_probe_location *location_copy = NULL; + + if (!kprobe || !location || kprobe->location) { + ret = -1; + goto end; + } + + location_copy = lttng_kernel_probe_location_copy(location); + if (!location_copy) { + ret = -1; + goto end; + } + + kprobe->location = location_copy; + location_copy = NULL; + ret = 0; +end: + lttng_kernel_probe_location_destroy(location_copy); + return ret; +} + +struct lttng_event_rule *lttng_event_rule_kernel_probe_create( + const struct lttng_kernel_probe_location *location) { struct lttng_event_rule *rule = NULL; - struct lttng_event_rule_kprobe *krule; + struct lttng_event_rule_kernel_probe *krule; - krule = zmalloc(sizeof(struct lttng_event_rule_kprobe)); + krule = zmalloc(sizeof(struct lttng_event_rule_kernel_probe)); if (!krule) { goto end; } rule = &krule->parent; - lttng_event_rule_init(&krule->parent, LTTNG_EVENT_RULE_TYPE_KPROBE); - krule->parent.validate = lttng_event_rule_kprobe_validate; - krule->parent.serialize = lttng_event_rule_kprobe_serialize; - krule->parent.equal = lttng_event_rule_kprobe_is_equal; - krule->parent.destroy = lttng_event_rule_kprobe_destroy; + lttng_event_rule_init(&krule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE); + krule->parent.validate = lttng_event_rule_kernel_probe_validate; + krule->parent.serialize = lttng_event_rule_kernel_probe_serialize; + krule->parent.equal = lttng_event_rule_kernel_probe_is_equal; + krule->parent.destroy = lttng_event_rule_kernel_probe_destroy; krule->parent.generate_filter_bytecode = - lttng_event_rule_kprobe_generate_filter_bytecode; - krule->parent.get_filter = lttng_event_rule_kprobe_get_filter; + lttng_event_rule_kernel_probe_generate_filter_bytecode; + krule->parent.get_filter = lttng_event_rule_kernel_probe_get_filter; krule->parent.get_filter_bytecode = - lttng_event_rule_kprobe_get_filter_bytecode; + lttng_event_rule_kernel_probe_get_filter_bytecode; krule->parent.generate_exclusions = - lttng_event_rule_kprobe_generate_exclusions; - krule->parent.hash = lttng_event_rule_kprobe_hash; + lttng_event_rule_kernel_probe_generate_exclusions; + krule->parent.hash = lttng_event_rule_kernel_probe_hash; + + if (kernel_probe_set_location(krule, location)) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + end: return rule; } LTTNG_HIDDEN -ssize_t lttng_event_rule_kprobe_create_from_payload( +ssize_t lttng_event_rule_kernel_probe_create_from_payload( struct lttng_payload_view *view, struct lttng_event_rule **_event_rule) { ssize_t ret, offset = 0; enum lttng_event_rule_status status; - const struct lttng_event_rule_kprobe_comm *kprobe_comm; + const struct lttng_event_rule_kernel_probe_comm *kprobe_comm; const char *name; struct lttng_buffer_view current_buffer_view; struct lttng_event_rule *rule = NULL; - struct lttng_event_rule_kprobe *kprobe = NULL; - struct lttng_kernel_probe_location *location; + struct lttng_kernel_probe_location *location = NULL; if (!_event_rule) { ret = -1; @@ -252,15 +286,6 @@ ssize_t lttng_event_rule_kprobe_create_from_payload( kprobe_comm = (typeof(kprobe_comm)) current_buffer_view.data; - rule = lttng_event_rule_kprobe_create(); - if (!rule) { - ERR("Failed to create event rule kprobe."); - ret = -1; - goto end; - } - - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); - /* Skip to payload */ offset += current_buffer_view.size; @@ -311,12 +336,17 @@ ssize_t lttng_event_rule_kprobe_create_from_payload( goto end; } - kprobe->location = location; - /* Skip after the location */ offset += kprobe_comm->location_len; - status = lttng_event_rule_kprobe_set_name(rule, name); + rule = lttng_event_rule_kernel_probe_create(location); + if (!rule) { + ERR("Failed to create event rule kprobe."); + ret = -1; + goto end; + } + + status = lttng_event_rule_kernel_probe_set_event_name(rule, name); if (status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to set event rule kprobe name."); ret = -1; @@ -327,54 +357,24 @@ ssize_t lttng_event_rule_kprobe_create_from_payload( rule = NULL; ret = offset; end: + lttng_kernel_probe_location_destroy(location); lttng_event_rule_destroy(rule); return ret; } -enum lttng_event_rule_status lttng_event_rule_kprobe_set_location( - struct lttng_event_rule *rule, - const struct lttng_kernel_probe_location *location) -{ - struct lttng_kernel_probe_location *location_copy = NULL; - struct lttng_event_rule_kprobe *kprobe; - enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; - - if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !location) { - status = LTTNG_EVENT_RULE_STATUS_INVALID; - goto end; - } - - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); - location_copy = lttng_kernel_probe_location_copy(location); - if (!location_copy) { - status = LTTNG_EVENT_RULE_STATUS_ERROR; - goto end; - } - - if (kprobe->location) { - lttng_kernel_probe_location_destroy(kprobe->location); - } - - kprobe->location = location_copy; - location_copy = NULL; -end: - lttng_kernel_probe_location_destroy(location_copy); - return status; -} - -enum lttng_event_rule_status lttng_event_rule_kprobe_get_location( +enum lttng_event_rule_status lttng_event_rule_kernel_probe_get_location( const struct lttng_event_rule *rule, const struct lttng_kernel_probe_location **location) { enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !location) { status = LTTNG_EVENT_RULE_STATUS_INVALID; goto end; } - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); *location = kprobe->location; if (!*location) { @@ -386,11 +386,11 @@ end: return status; } -enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( +enum lttng_event_rule_status lttng_event_rule_kernel_probe_set_event_name( struct lttng_event_rule *rule, const char *name) { char *name_copy = NULL; - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name || @@ -399,7 +399,7 @@ enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( goto end; } - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); name_copy = strdup(name); if (!name_copy) { status = LTTNG_EVENT_RULE_STATUS_ERROR; @@ -414,10 +414,10 @@ end: return status; } -enum lttng_event_rule_status lttng_event_rule_kprobe_get_name( +enum lttng_event_rule_status lttng_event_rule_kernel_probe_get_event_name( const struct lttng_event_rule *rule, const char **name) { - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name) { @@ -425,7 +425,7 @@ enum lttng_event_rule_status lttng_event_rule_kprobe_get_name( goto end; } - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); if (!kprobe->name) { status = LTTNG_EVENT_RULE_STATUS_UNSET; goto end; diff --git a/src/common/event-rule/syscall.c b/src/common/event-rule/syscall.c index b1b556aba..22353028f 100644 --- a/src/common/event-rule/syscall.c +++ b/src/common/event-rule/syscall.c @@ -249,6 +249,7 @@ struct lttng_event_rule *lttng_event_rule_syscall_create(void) { struct lttng_event_rule *rule = NULL; struct lttng_event_rule_syscall *syscall_rule; + enum lttng_event_rule_status status; syscall_rule = zmalloc(sizeof(struct lttng_event_rule_syscall)); if (!syscall_rule) { @@ -271,6 +272,14 @@ struct lttng_event_rule *lttng_event_rule_syscall_create(void) syscall_rule->parent.generate_exclusions = lttng_event_rule_syscall_generate_exclusions; syscall_rule->parent.hash = lttng_event_rule_syscall_hash; + + /* Default pattern is '*' */ + status = lttng_event_rule_syscall_set_pattern(rule, "*"); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + end: return rule; } diff --git a/src/common/event-rule/tracepoint.c b/src/common/event-rule/tracepoint.c index c8111a372..c3bac05c2 100644 --- a/src/common/event-rule/tracepoint.c +++ b/src/common/event-rule/tracepoint.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #define IS_TRACEPOINT_EVENT_RULE(rule) \ @@ -75,18 +77,22 @@ static int lttng_event_rule_tracepoint_serialize( struct lttng_payload *payload) { int ret, i; - size_t pattern_len, filter_expression_len, exclusions_len; + size_t pattern_len, filter_expression_len, exclusions_len, header_offset; + size_t size_before_log_level_rule; struct lttng_event_rule_tracepoint *tracepoint; struct lttng_event_rule_tracepoint_comm tracepoint_comm; enum lttng_event_rule_status status; unsigned int exclusion_count; size_t exclusions_appended_len = 0; + struct lttng_event_rule_tracepoint_comm *header; if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { ret = -1; goto end; } + header_offset = payload->buffer.size; + DBG("Serializing tracepoint event rule."); tracepoint = container_of( rule, struct lttng_event_rule_tracepoint, parent); @@ -118,8 +124,6 @@ static int lttng_event_rule_tracepoint_serialize( } tracepoint_comm.domain_type = (int8_t) tracepoint->domain; - tracepoint_comm.loglevel_type = (int8_t) tracepoint->loglevel.type; - tracepoint_comm.loglevel_value = tracepoint->loglevel.value; tracepoint_comm.pattern_len = pattern_len; tracepoint_comm.filter_expression_len = filter_expression_len; tracepoint_comm.exclusions_count = exclusion_count; @@ -143,6 +147,16 @@ static int lttng_event_rule_tracepoint_serialize( goto end; } + size_before_log_level_rule = payload->buffer.size; + + ret = lttng_log_level_rule_serialize(tracepoint->log_level_rule, payload); + if (ret < 0) { + goto end; + } + + header = (typeof(header)) ((char *) payload->buffer.data + header_offset); + header->log_level_rule_len = payload->buffer.size - size_before_log_level_rule; + for (i = 0; i < exclusion_count; i++) { size_t len; const char *exclusion; @@ -224,11 +238,7 @@ static bool lttng_event_rule_tracepoint_is_equal( goto end; } - if (a->loglevel.type != b->loglevel.type) { - goto end; - } - - if (a->loglevel.value != b->loglevel.value) { + if (!lttng_log_level_rule_is_equal(a->log_level_rule, b->log_level_rule)) { goto end; } @@ -266,7 +276,7 @@ static int generate_agent_filter( char *agent_filter = NULL; const char *pattern; const char *filter; - enum lttng_loglevel_type loglevel_type; + const struct lttng_log_level_rule *log_level_rule = NULL; enum lttng_event_rule_status status; assert(rule); @@ -286,12 +296,6 @@ static int generate_agent_filter( goto end; } - status = lttng_event_rule_tracepoint_get_log_level_type( - rule, &loglevel_type); - if (status != LTTNG_EVENT_RULE_STATUS_OK) { - ret = -1; - goto end; - } /* Don't add filter for the '*' event. */ if (strcmp(pattern, "*") != 0) { @@ -311,21 +315,32 @@ static int generate_agent_filter( } } - if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + status = lttng_event_rule_tracepoint_get_log_level_rule( + rule, &log_level_rule); + if (status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_log_level_rule_status llr_status; const char *op; - int loglevel_value; + int level; - status = lttng_event_rule_tracepoint_get_log_level( - rule, &loglevel_value); - if (status != LTTNG_EVENT_RULE_STATUS_OK) { - ret = -1; - goto end; + switch (lttng_log_level_rule_get_type(log_level_rule)) + { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &level); + op = "=="; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &level); + op = ">="; + break; + default: + abort(); } - if (loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE) { - op = ">="; - } else { - op = "=="; + if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) { + ret = -1; + goto end; } if (filter || agent_filter) { @@ -334,14 +349,14 @@ static int generate_agent_filter( err = asprintf(&new_filter, "(%s) && (int_loglevel %s %d)", agent_filter ? agent_filter : filter, - op, loglevel_value); + op, level); if (agent_filter) { free(agent_filter); } agent_filter = new_filter; } else { err = asprintf(&agent_filter, "int_loglevel %s %d", op, - loglevel_value); + level); } if (err < 0) { @@ -576,12 +591,8 @@ static unsigned long lttng_event_rule_tracepoint_hash( hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed); } - hash ^= hash_key_ulong((void *) tp_rule->loglevel.type, - lttng_ht_seed); - if (tp_rule->loglevel.type != LTTNG_EVENT_LOGLEVEL_ALL) { - hash ^= hash_key_ulong( - (void *) (unsigned long) tp_rule->loglevel.value, - lttng_ht_seed); + if (tp_rule->log_level_rule) { + hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule); } status = lttng_event_rule_tracepoint_get_exclusions_count(rule, @@ -607,6 +618,10 @@ static struct lttng_event *lttng_event_rule_tracepoint_generate_lttng_event( const struct lttng_event_rule_tracepoint *tracepoint; struct lttng_event *local_event = NULL; struct lttng_event *event = NULL; + enum lttng_loglevel_type loglevel_type; + int loglevel_value = 0; + enum lttng_event_rule_status status; + const struct lttng_log_level_rule *log_level_rule; tracepoint = container_of( rule, const struct lttng_event_rule_tracepoint, parent); @@ -625,8 +640,40 @@ static struct lttng_event *lttng_event_rule_tracepoint_generate_lttng_event( goto error; } - local_event->loglevel_type = tracepoint->loglevel.type; - local_event->loglevel = tracepoint->loglevel.value; + + /* Map the log level rule to an equivalent lttng_loglevel */ + status = lttng_event_rule_tracepoint_get_log_level_rule(rule, &log_level_rule); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { + loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + loglevel_value = 0; + } else if (status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_log_level_rule_status llr_status; + + switch (lttng_log_level_rule_get_type(log_level_rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &loglevel_value); + loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &loglevel_value); + loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; + break; + default: + abort(); + break; + } + + if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) { + goto error; + } + } else { + goto error; + } + + local_event->loglevel_type = loglevel_type; + local_event->loglevel = loglevel_value; event = local_event; local_event = NULL; @@ -640,6 +687,7 @@ struct lttng_event_rule *lttng_event_rule_tracepoint_create( { struct lttng_event_rule *rule = NULL; struct lttng_event_rule_tracepoint *tp_rule; + enum lttng_event_rule_status status; if (domain_type == LTTNG_DOMAIN_NONE) { goto end; @@ -669,10 +717,18 @@ struct lttng_event_rule *lttng_event_rule_tracepoint_create( lttng_event_rule_tracepoint_generate_lttng_event; tp_rule->domain = domain_type; - tp_rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL; + tp_rule->log_level_rule = NULL; lttng_dynamic_pointer_array_init(&tp_rule->exclusions, destroy_lttng_exclusions_element); + + /* Default pattern is '*' */ + status = lttng_event_rule_tracepoint_set_pattern(rule, "*"); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + end: return rule; } @@ -686,7 +742,6 @@ ssize_t lttng_event_rule_tracepoint_create_from_payload( int i; enum lttng_event_rule_status status; enum lttng_domain_type domain_type; - enum lttng_loglevel_type loglevel_type; const struct lttng_event_rule_tracepoint_comm *tracepoint_comm; const char *pattern; const char *filter_expression = NULL; @@ -695,6 +750,7 @@ ssize_t lttng_event_rule_tracepoint_create_from_payload( const char *exclusion; struct lttng_buffer_view current_buffer_view; struct lttng_event_rule *rule = NULL; + struct lttng_log_level_rule *log_level_rule = NULL; if (!_event_rule) { ret = -1; @@ -728,32 +784,6 @@ ssize_t lttng_event_rule_tracepoint_create_from_payload( goto end; } - loglevel_type = (enum lttng_loglevel_type) - tracepoint_comm->loglevel_type; - switch (loglevel_type) { - case LTTNG_EVENT_LOGLEVEL_ALL: - status = lttng_event_rule_tracepoint_set_log_level_all(rule); - break; - case LTTNG_EVENT_LOGLEVEL_RANGE: - status = lttng_event_rule_tracepoint_set_log_level_range_lower_bound(rule, - (enum lttng_loglevel_type) tracepoint_comm - ->loglevel_value); - break; - case LTTNG_EVENT_LOGLEVEL_SINGLE: - status = lttng_event_rule_tracepoint_set_log_level(rule, - (enum lttng_loglevel_type) tracepoint_comm - ->loglevel_value); - break; - default: - ERR("Failed to set event rule tracepoint loglevel: unknown loglevel type."); - ret = -1; - goto end; - } - - if (status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set event rule tracepoint loglevel."); - } - /* Skip to payload. */ offset += current_buffer_view.size; @@ -800,6 +830,29 @@ ssize_t lttng_event_rule_tracepoint_create_from_payload( offset += tracepoint_comm->filter_expression_len; skip_filter_expression: + if (!tracepoint_comm->log_level_rule_len) { + goto skip_log_level_rule; + } + + { + /* Map the log level rule. */ + struct lttng_payload_view current_payload_view = + lttng_payload_view_from_view(view, offset, + tracepoint_comm->log_level_rule_len); + ret = lttng_log_level_rule_create_from_payload( + ¤t_payload_view, &log_level_rule); + if (ret < 0) { + ret = -1; + goto end; + } + + assert(ret == tracepoint_comm->log_level_rule_len); + } + + /* Skip after the log level rule. */ + offset += tracepoint_comm->log_level_rule_len; + +skip_log_level_rule: for (i = 0; i < tracepoint_comm->exclusions_count; i++) { current_buffer_view = lttng_buffer_view_from_view( &view->buffer, offset, sizeof(*exclusion_len)); @@ -854,11 +907,22 @@ skip_filter_expression: } } + if (log_level_rule) { + status = lttng_event_rule_tracepoint_set_log_level_rule( + rule, log_level_rule); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule tracepoint log level rule."); + ret = -1; + goto end; + } + } + *_event_rule = rule; rule = NULL; ret = offset; end: free(exclusions); + lttng_log_level_rule_destroy(log_level_rule); lttng_event_rule_destroy(rule); return ret; } @@ -989,10 +1053,25 @@ end: return status; } -static bool log_level_value_valid( - int level, enum lttng_domain_type domain) +static bool log_level_rule_valid( + const struct lttng_log_level_rule *rule , enum lttng_domain_type domain) { bool valid = false; + enum lttng_log_level_rule_status status; + int level; + + switch(lttng_log_level_rule_get_type(rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + status = lttng_log_level_rule_exactly_get_level(rule, &level); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + status = lttng_log_level_rule_at_least_as_severe_as_get_level(rule, &level); + break; + default: + abort(); + } + + assert(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); switch (domain) { case LTTNG_DOMAIN_KERNEL: @@ -1031,11 +1110,13 @@ end: return valid; } -enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level( - struct lttng_event_rule *rule, int level) +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level_rule( + struct lttng_event_rule *rule, + const struct lttng_log_level_rule *log_level_rule) { struct lttng_event_rule_tracepoint *tracepoint; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + struct lttng_log_level_rule *copy = NULL; if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { status = LTTNG_EVENT_RULE_STATUS_INVALID; @@ -1045,99 +1126,48 @@ enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level( tracepoint = container_of( rule, struct lttng_event_rule_tracepoint, parent); - if (!log_level_value_valid(level, tracepoint->domain)) { + if (!log_level_rule_valid(log_level_rule, tracepoint->domain)) { status = LTTNG_EVENT_RULE_STATUS_INVALID; goto end; } - tracepoint->loglevel.value = level; - tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_SINGLE; -end: - return status; -} - -enum lttng_event_rule_status -lttng_event_rule_tracepoint_set_log_level_range_lower_bound( - struct lttng_event_rule *rule, int level) -{ - struct lttng_event_rule_tracepoint *tracepoint; - enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; - - if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { - status = LTTNG_EVENT_RULE_STATUS_INVALID; + copy = lttng_log_level_rule_copy(log_level_rule); + if (copy == NULL) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; goto end; } - tracepoint = container_of( - rule, struct lttng_event_rule_tracepoint, parent); - - if (!log_level_value_valid(level, tracepoint->domain)) { - status = LTTNG_EVENT_RULE_STATUS_INVALID; - goto end; + if (tracepoint->log_level_rule) { + lttng_log_level_rule_destroy(tracepoint->log_level_rule); } - tracepoint->loglevel.value = level; - tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_RANGE; -end: - return status; -} - -enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level_all( - struct lttng_event_rule *rule) -{ - struct lttng_event_rule_tracepoint *tracepoint; - enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + tracepoint->log_level_rule = copy; - if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { - status = LTTNG_EVENT_RULE_STATUS_INVALID; - goto end; - } - - tracepoint = container_of( - rule, struct lttng_event_rule_tracepoint, parent); - tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL; end: return status; } -enum lttng_event_rule_status lttng_event_rule_tracepoint_get_log_level_type( +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_log_level_rule( const struct lttng_event_rule *rule, - enum lttng_loglevel_type *type) -{ - struct lttng_event_rule_tracepoint *tracepoint; - enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; - - if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !type) { - status = LTTNG_EVENT_RULE_STATUS_INVALID; - goto end; - } - - tracepoint = container_of( - rule, struct lttng_event_rule_tracepoint, parent); - *type = tracepoint->loglevel.type; -end: - return status; -} - -enum lttng_event_rule_status lttng_event_rule_tracepoint_get_log_level( - const struct lttng_event_rule *rule, int *level) + const struct lttng_log_level_rule **log_level_rule + ) { struct lttng_event_rule_tracepoint *tracepoint; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; - if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !level) { + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !log_level_rule) { status = LTTNG_EVENT_RULE_STATUS_INVALID; goto end; } tracepoint = container_of( rule, struct lttng_event_rule_tracepoint, parent); - if (tracepoint->loglevel.type == LTTNG_EVENT_LOGLEVEL_ALL) { + if (tracepoint->log_level_rule == NULL) { status = LTTNG_EVENT_RULE_STATUS_UNSET; goto end; } - *level = tracepoint->loglevel.value; + *log_level_rule = tracepoint->log_level_rule; end: return status; } diff --git a/src/common/event-rule/uprobe.c b/src/common/event-rule/userspace-probe.c similarity index 63% rename from src/common/event-rule/uprobe.c rename to src/common/event-rule/userspace-probe.c index 67f99c482..52c7553e9 100644 --- a/src/common/event-rule/uprobe.c +++ b/src/common/event-rule/userspace-probe.c @@ -15,34 +15,34 @@ #include #include #include -#include +#include #include #define IS_UPROBE_EVENT_RULE(rule) \ - (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE) + (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE) -static void lttng_event_rule_uprobe_destroy(struct lttng_event_rule *rule) +static void lttng_event_rule_userspace_probe_destroy(struct lttng_event_rule *rule) { - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); lttng_userspace_probe_location_destroy(uprobe->location); free(uprobe->name); free(uprobe); } -static bool lttng_event_rule_uprobe_validate( +static bool lttng_event_rule_userspace_probe_validate( const struct lttng_event_rule *rule) { bool valid = false; - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; if (!rule) { goto end; } - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); /* Required field. */ if (!uprobe->name) { @@ -60,15 +60,15 @@ end: return valid; } -static int lttng_event_rule_uprobe_serialize( +static int lttng_event_rule_userspace_probe_serialize( const struct lttng_event_rule *rule, struct lttng_payload *payload) { int ret; size_t name_len, header_offset, size_before_probe; - struct lttng_event_rule_uprobe *uprobe; - struct lttng_event_rule_uprobe_comm uprobe_comm = {}; - struct lttng_event_rule_uprobe_comm *header; + struct lttng_event_rule_userspace_probe *uprobe; + struct lttng_event_rule_userspace_probe_comm uprobe_comm = {}; + struct lttng_event_rule_userspace_probe_comm *header; if (!rule || !IS_UPROBE_EVENT_RULE(rule)) { ret = -1; @@ -78,7 +78,7 @@ static int lttng_event_rule_uprobe_serialize( header_offset = payload->buffer.size; DBG("Serializing uprobe event rule."); - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); name_len = strlen(uprobe->name) + 1; @@ -105,7 +105,7 @@ static int lttng_event_rule_uprobe_serialize( } /* Update the header regarding the probe size. */ - header = (struct lttng_event_rule_uprobe_comm + header = (struct lttng_event_rule_userspace_probe_comm *) ((char *) payload->buffer.data + header_offset); header->location_len = payload->buffer.size - size_before_probe; @@ -116,14 +116,14 @@ end: return ret; } -static bool lttng_event_rule_uprobe_is_equal(const struct lttng_event_rule *_a, +static bool lttng_event_rule_userspace_probe_is_equal(const struct lttng_event_rule *_a, const struct lttng_event_rule *_b) { bool is_equal = false; - struct lttng_event_rule_uprobe *a, *b; + struct lttng_event_rule_userspace_probe *a, *b; - a = container_of(_a, struct lttng_event_rule_uprobe, parent); - b = container_of(_b, struct lttng_event_rule_uprobe, parent); + a = container_of(_a, struct lttng_event_rule_userspace_probe, parent); + b = container_of(_b, struct lttng_event_rule_userspace_probe, parent); /* uprobe is invalid if this is not true. */ assert(a->name); @@ -140,7 +140,7 @@ end: return is_equal; } -static enum lttng_error_code lttng_event_rule_uprobe_generate_filter_bytecode( +static enum lttng_error_code lttng_event_rule_userspace_probe_generate_filter_bytecode( struct lttng_event_rule *rule, const struct lttng_credentials *creds) { @@ -148,7 +148,7 @@ static enum lttng_error_code lttng_event_rule_uprobe_generate_filter_bytecode( return LTTNG_OK; } -static const char *lttng_event_rule_uprobe_get_filter( +static const char *lttng_event_rule_userspace_probe_get_filter( const struct lttng_event_rule *rule) { /* Unsupported. */ @@ -156,14 +156,14 @@ static const char *lttng_event_rule_uprobe_get_filter( } static const struct lttng_bytecode * -lttng_event_rule_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule) +lttng_event_rule_userspace_probe_get_filter_bytecode(const struct lttng_event_rule *rule) { /* Unsupported. */ return NULL; } static enum lttng_event_rule_generate_exclusions_status -lttng_event_rule_uprobe_generate_exclusions(const struct lttng_event_rule *rule, +lttng_event_rule_userspace_probe_generate_exclusions(const struct lttng_event_rule *rule, struct lttng_event_exclusion **exclusions) { /* Unsupported. */ @@ -172,14 +172,14 @@ lttng_event_rule_uprobe_generate_exclusions(const struct lttng_event_rule *rule, } static unsigned long -lttng_event_rule_uprobe_hash( +lttng_event_rule_userspace_probe_hash( const struct lttng_event_rule *rule) { unsigned long hash; - struct lttng_event_rule_uprobe *urule = + struct lttng_event_rule_userspace_probe *urule = container_of(rule, typeof(*urule), parent); - hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_UPROBE, + hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE, lttng_ht_seed); hash ^= hash_key_str(urule->name, lttng_ht_seed); hash ^= lttng_userspace_probe_location_hash(urule->location); @@ -187,46 +187,79 @@ lttng_event_rule_uprobe_hash( return hash; } -struct lttng_event_rule *lttng_event_rule_uprobe_create(void) +static +int userspace_probe_set_location( + struct lttng_event_rule_userspace_probe *uprobe, + const struct lttng_userspace_probe_location *location) +{ + int ret; + struct lttng_userspace_probe_location *location_copy = NULL; + + if (!uprobe || !location || uprobe->location) { + ret = -1; + goto end; + } + + location_copy = lttng_userspace_probe_location_copy(location); + if (!location_copy) { + ret = -1; + goto end; + } + + uprobe->location = location_copy; + location_copy = NULL; + ret = 0; +end: + lttng_userspace_probe_location_destroy(location_copy); + return ret; +} + +struct lttng_event_rule *lttng_event_rule_userspace_probe_create( + const struct lttng_userspace_probe_location *location) { struct lttng_event_rule *rule = NULL; - struct lttng_event_rule_uprobe *urule; + struct lttng_event_rule_userspace_probe *urule; - urule = zmalloc(sizeof(struct lttng_event_rule_uprobe)); + urule = zmalloc(sizeof(struct lttng_event_rule_userspace_probe)); if (!urule) { goto end; } rule = &urule->parent; - lttng_event_rule_init(&urule->parent, LTTNG_EVENT_RULE_TYPE_UPROBE); - urule->parent.validate = lttng_event_rule_uprobe_validate; - urule->parent.serialize = lttng_event_rule_uprobe_serialize; - urule->parent.equal = lttng_event_rule_uprobe_is_equal; - urule->parent.destroy = lttng_event_rule_uprobe_destroy; + lttng_event_rule_init(&urule->parent, LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE); + urule->parent.validate = lttng_event_rule_userspace_probe_validate; + urule->parent.serialize = lttng_event_rule_userspace_probe_serialize; + urule->parent.equal = lttng_event_rule_userspace_probe_is_equal; + urule->parent.destroy = lttng_event_rule_userspace_probe_destroy; urule->parent.generate_filter_bytecode = - lttng_event_rule_uprobe_generate_filter_bytecode; - urule->parent.get_filter = lttng_event_rule_uprobe_get_filter; + lttng_event_rule_userspace_probe_generate_filter_bytecode; + urule->parent.get_filter = lttng_event_rule_userspace_probe_get_filter; urule->parent.get_filter_bytecode = - lttng_event_rule_uprobe_get_filter_bytecode; + lttng_event_rule_userspace_probe_get_filter_bytecode; urule->parent.generate_exclusions = - lttng_event_rule_uprobe_generate_exclusions; - urule->parent.hash = lttng_event_rule_uprobe_hash; + lttng_event_rule_userspace_probe_generate_exclusions; + urule->parent.hash = lttng_event_rule_userspace_probe_hash; + + if (userspace_probe_set_location(urule, location)) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + end: return rule; } LTTNG_HIDDEN -ssize_t lttng_event_rule_uprobe_create_from_payload( +ssize_t lttng_event_rule_userspace_probe_create_from_payload( struct lttng_payload_view *view, struct lttng_event_rule **_event_rule) { ssize_t ret, offset = 0; - const struct lttng_event_rule_uprobe_comm *uprobe_comm; + const struct lttng_event_rule_userspace_probe_comm *uprobe_comm; const char *name; struct lttng_buffer_view current_buffer_view; struct lttng_event_rule *rule = NULL; - struct lttng_userspace_probe_location *location; - struct lttng_event_rule_uprobe *uprobe; + struct lttng_userspace_probe_location *location = NULL; enum lttng_event_rule_status status; if (!_event_rule) { @@ -244,13 +277,6 @@ ssize_t lttng_event_rule_uprobe_create_from_payload( uprobe_comm = (typeof(uprobe_comm)) current_buffer_view.data; - rule = lttng_event_rule_uprobe_create(); - if (!rule) { - ERR("Failed to create event rule uprobe"); - ret = -1; - goto end; - } - /* Skip to payload. */ offset += current_buffer_view.size; @@ -297,16 +323,20 @@ ssize_t lttng_event_rule_uprobe_create_from_payload( /* Skip after the location. */ offset += uprobe_comm->location_len; - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); - uprobe->location = location; + rule = lttng_event_rule_userspace_probe_create(location); + if (!rule) { + ERR("Failed to create event rule uprobe."); + ret = -1; + goto end; + } - status = lttng_event_rule_uprobe_set_name(rule, name); + status = lttng_event_rule_userspace_probe_set_event_name(rule, name); if (status != LTTNG_EVENT_RULE_STATUS_OK) { ret = -1; goto end; } - if (!lttng_event_rule_uprobe_validate(rule)) { + if (!lttng_event_rule_userspace_probe_validate(rule)) { ret = -1; goto end; } @@ -315,42 +345,13 @@ ssize_t lttng_event_rule_uprobe_create_from_payload( rule = NULL; ret = offset; end: + lttng_userspace_probe_location_destroy(location); lttng_event_rule_destroy(rule); return ret; } -enum lttng_event_rule_status lttng_event_rule_uprobe_set_location( - struct lttng_event_rule *rule, - const struct lttng_userspace_probe_location *location) -{ - struct lttng_userspace_probe_location *location_copy = NULL; - struct lttng_event_rule_uprobe *uprobe; - enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; - - if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) { - status = LTTNG_EVENT_RULE_STATUS_INVALID; - goto end; - } - - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); - location_copy = lttng_userspace_probe_location_copy(location); - if (!location_copy) { - status = LTTNG_EVENT_RULE_STATUS_ERROR; - goto end; - } - - if (uprobe->location) { - lttng_userspace_probe_location_destroy(uprobe->location); - } - - uprobe->location = location_copy; - location_copy = NULL; -end: - lttng_userspace_probe_location_destroy(location_copy); - return status; -} -enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( +enum lttng_event_rule_status lttng_event_rule_userspace_probe_get_location( const struct lttng_event_rule *rule, const struct lttng_userspace_probe_location **location) { @@ -361,7 +362,7 @@ enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( goto end; } - *location = lttng_event_rule_uprobe_get_location_mutable(rule); + *location = lttng_event_rule_userspace_probe_get_location_mutable(rule); if (!*location) { status = LTTNG_EVENT_RULE_STATUS_UNSET; goto end; @@ -373,22 +374,22 @@ end: LTTNG_HIDDEN struct lttng_userspace_probe_location * -lttng_event_rule_uprobe_get_location_mutable( +lttng_event_rule_userspace_probe_get_location_mutable( const struct lttng_event_rule *rule) { - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; assert(rule); - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); return uprobe->location; } -enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( +enum lttng_event_rule_status lttng_event_rule_userspace_probe_set_event_name( struct lttng_event_rule *rule, const char *name) { char *name_copy = NULL; - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name || @@ -397,7 +398,7 @@ enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( goto end; } - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); name_copy = strdup(name); if (!name_copy) { status = LTTNG_EVENT_RULE_STATUS_ERROR; @@ -414,10 +415,10 @@ end: return status; } -enum lttng_event_rule_status lttng_event_rule_uprobe_get_name( +enum lttng_event_rule_status lttng_event_rule_userspace_probe_get_event_name( const struct lttng_event_rule *rule, const char **name) { - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name) { @@ -425,7 +426,7 @@ enum lttng_event_rule_status lttng_event_rule_uprobe_get_name( goto end; } - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); if (!uprobe->name) { status = LTTNG_EVENT_RULE_STATUS_UNSET; goto end; diff --git a/src/common/hashtable/hashtable.c b/src/common/hashtable/hashtable.c index f449fab8b..af16676cc 100644 --- a/src/common/hashtable/hashtable.c +++ b/src/common/hashtable/hashtable.c @@ -76,6 +76,24 @@ static int match_two_u64(struct cds_lfht_node *node, const void *key) return hash_match_key_two_u64((void *) &match_node->key, (void *) key); } +static inline +const char *lttng_ht_type_str(enum lttng_ht_type type) +{ + switch (type) { + case LTTNG_HT_TYPE_STRING: + return "STRING"; + case LTTNG_HT_TYPE_ULONG: + return "ULONG"; + case LTTNG_HT_TYPE_U64: + return "U64"; + case LTTNG_HT_TYPE_TWO_U64: + return "TWO_U64"; + default: + ERR("Unknown lttng hashtable type %d", type); + abort(); + } +} + /* * Return an allocated lttng hashtable. */ @@ -132,7 +150,8 @@ struct lttng_ht *lttng_ht_new(unsigned long size, int type) goto error; } - DBG3("Created hashtable size %lu at %p of type %d", size, ht->ht, type); + DBG3("Created hashtable size %lu at %p of type %s", size, ht->ht, + lttng_ht_type_str(type)); return ht; diff --git a/src/common/index-allocator.c b/src/common/index-allocator.c new file mode 100644 index 000000000..ff5e2c886 --- /dev/null +++ b/src/common/index-allocator.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include + +#include +#include + +#include "macros.h" +#include "error.h" + +#include "index-allocator.h" + +struct lttng_index_allocator { + struct cds_list_head unused_list; + uint64_t size; + uint64_t position; +}; + +struct lttng_index { + uint64_t index; + struct cds_list_head head; +}; + +struct lttng_index_allocator *lttng_index_allocator_create( + uint64_t index_count) +{ + struct lttng_index_allocator *allocator = NULL; + + allocator = zmalloc(sizeof(*allocator)); + if (!allocator) { + PERROR("Failed to allocate free index queue"); + goto end; + } + allocator->size = index_count; + allocator->position = 0; + + CDS_INIT_LIST_HEAD(&allocator->unused_list); + +end: + return allocator; +} + +uint64_t lttng_index_allocator_get_index_count(struct lttng_index_allocator *allocator) +{ + return allocator->size; +} + +enum lttng_index_allocator_status lttng_index_allocator_alloc( + struct lttng_index_allocator *allocator, + uint64_t *allocated_index) +{ + enum lttng_index_allocator_status status = + LTTNG_INDEX_ALLOCATOR_STATUS_OK; + + if (cds_list_empty(&allocator->unused_list)) { + if (allocator->position >= allocator->size) { + /* No indices left. */ + status = LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY; + goto end; + } + + *allocated_index = allocator->position++; + } else { + struct lttng_index *index; + + index = cds_list_first_entry(&allocator->unused_list, + typeof(*index), head); + cds_list_del(&index->head); + *allocated_index = index->index; + free(index); + } + +end: + return status; +} + +enum lttng_index_allocator_status lttng_index_allocator_release( + struct lttng_index_allocator *allocator, uint64_t idx) +{ + struct lttng_index *index = NULL; + enum lttng_index_allocator_status status = + LTTNG_INDEX_ALLOCATOR_STATUS_OK; + + assert(idx < allocator->size); + + index = zmalloc(sizeof(*index)); + if (!index) { + PERROR("Failed to allocate free index queue"); + status = LTTNG_INDEX_ALLOCATOR_STATUS_ERROR; + goto end; + } + + index->index = idx; + cds_list_add_tail(&index->head, &allocator->unused_list); + +end: + return status; +} + +void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator) +{ + struct lttng_index *index = NULL, *tmp_index = NULL; + + if (!allocator) { + return; + } + + cds_list_for_each_entry_safe(index, tmp_index, + &allocator->unused_list, head) { + cds_list_del(&index->head); + free(index); + } + + free(allocator); +} diff --git a/src/common/index-allocator.h b/src/common/index-allocator.h new file mode 100644 index 000000000..cdc575041 --- /dev/null +++ b/src/common/index-allocator.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef _COMMON_INDEX_ALLOCATOR_H +#define _COMMON_INDEX_ALLOCATOR_H + +#include + +struct lttng_index_allocator; + +enum lttng_index_allocator_status { + LTTNG_INDEX_ALLOCATOR_STATUS_OK, + LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY, + LTTNG_INDEX_ALLOCATOR_STATUS_ERROR, +}; + +struct lttng_index_allocator *lttng_index_allocator_create( + uint64_t index_count); + +uint64_t lttng_index_allocator_get_index_count( + struct lttng_index_allocator *allocator); + +enum lttng_index_allocator_status lttng_index_allocator_alloc( + struct lttng_index_allocator *allocator, + uint64_t *index); + +enum lttng_index_allocator_status lttng_index_allocator_release( + struct lttng_index_allocator *allocator, uint64_t index); + +void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator); + +#endif /* _COMMON_INDEX_ALLOCATOR_H */ diff --git a/src/common/kernel-ctl/kernel-ctl.c b/src/common/kernel-ctl/kernel-ctl.c index bb6602e2b..7bcfd5dc2 100644 --- a/src/common/kernel-ctl/kernel-ctl.c +++ b/src/common/kernel-ctl/kernel-ctl.c @@ -155,6 +155,12 @@ int kernctl_create_channel(int fd, struct lttng_channel_attr *chops) return LTTNG_IOCTL_NO_CHECK(fd, LTTNG_KERNEL_CHANNEL, &channel); } +int kernctl_create_session_counter(int session_fd, struct lttng_kernel_counter_conf *counter_conf) +{ + return LTTNG_IOCTL_NO_CHECK(session_fd, LTTNG_KERNEL_COUNTER, counter_conf); +} + + int kernctl_syscall_mask(int fd, char **syscall_mask, uint32_t *nr_bits) { struct lttng_kernel_syscall_mask kmask_len, *kmask = NULL; @@ -424,12 +430,64 @@ int kernctl_create_event_notifier_group(int fd) LTTNG_KERNEL_EVENT_NOTIFIER_GROUP_CREATE); } +int kernctl_create_counter_event(int fd, struct lttng_kernel_counter_event *ev) +{ + return LTTNG_IOCTL_NO_CHECK(fd, LTTNG_KERNEL_COUNTER_EVENT, ev); +} + int kernctl_create_event_notifier_group_notification_fd(int group_fd) { return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD); } +int kernctl_create_event_notifier_group_error_counter(int group_fd, + struct lttng_kernel_counter_conf *error_counter_conf) +{ + return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_COUNTER, + error_counter_conf); +} + +int kernctl_counter_read_value(int counter_fd, + struct lttng_kernel_counter_read *value) +{ + return LTTNG_IOCTL_NO_CHECK(counter_fd, LTTNG_KERNEL_COUNTER_READ, + value); +} + +int kernctl_counter_get_aggregate_value(int counter_fd, + struct lttng_kernel_counter_aggregate *value) +{ + return LTTNG_IOCTL_NO_CHECK(counter_fd, LTTNG_KERNEL_COUNTER_AGGREGATE, + value); +} + +int kernctl_counter_clear(int counter_fd, + struct lttng_kernel_counter_clear *clear) +{ + return LTTNG_IOCTL_NO_CHECK(counter_fd, LTTNG_KERNEL_COUNTER_CLEAR, + clear); +} + +int kernctl_counter_map_descriptor_count(int counter_fd, uint64_t *count) +{ + struct lttng_kernel_counter_map_nr_descriptors nr_desc; + int ret; + + ret = LTTNG_IOCTL_NO_CHECK(counter_fd, + LTTNG_KERNEL_COUNTER_MAP_NR_DESCRIPTORS, &nr_desc); + *count = nr_desc.nr_descriptors; + + return ret; +} + +int kernctl_counter_map_descriptor(int counter_fd, + struct lttng_kernel_counter_map_descriptor *descriptor) +{ + return LTTNG_IOCTL_NO_CHECK(counter_fd, + LTTNG_KERNEL_COUNTER_MAP_DESCRIPTOR, descriptor); +} + int kernctl_create_event_notifier(int group_fd, const struct lttng_kernel_event_notifier *event_notifier) { @@ -437,6 +495,25 @@ int kernctl_create_event_notifier(int group_fd, LTTNG_KERNEL_EVENT_NOTIFIER_CREATE, event_notifier); } +int kernctl_capture(int fd, const struct lttng_bytecode *capture) +{ + struct lttng_kernel_capture_bytecode *kb; + uint32_t len; + int ret; + + /* Translate bytecode to kernel bytecode */ + kb = zmalloc(sizeof(*kb) + capture->len); + if (!kb) + return -ENOMEM; + kb->len = len = capture->len; + kb->reloc_offset = capture->reloc_table_offset; + kb->seqnum = capture->seqnum; + memcpy(kb->data, capture->data, len); + ret = LTTNG_IOCTL_CHECK(fd, LTTNG_KERNEL_CAPTURE, kb); + free(kb); + return ret; +} + int kernctl_filter(int fd, const struct lttng_bytecode *filter) { struct lttng_kernel_filter_bytecode *kb; diff --git a/src/common/kernel-ctl/kernel-ctl.h b/src/common/kernel-ctl/kernel-ctl.h index 3bbe69f48..537590f35 100644 --- a/src/common/kernel-ctl/kernel-ctl.h +++ b/src/common/kernel-ctl/kernel-ctl.h @@ -19,8 +19,10 @@ int kernctl_create_session(int fd); int kernctl_open_metadata(int fd, struct lttng_channel_attr *chops); int kernctl_create_channel(int fd, struct lttng_channel_attr *chops); +int kernctl_create_session_counter(int session_fd, struct lttng_kernel_counter_conf *counter_conf); int kernctl_create_stream(int fd); int kernctl_create_event(int fd, struct lttng_kernel_event *ev); +int kernctl_create_counter_event(int fd, struct lttng_kernel_counter_event *ev); int kernctl_add_context(int fd, struct lttng_kernel_context *ctx); int kernctl_enable(int fd); @@ -32,12 +34,26 @@ int kernctl_create_event_notifier_group(int fd); /* Apply on event notifier_group file descriptor. */ int kernctl_create_event_notifier_group_notification_fd(int fd); +int kernctl_create_event_notifier_group_error_counter(int fd, + struct lttng_kernel_counter_conf *error_counter_conf); int kernctl_create_event_notifier(int fd, const struct lttng_kernel_event_notifier *event_notifier); +int kernctl_counter_read_value(int counter_fd, + struct lttng_kernel_counter_read *value); +int kernctl_counter_get_aggregate_value(int counter_fd, + struct lttng_kernel_counter_aggregate *value); +int kernctl_counter_clear(int counter_fd, + struct lttng_kernel_counter_clear *clear); + +int kernctl_counter_map_descriptor_count(int counter_fd, uint64_t *count); +int kernctl_counter_map_descriptor(int counter_fd, + struct lttng_kernel_counter_map_descriptor *descriptor); + /* Apply on event file descriptor. */ int kernctl_filter(int fd, const struct lttng_bytecode *filter); int kernctl_add_callsite(int fd, struct lttng_kernel_event_callsite *callsite); +int kernctl_capture(int fd, const struct lttng_bytecode *capture); int kernctl_tracepoint_list(int fd); int kernctl_syscall_list(int fd); diff --git a/src/common/kernel-ctl/kernel-ioctl.h b/src/common/kernel-ctl/kernel-ioctl.h index 8fa0a7d10..e45359763 100644 --- a/src/common/kernel-ctl/kernel-ioctl.h +++ b/src/common/kernel-ctl/kernel-ioctl.h @@ -160,6 +160,10 @@ #define LTTNG_KERNEL_ENABLE _IO(0xF6, 0x82) #define LTTNG_KERNEL_DISABLE _IO(0xF6, 0x83) +/* Event notifier group ioctl */ +#define LTTNG_KERNEL_COUNTER \ + _IOW(0xF6, 0x84, struct lttng_kernel_counter_conf) + /* Event and event notifier FD ioctl */ #define LTTNG_KERNEL_FILTER _IO(0xF6, 0x90) #define LTTNG_KERNEL_ADD_CALLSITE _IO(0xF6, 0x91) @@ -178,4 +182,23 @@ #define LTTNG_KERNEL_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD \ _IO(0xF6, 0xB1) +/* Event notifier file descriptor ioctl */ +#define LTTNG_KERNEL_CAPTURE _IO(0xF6, 0xB8) + +/* Counter file descriptor ioctl */ +#define LTTNG_KERNEL_COUNTER_READ \ + _IOWR(0xF6, 0xC0, struct lttng_kernel_counter_read) +#define LTTNG_KERNEL_COUNTER_AGGREGATE \ + _IOWR(0xF6, 0xC1, struct lttng_kernel_counter_aggregate) +#define LTTNG_KERNEL_COUNTER_CLEAR \ + _IOW(0xF6, 0xC2, struct lttng_kernel_counter_clear) +#define LTTNG_KERNEL_COUNTER_MAP_NR_DESCRIPTORS \ + _IOR(0xF6, 0xC3, uint64_t) +#define LTTNG_KERNEL_COUNTER_MAP_DESCRIPTOR \ + _IOWR(0xF6, 0xC4, struct lttng_kernel_counter_map_descriptor) +#define LTTNG_KERNEL_COUNTER_EVENT \ + _IOW(0xF6, 0xC5, struct lttng_kernel_counter_event) + +/* LTTNG_KERNEL_EVENT also applies to counter fds. */ + #endif /* _LTT_KERNEL_IOCTL_H */ diff --git a/src/common/kernel-function.c b/src/common/kernel-function.c new file mode 100644 index 000000000..c0b1cef92 --- /dev/null +++ b/src/common/kernel-function.c @@ -0,0 +1,726 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "lttng/lttng-error.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static +int lttng_kernel_function_location_address_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload); + +static +int lttng_kernel_function_location_symbol_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload); + +static +bool lttng_kernel_function_location_address_is_equal( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); + +static +bool lttng_kernel_function_location_symbol_is_equal( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); + +static +unsigned long lttng_kernel_function_location_address_hash( + const struct lttng_kernel_function_location *location); + +static +unsigned long lttng_kernel_function_location_symbol_hash( + const struct lttng_kernel_function_location *location); + +enum lttng_kernel_function_location_type lttng_kernel_function_location_get_type( + const struct lttng_kernel_function_location *location) +{ + return location ? location->type : + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_UNKNOWN; +} + +static +void lttng_kernel_function_location_address_destroy( + struct lttng_kernel_function_location *location) +{ + assert(location); + free(location); +} + +static +void lttng_kernel_function_location_symbol_destroy( + struct lttng_kernel_function_location *location) +{ + struct lttng_kernel_function_location_symbol *location_symbol = NULL; + + assert(location); + + location_symbol = container_of(location, + struct lttng_kernel_function_location_symbol, + parent); + + assert(location_symbol); + + free(location_symbol->symbol_name); + free(location); +} + +void lttng_kernel_function_location_destroy( + struct lttng_kernel_function_location *location) +{ + if (!location) { + return; + } + + switch (location->type) { + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS: + lttng_kernel_function_location_address_destroy(location); + break; + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET: + lttng_kernel_function_location_symbol_destroy(location); + break; + default: + abort(); + } +} + +struct lttng_kernel_function_location * +lttng_kernel_function_location_address_create(uint64_t address) +{ + struct lttng_kernel_function_location *ret = NULL; + struct lttng_kernel_function_location_address *location; + + location = zmalloc(sizeof(*location)); + if (!location) { + PERROR("Error allocating userspace function location."); + goto end; + } + + location->address = address; + + ret = &location->parent; + ret->type = LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS; + ret->equal = lttng_kernel_function_location_address_is_equal; + ret->serialize = lttng_kernel_function_location_address_serialize; + ret->hash = lttng_kernel_function_location_address_hash; + +end: + return ret; +} + +struct lttng_kernel_function_location * +lttng_kernel_function_location_symbol_create(const char *symbol_name, + uint64_t offset) +{ + char *symbol_name_copy = NULL; + struct lttng_kernel_function_location *ret = NULL; + struct lttng_kernel_function_location_symbol *location; + + if (!symbol_name || strlen(symbol_name) >= LTTNG_SYMBOL_NAME_LEN) { + goto error; + } + + symbol_name_copy = strdup(symbol_name); + if (!symbol_name_copy) { + PERROR("Failed to copy symbol name '%s'", symbol_name); + goto error; + } + + location = zmalloc(sizeof(*location)); + if (!location) { + PERROR("Failed to allocate kernel symbol function location"); + goto error; + } + + location->symbol_name = symbol_name_copy; + location->offset = offset; + + ret = &location->parent; + ret->type = LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET; + ret->equal = lttng_kernel_function_location_symbol_is_equal; + ret->serialize = lttng_kernel_function_location_symbol_serialize; + ret->hash = lttng_kernel_function_location_symbol_hash; + goto end; + +error: + free(symbol_name_copy); +end: + return ret; +} + +enum lttng_kernel_function_location_status +lttng_kernel_function_location_address_get_address( + const struct lttng_kernel_function_location *location, + uint64_t *offset) +{ + enum lttng_kernel_function_location_status ret = + LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK; + struct lttng_kernel_function_location_address *address_location; + + assert(offset); + + if (!location || lttng_kernel_function_location_get_type(location) != + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_INVALID; + goto end; + } + + address_location = container_of(location, + struct lttng_kernel_function_location_address, parent); + *offset = address_location->address; +end: + return ret; +} + +const char *lttng_kernel_function_location_symbol_get_name( + const struct lttng_kernel_function_location *location) +{ + const char *ret = NULL; + struct lttng_kernel_function_location_symbol *symbol_location; + + if (!location || lttng_kernel_function_location_get_type(location) != + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + symbol_location = container_of(location, + struct lttng_kernel_function_location_symbol, parent); + ret = symbol_location->symbol_name; +end: + return ret; +} + +enum lttng_kernel_function_location_status +lttng_kernel_function_location_symbol_get_offset( + const struct lttng_kernel_function_location *location, + uint64_t *offset) +{ + enum lttng_kernel_function_location_status ret = + LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK; + struct lttng_kernel_function_location_symbol *symbol_location; + + assert(offset); + + if (!location || lttng_kernel_function_location_get_type(location) != + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_INVALID; + goto end; + } + + symbol_location = container_of(location, + struct lttng_kernel_function_location_symbol, parent); + *offset = symbol_location->offset; +end: + return ret; +} + +static +int lttng_kernel_function_location_symbol_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t symbol_name_len; + size_t original_payload_size; + struct lttng_kernel_function_location_symbol *location_symbol; + struct lttng_kernel_function_location_symbol_comm location_symbol_comm; + + if (!location || !payload) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = -LTTNG_ERR_INVALID; + goto end; + } + + assert(lttng_kernel_function_location_get_type(location) == + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET); + + original_payload_size = payload->buffer.size; + location_symbol = container_of(location, + struct lttng_kernel_function_location_symbol, parent); + + if (!location_symbol->symbol_name) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + symbol_name_len = strlen(location_symbol->symbol_name); + if (symbol_name_len == 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_symbol_comm.symbol_len = symbol_name_len + 1; + location_symbol_comm.offset = location_symbol->offset; + + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_symbol_comm, sizeof(location_symbol_comm)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + location_symbol->symbol_name, + location_symbol_comm.symbol_len); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (int) (payload->buffer.size - original_payload_size); +end: + return ret; +} + +static +int lttng_kernel_function_location_address_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t original_payload_size; + struct lttng_kernel_function_location_address *location_address; + struct lttng_kernel_function_location_address_comm location_address_comm; + + assert(location); + assert(lttng_kernel_function_location_get_type(location) == + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS); + + original_payload_size = payload->buffer.size; + location_address = container_of(location, + struct lttng_kernel_function_location_address, + parent); + + location_address_comm.address = location_address->address; + + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_address_comm, + sizeof(location_address_comm)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (int) (payload->buffer.size - original_payload_size); +end: + return ret; +} + +LTTNG_HIDDEN +int lttng_kernel_function_location_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t original_payload_size; + struct lttng_kernel_function_location_comm location_generic_comm = {}; + + if (!location || !payload) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = -LTTNG_ERR_INVALID; + goto end; + } + + original_payload_size = payload->buffer.size; + location_generic_comm.type = (int8_t) location->type; + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_generic_comm, + sizeof(location_generic_comm)); + if (ret) { + goto end; + } + + ret = location->serialize(location, payload); + if (ret < 0) { + goto end; + } + + ret = (int) (payload->buffer.size - original_payload_size); +end: + return ret; +} + +static +int lttng_kernel_function_location_symbol_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **location) +{ + struct lttng_kernel_function_location_symbol_comm *location_symbol_comm; + const char *symbol_name_src; + ssize_t ret = 0; + size_t expected_size; + + assert(location); + + if (view->buffer.size < sizeof(*location_symbol_comm)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_symbol_comm = + (typeof(location_symbol_comm)) view->buffer.data; + + expected_size = sizeof(*location_symbol_comm) + + location_symbol_comm->symbol_len; + + if (view->buffer.size < expected_size) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + symbol_name_src = view->buffer.data + sizeof(*location_symbol_comm); + + if (!lttng_buffer_view_contains_string(&view->buffer, symbol_name_src, + location_symbol_comm->symbol_len)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + *location = lttng_kernel_function_location_symbol_create( + symbol_name_src, location_symbol_comm->offset); + if (!(*location)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (ssize_t) expected_size; +end: + return ret; +} + +static +ssize_t lttng_kernel_function_location_address_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **location) +{ + struct lttng_kernel_function_location_address_comm *location_address_comm; + ssize_t ret = 0; + size_t expected_size; + + assert(location); + + expected_size = sizeof(*location_address_comm); + + if (view->buffer.size < expected_size) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_address_comm = + (typeof(location_address_comm)) view->buffer.data; + + *location = lttng_kernel_function_location_address_create(location_address_comm->address); + if (!(*location)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (size_t) expected_size; +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_kernel_function_location_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **location) +{ + enum lttng_kernel_function_location_type type; + ssize_t consumed = 0; + ssize_t ret; + const struct lttng_kernel_function_location_comm *function_location_comm; + const struct lttng_payload_view function_location_comm_view = + lttng_payload_view_from_view( + view, 0, sizeof(*function_location_comm)); + + assert(view); + assert(location); + + if (!lttng_payload_view_is_valid(&function_location_comm_view)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + function_location_comm = (typeof(function_location_comm)) function_location_comm_view.buffer.data; + type = (enum lttng_kernel_function_location_type) function_location_comm->type; + consumed += sizeof(*function_location_comm); + + switch (type) { + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view( + view, consumed, -1); + + ret = lttng_kernel_function_location_symbol_create_from_payload( + &location_view, location); + break; + } + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view(view, consumed, -1); + + ret = lttng_kernel_function_location_address_create_from_payload( + &location_view, location); + break; + } + default: + ret = -LTTNG_ERR_INVALID; + break; + } + + if (ret < 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret += consumed; + +end: + return ret; +} + +static +unsigned long lttng_kernel_function_location_address_hash( + const struct lttng_kernel_function_location *location) +{ + unsigned long hash = hash_key_ulong( + (void *) LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS, + lttng_ht_seed); + struct lttng_kernel_function_location_address *address_location = + container_of(location, typeof(*address_location), + parent); + + hash ^= hash_key_u64(&address_location->address, lttng_ht_seed); + + return hash; +} + +static +bool lttng_kernel_function_location_address_is_equal( + const struct lttng_kernel_function_location *_a, + const struct lttng_kernel_function_location *_b) +{ + bool is_equal = false; + struct lttng_kernel_function_location_address *a, *b; + + a = container_of(_a, struct lttng_kernel_function_location_address, + parent); + b = container_of(_b, struct lttng_kernel_function_location_address, + parent); + + if (a->address != b->address) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +static +unsigned long lttng_kernel_function_location_symbol_hash( + const struct lttng_kernel_function_location *location) +{ + unsigned long hash = hash_key_ulong( + (void *) LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET, + lttng_ht_seed); + struct lttng_kernel_function_location_symbol *symbol_location = + container_of(location, typeof(*symbol_location), + parent); + + hash ^= hash_key_str(symbol_location->symbol_name, lttng_ht_seed); + hash ^= hash_key_u64(&symbol_location->offset, lttng_ht_seed); + + return hash; +} + +static +bool lttng_kernel_function_location_symbol_is_equal( + const struct lttng_kernel_function_location *_a, + const struct lttng_kernel_function_location *_b) +{ + bool is_equal = false; + struct lttng_kernel_function_location_symbol *a, *b; + + a = container_of(_a, struct lttng_kernel_function_location_symbol, + parent); + b = container_of(_b, struct lttng_kernel_function_location_symbol, + parent); + + assert(a->symbol_name); + assert(b->symbol_name); + if (strcmp(a->symbol_name, b->symbol_name)) { + goto end; + } + + if (a->offset != b->offset) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +LTTNG_HIDDEN +bool lttng_kernel_function_location_is_equal( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + if (a->type != b->type) { + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +static struct lttng_kernel_function_location * +lttng_kernel_function_location_symbol_copy( + const struct lttng_kernel_function_location *location) +{ + struct lttng_kernel_function_location *new_location = NULL; + struct lttng_kernel_function_location_symbol *symbol_location; + enum lttng_kernel_function_location_status status; + const char *symbol_name = NULL; + uint64_t offset; + + assert(location); + assert(location->type == LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET); + symbol_location = container_of( + location, typeof(*symbol_location), parent); + + /* Get function location offset */ + status = lttng_kernel_function_location_symbol_get_offset(location, &offset); + if (status != LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK) { + ERR("Get kernel function location offset failed."); + goto error; + } + + symbol_name = lttng_kernel_function_location_symbol_get_name(location); + if (!symbol_name) { + ERR("Kernel function symbol name is NULL."); + goto error; + } + + /* Create the function_location */ + new_location = lttng_kernel_function_location_symbol_create( + symbol_name, offset); + + goto end; + +error: + new_location = NULL; +end: + return new_location; +} +static struct lttng_kernel_function_location * +lttng_kernel_function_location_address_copy( + const struct lttng_kernel_function_location *location) +{ + struct lttng_kernel_function_location *new_location = NULL; + struct lttng_kernel_function_location_address *address_location; + enum lttng_kernel_function_location_status status; + uint64_t address; + + assert(location); + assert(location->type == LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS); + address_location = container_of( + location, typeof(*address_location), parent); + + + /* Get function location fields */ + status = lttng_kernel_function_location_address_get_address(location, &address); + if (status != LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK) { + ERR("Get kernel function address failed."); + goto error; + } + + /* Create the function_location */ + new_location = lttng_kernel_function_location_address_create(address); + + goto end; + +error: + new_location = NULL; +end: + return new_location; +} + +LTTNG_HIDDEN +struct lttng_kernel_function_location *lttng_kernel_function_location_copy( + const struct lttng_kernel_function_location *location) +{ + struct lttng_kernel_function_location *new_location = NULL; + enum lttng_kernel_function_location_type type; + + if (!location) { + goto err; + } + + type = lttng_kernel_function_location_get_type(location); + switch (type) { + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS: + new_location = + lttng_kernel_function_location_address_copy(location); + if (!new_location) { + goto err; + } + break; + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET: + new_location = + lttng_kernel_function_location_symbol_copy(location); + if (!new_location) { + goto err; + } + break; + default: + new_location = NULL; + goto err; + } +err: + return new_location; +} + +LTTNG_HIDDEN +unsigned long lttng_kernel_function_location_hash( + const struct lttng_kernel_function_location *location) +{ + return location->hash(location); +} diff --git a/src/common/log-level-rule.c b/src/common/log-level-rule.c new file mode 100644 index 000000000..b5d0e9dcb --- /dev/null +++ b/src/common/log-level-rule.c @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool is_log_level_rule_exactly_type(const struct lttng_log_level_rule *rule) +{ + enum lttng_log_level_rule_type type = + lttng_log_level_rule_get_type(rule); + + return type == LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY; +} + +static bool is_log_level_rule_at_least_as_severe_type(const struct lttng_log_level_rule *rule) +{ + + enum lttng_log_level_rule_type type = + lttng_log_level_rule_get_type(rule); + + return type == LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS; +} + +enum lttng_log_level_rule_type lttng_log_level_rule_get_type( + const struct lttng_log_level_rule *rule) +{ + return rule ? rule->type : LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN; +} + +struct lttng_log_level_rule *lttng_log_level_rule_exactly_create( + int level) +{ + struct lttng_log_level_rule *rule = NULL; + + rule = zmalloc(sizeof(struct lttng_log_level_rule)); + if (!rule) { + goto end; + } + + rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY; + rule->level = level; + +end: + return rule; +} + +enum lttng_log_level_rule_status lttng_log_level_rule_exactly_get_level( + const struct lttng_log_level_rule *rule, int *level) +{ + enum lttng_log_level_rule_status status = LTTNG_LOG_LEVEL_RULE_STATUS_OK; + if (!rule || !level || !is_log_level_rule_exactly_type(rule)) { + status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID; + goto end; + } + + *level = rule->level; +end: + return status; +} + +struct lttng_log_level_rule * +lttng_log_level_rule_at_least_as_severe_as_create(int level) +{ + struct lttng_log_level_rule *rule = NULL; + + rule = zmalloc(sizeof(struct lttng_log_level_rule)); + if (!rule) { + goto end; + } + + rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS; + rule->level = level; + +end: + return rule; +} + +enum lttng_log_level_rule_status +lttng_log_level_rule_at_least_as_severe_as_get_level( + const struct lttng_log_level_rule *rule, int *level) +{ + enum lttng_log_level_rule_status status = LTTNG_LOG_LEVEL_RULE_STATUS_OK; + if (!rule || !level || !is_log_level_rule_at_least_as_severe_type(rule)) { + status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID; + goto end; + } + + *level = rule->level; +end: + return status; +} + +void lttng_log_level_rule_destroy(struct lttng_log_level_rule *log_level_rule) +{ + free(log_level_rule); +} + +LTTNG_HIDDEN +ssize_t lttng_log_level_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_log_level_rule **_rule) +{ + ssize_t ret; + size_t offset = 0; + struct lttng_log_level_rule *rule = NULL; + const struct lttng_log_level_rule_comm *comm = + (const struct lttng_log_level_rule_comm *) + view->buffer.data; + + offset += sizeof(*comm); + + if (!_rule) { + ret = -1; + goto end; + } + + if (view->buffer.size < sizeof(*comm)) { + ret = -1; + goto end; + } + + switch (comm->type) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + rule = lttng_log_level_rule_exactly_create((int) comm->level); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + rule = lttng_log_level_rule_at_least_as_severe_as_create( + (int) comm->level); + break; + default: + abort(); + } + + if (!rule) { + ret = -1; + goto end; + } + + *_rule = rule; + ret = offset; + +end: + return ret; +} + +LTTNG_HIDDEN +int lttng_log_level_rule_serialize(const struct lttng_log_level_rule *rule, + struct lttng_payload *payload) +{ + int ret; + struct lttng_log_level_rule_comm comm; + + + if (!rule) { + ret = 0; + goto end; + } + + comm.type = (int8_t) rule->type; + comm.level = (int32_t) rule->level; + + DBG("Serializing log level rule of type %d", rule->type); + ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, + sizeof(comm)); + if (ret) { + goto end; + } + +end: + return ret; +} + +LTTNG_HIDDEN +bool lttng_log_level_rule_is_equal(const struct lttng_log_level_rule *a, + const struct lttng_log_level_rule *b) +{ + bool is_equal = false; + + if (a == NULL && b == NULL) { + /* Both are null. */ + is_equal = true; + goto end; + } + + if (a == NULL || b == NULL) { + /* One is NULL.*/ + goto end; + } + + if (a == b) { + /* Same object.*/ + is_equal = true; + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a->level != b->level) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +LTTNG_HIDDEN +struct lttng_log_level_rule * lttng_log_level_rule_copy(const struct lttng_log_level_rule *source) +{ + struct lttng_log_level_rule *copy = NULL; + + assert(source); + + copy = zmalloc(sizeof(struct lttng_log_level_rule)); + if (!copy) { + goto end; + } + + copy->type = source->type; + copy->level = source->level; +end: + return copy; +} + +LTTNG_HIDDEN +void lttng_log_level_rule_to_loglevel( + const struct lttng_log_level_rule *log_level_rule, + enum lttng_loglevel_type *loglevel_type, + int *loglevel_value) +{ + assert(log_level_rule); + + switch(log_level_rule->type) + { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + *loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + *loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; + break; + default: + abort(); + } + + *loglevel_value = log_level_rule->level; +} + +LTTNG_HIDDEN +unsigned long lttng_log_level_rule_hash( + const struct lttng_log_level_rule *log_level_rule) +{ + unsigned long hash; + enum lttng_log_level_rule_status llr_status; + int log_level_value; + enum lttng_log_level_rule_type type; + + assert(log_level_rule); + + type = lttng_log_level_rule_get_type(log_level_rule); + + switch (type) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &log_level_value); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &log_level_value); + break; + default: + abort(); + break; + } + + assert(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + + hash = hash_key_ulong((void *) (unsigned long) type, lttng_ht_seed); + + hash ^= hash_key_ulong((void *) (unsigned long) log_level_value, + lttng_ht_seed); + + return hash; +} diff --git a/src/common/lttng-kernel.h b/src/common/lttng-kernel.h index cd343bb76..343d68dab 100644 --- a/src/common/lttng-kernel.h +++ b/src/common/lttng-kernel.h @@ -178,18 +178,140 @@ struct lttng_kernel_event { } u; } LTTNG_PACKED; -#define LTTNG_KERNEL_EVENT_NOTIFIER_PADDING 40 +#define LTTNG_KERNEL_EVENT_NOTIFIER_PADDING 32 struct lttng_kernel_event_notifier { struct lttng_kernel_event event; + uint64_t error_counter_idx; + char padding[LTTNG_KERNEL_EVENT_NOTIFIER_PADDING]; } LTTNG_PACKED; -#define LTTNG_KERNEL_EVENT_NOTIFIER_NOTIFICATION_PADDING 34 +enum lttng_kernel_key_token_type { + LTTNG_KERNEL_KEY_TOKEN_STRING = 0, /* arg: strtab_offset. */ + LTTNG_KERNEL_KEY_TOKEN_EVENT_NAME = 1, /* no arg. */ +}; + +#define LTTNG_KERNEL_KEY_ARG_PADDING1 60 +#define LTTNG_KERNEL_KEY_TOKEN_STRING_LEN_MAX 256 +struct lttng_kernel_key_token { + uint32_t type; /* enum lttng_kernel_key_token_type */ + union { + uint64_t string_ptr; + char padding[LTTNG_KERNEL_KEY_ARG_PADDING1]; + } arg; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_NR_KEY_TOKEN 4 +struct lttng_kernel_counter_key_dimension { + uint32_t nr_key_tokens; + struct lttng_kernel_key_token key_tokens[LTTNG_KERNEL_NR_KEY_TOKEN]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_DIMENSION_MAX 4 +struct lttng_kernel_counter_key { + uint32_t nr_dimensions; + struct lttng_kernel_counter_key_dimension key_dimensions[LTTNG_KERNEL_COUNTER_DIMENSION_MAX]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_EVENT_PADDING1 16 +struct lttng_kernel_counter_event { + struct lttng_kernel_event event; + struct lttng_kernel_counter_key key; + char padding[LTTNG_KERNEL_COUNTER_EVENT_PADDING1]; +} LTTNG_PACKED; + +enum lttng_kernel_counter_arithmetic { + LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR = 0, +}; + +enum lttng_kernel_counter_bitness { + LTTNG_KERNEL_COUNTER_BITNESS_32 = 0, + LTTNG_KERNEL_COUNTER_BITNESS_64 = 1, +}; + +struct lttng_kernel_counter_dimension { + uint64_t size; + uint64_t underflow_index; + uint64_t overflow_index; + uint8_t has_underflow; + uint8_t has_overflow; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_CONF_PADDING1 67 +struct lttng_kernel_counter_conf { + uint32_t arithmetic; /* enum lttng_kernel_counter_arithmetic */ + uint32_t bitness; /* enum lttng_kernel_counter_bitness */ + uint32_t number_dimensions; + int64_t global_sum_step; + struct lttng_kernel_counter_dimension dimensions[LTTNG_KERNEL_COUNTER_DIMENSION_MAX]; + uint8_t coalesce_hits; + char padding[LTTNG_KERNEL_COUNTER_CONF_PADDING1]; +} LTTNG_PACKED; + +struct lttng_kernel_counter_index { + uint32_t number_dimensions; + uint64_t dimension_indexes[LTTNG_KERNEL_COUNTER_DIMENSION_MAX]; +} LTTNG_PACKED; + +struct lttng_kernel_counter_value { + int64_t value; + uint8_t underflow; + uint8_t overflow; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_READ_PADDING 32 +struct lttng_kernel_counter_read { + struct lttng_kernel_counter_index index; + int32_t cpu; /* -1 for global counter, >= 0 for specific cpu. */ + struct lttng_kernel_counter_value value; /* output */ + char padding[LTTNG_KERNEL_COUNTER_READ_PADDING]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_AGGREGATE_PADDING 32 +struct lttng_kernel_counter_aggregate { + struct lttng_kernel_counter_index index; + struct lttng_kernel_counter_value value; /* output */ + char padding[LTTNG_KERNEL_COUNTER_AGGREGATE_PADDING]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_CLEAR_PADDING 32 +struct lttng_kernel_counter_clear { + struct lttng_kernel_counter_index index; + char padding[LTTNG_KERNEL_COUNTER_CLEAR_PADDING]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_MAP_NR_DESCRIPTORS_PADDING 32 +struct lttng_kernel_counter_map_nr_descriptors { + uint64_t nr_descriptors; +}; + +#define LTTNG_KERNEL_COUNTER_KEY_LEN 256 +#define LTTNG_KERNEL_COUNTER_MAP_DESCRIPTOR_PADDING 32 +struct lttng_kernel_counter_map_descriptor { + uint64_t descriptor_index; /* input. [ 0 .. nr_descriptors - 1 ] */ + uint32_t dimension; /* outputs */ + uint64_t array_index; + uint64_t user_token; + char key[LTTNG_KERNEL_COUNTER_KEY_LEN]; + + char padding[LTTNG_KERNEL_COUNTER_MAP_DESCRIPTOR_PADDING]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_EVENT_NOTIFIER_NOTIFICATION_PADDING 32 struct lttng_kernel_event_notifier_notification { uint64_t token; + uint16_t capture_buf_size; char padding[LTTNG_KERNEL_EVENT_NOTIFIER_NOTIFICATION_PADDING]; } LTTNG_PACKED; +#define LTTNG_KERNEL_CAPTURE_BYTECODE_MAX_LEN 65536 +struct lttng_kernel_capture_bytecode { + uint32_t len; + uint32_t reloc_offset; + uint64_t seqnum; + char data[0]; +} LTTNG_PACKED; + struct lttng_kernel_tracer_version { uint32_t major; uint32_t minor; diff --git a/src/common/map-key/map-key.c b/src/common/map-key/map-key.c new file mode 100644 index 000000000..1cce2b121 --- /dev/null +++ b/src/common/map-key/map-key.c @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define TOKEN_VAR_EVENT_NAME "EVENT_NAME" +#define TOKEN_VAR_PROVIDER_NAME "PROVIDER_NAME" + +static +void destroy_map_key_token(void *ptr) +{ + struct lttng_map_key_token *token = (struct lttng_map_key_token *) ptr; + switch (token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + struct lttng_map_key_token_string *token_string = + (struct lttng_map_key_token_string *) token; + free(token_string->string); + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + break; + default: + abort(); + } + + free(token); +} + +struct lttng_map_key *lttng_map_key_create(void) +{ + struct lttng_map_key *key = NULL; + + key = zmalloc(sizeof(*key)); + if (!key) { + return NULL; + } + + urcu_ref_init(&key->ref); + lttng_dynamic_pointer_array_init(&key->tokens, + destroy_map_key_token); + + return key; +} + +ssize_t lttng_map_key_create_from_payload(struct lttng_payload_view *src_view, + struct lttng_map_key **out_key) +{ + ssize_t ret, consumed_len; + uint32_t i; + const struct lttng_map_key_comm *comm; + struct lttng_map_key *key; + + if (!src_view || !out_key) { + ret = -1; + goto end; + } + + key = lttng_map_key_create(); + if (!key) { + ret = -1; + goto end; + } + + comm = (typeof(comm)) src_view->buffer.data; + consumed_len = sizeof(*comm); + + assert(comm->token_count > 0); + + for (i = 0; i < comm->token_count; i++) { + enum lttng_map_key_status key_status; + const struct lttng_map_key_token_comm *token_comm; + struct lttng_payload_view child_view = + lttng_payload_view_from_view(src_view, consumed_len, + src_view->buffer.size - consumed_len); + if (!lttng_payload_view_is_valid(&child_view)) { + ret = -1; + goto end; + } + + token_comm = (const struct lttng_map_key_token_comm *) child_view.buffer.data; + + switch (token_comm->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + const char *str_val; + const struct lttng_map_key_token_string_comm *comm; + + comm = (typeof(comm)) token_comm; + str_val = (const char *) &comm->payload; + + if (!lttng_buffer_view_contains_string(&child_view.buffer, + str_val, comm->string_len)) { + ret = -1; + goto end; + } + + key_status = lttng_map_key_append_token_string(key, str_val); + if (key_status != LTTNG_MAP_KEY_STATUS_OK) { + ret = -1; + goto end; + } + + consumed_len += sizeof(*comm) + comm->string_len; + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + const struct lttng_map_key_token_variable_comm *comm; + + comm = (typeof(comm)) token_comm; + key_status = lttng_map_key_append_token_variable(key, + comm->var_type); + if (key_status != LTTNG_MAP_KEY_STATUS_OK) { + ret = -1; + goto end; + } + + consumed_len += sizeof(*comm); + break; + } + default: + abort(); + } + } + + *out_key = key; + ret = consumed_len; +end: + return ret; +} + +int lttng_map_key_serialize(const struct lttng_map_key *key, + struct lttng_payload *payload) +{ + int ret; + uint32_t i, nb_tokens; + enum lttng_map_key_status key_status; + struct lttng_map_key_comm comm = {0}; + + DBG("Serializing map key"); + + key_status = lttng_map_key_get_token_count(key, &nb_tokens); + if (key_status != LTTNG_MAP_KEY_STATUS_OK) { + ret = -1; + goto end; + } + + if (nb_tokens == 0) { + ERR("Map key token number is zero"); + ret = -1; + goto end; + } + + DBG("Serializing map key token count: %" PRIu32, nb_tokens); + comm.token_count = nb_tokens; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, + sizeof(comm)); + if (ret) { + goto end; + } + + for (i = 0; i < nb_tokens; i++) { + uint8_t token_type; + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + DBG("Serializing map key token's type: %d", token->type); + + token_type = (uint8_t) token->type; + + switch (token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + struct lttng_map_key_token_string *str_token = + (struct lttng_map_key_token_string *) token; + struct lttng_map_key_token_string_comm comm = {0}; + uint32_t len = strlen(str_token->string) + 1; + + DBG("Serializing a string type key token"); + comm.parent_type = token_type; + comm.string_len = len; + + /* Serialize the length, include the null character */ + DBG("Serializing map key token string length (include null character):" + "%" PRIu32, len); + ret = lttng_dynamic_buffer_append(&payload->buffer, + &comm, sizeof(comm)); + if (ret) { + goto end; + } + + /* Serialize the string */ + DBG("Serializing map key token string's value: \"%s\"", + str_token->string); + ret = lttng_dynamic_buffer_append(&payload->buffer, + str_token->string, len); + if (ret) { + goto end; + } + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + struct lttng_map_key_token_variable *var_token = + (struct lttng_map_key_token_variable *) token; + struct lttng_map_key_token_variable_comm comm = {0}; + + DBG("Serializing a variable type key token"); + + comm.parent_type = token_type; + comm.var_type = var_token->type; + + ret = lttng_dynamic_buffer_append(&payload->buffer, + &comm, sizeof(comm)); + if (ret) { + goto end; + } + + break; + } + default: + abort(); + } + } + +end: + return ret; +} + +static inline +bool token_variable_type_is_valid(enum lttng_map_key_token_variable_type var_type) +{ + switch (var_type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + return true; + default: + return false; + } +} + +static bool lttng_map_key_token_variable_is_equal( + const struct lttng_map_key_token *_a, + const struct lttng_map_key_token *_b) +{ + struct lttng_map_key_token_variable *a, *b; + assert(_a->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE); + assert(_b->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE); + + a = container_of(_a, struct lttng_map_key_token_variable, parent); + b = container_of(_b, struct lttng_map_key_token_variable, parent); + + return lttng_map_key_token_variable_get_type(a) == + lttng_map_key_token_variable_get_type(b); +} + +enum lttng_map_key_status lttng_map_key_append_token_variable( + struct lttng_map_key *key, + enum lttng_map_key_token_variable_type var_type) +{ + int ret; + enum lttng_map_key_status status; + struct lttng_map_key_token_variable *token = NULL; + + if (!token_variable_type_is_valid(var_type)) { + ERR("Invalid token variable type"); + status = LTTNG_MAP_KEY_STATUS_INVALID; + goto end; + } + + token = zmalloc(sizeof(*token)); + if (!token) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + token->parent.type = LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE; + token->parent.equal = lttng_map_key_token_variable_is_equal; + token->type = var_type; + + ret = lttng_dynamic_pointer_array_add_pointer( + &key->tokens, token); + if (ret) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_KEY_STATUS_OK; + +end: + return status; +} + +static bool lttng_map_key_token_string_is_equal( + const struct lttng_map_key_token *_a, + const struct lttng_map_key_token *_b) +{ + struct lttng_map_key_token_string *a, *b; + const char *a_string, *b_string; + + assert(_a->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING); + assert(_b->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING); + + a = container_of(_a, struct lttng_map_key_token_string, parent); + b = container_of(_b, struct lttng_map_key_token_string, parent); + + a_string = lttng_map_key_token_string_get_string(a); + b_string = lttng_map_key_token_string_get_string(b); + + return !strcmp(a_string, b_string); +} + +enum lttng_map_key_status lttng_map_key_append_token_string( + struct lttng_map_key *key, const char *string) +{ + int ret; + enum lttng_map_key_status status; + struct lttng_map_key_token_string *token = NULL; + + + token = zmalloc(sizeof(*token)); + if (!token) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + token->parent.type = LTTNG_MAP_KEY_TOKEN_TYPE_STRING; + token->parent.equal = lttng_map_key_token_string_is_equal; + token->string = strdup(string); + if (!token->string) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &key->tokens, token); + if (ret) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_KEY_STATUS_OK; +end: + return status; +} + +enum lttng_map_key_status lttng_map_key_get_token_count( + const struct lttng_map_key *key, unsigned int *count) +{ + enum lttng_map_key_status status; + if (!key || !count) { + status = LTTNG_MAP_KEY_STATUS_INVALID; + goto end; + + } + + *count = lttng_dynamic_pointer_array_get_count( + &key->tokens); + + status = LTTNG_MAP_KEY_STATUS_OK; +end: + return status; +} + +const struct lttng_map_key_token * +lttng_map_key_get_token_at_index(const struct lttng_map_key *key, + unsigned int index) +{ + const struct lttng_map_key_token *token = NULL; + enum lttng_map_key_status status; + unsigned int count; + + if (!key) { + goto end; + } + + status = lttng_map_key_get_token_count(key, &count); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + goto end; + } + + if (index >= count) { + goto end; + } + + token = lttng_dynamic_pointer_array_get_pointer(&key->tokens, + index); + +end: + return token; +} + +enum lttng_map_key_token_variable_type lttng_map_key_token_variable_get_type( + const struct lttng_map_key_token_variable *token) +{ + assert(token->parent.type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE); + + return token->type; +} + +const char *lttng_map_key_token_string_get_string( + const struct lttng_map_key_token_string *token) +{ + assert(token->parent.type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING); + + return token->string; +} + +void lttng_map_key_destroy(struct lttng_map_key *key) +{ + lttng_map_key_put(key); +} + +LTTNG_HIDDEN +struct lttng_map_key *lttng_map_key_parse_from_string(const char *_key_str) +{ + struct lttng_map_key *key = NULL; + enum lttng_map_key_status status; + char *key_str = NULL, *curr_pos; + + key = lttng_map_key_create(); + if (!key) { + goto end; + } + + key_str = strdup(_key_str); + + curr_pos = key_str; + + curr_pos = strtok(curr_pos, "$}"); + while (curr_pos) { + if (curr_pos[0] == '{') { + if (strncmp(&curr_pos[1], TOKEN_VAR_EVENT_NAME, strlen(TOKEN_VAR_EVENT_NAME)) == 0) { + status = lttng_map_key_append_token_variable(key, + LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + goto error; + } + } else if (strncmp(&curr_pos[1], TOKEN_VAR_PROVIDER_NAME, strlen(TOKEN_VAR_PROVIDER_NAME)) == 0) { + status = lttng_map_key_append_token_variable(key, + LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + goto error; + } + } else { + goto error; + } + } else { + status = lttng_map_key_append_token_string(key, curr_pos); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + goto error; + } + } + curr_pos = strtok(NULL, "$}"); + } + goto end; + +error: + lttng_map_key_destroy(key); + key = NULL; +end: + free(key_str); + + return key; +} + +LTTNG_HIDDEN +bool lttng_map_key_is_equal( + const struct lttng_map_key *a, const struct lttng_map_key *b) +{ + bool is_equal = false; + enum lttng_map_key_status status; + unsigned int a_count, b_count, i; + + if (!!a != !!b) { + goto end; + } + + if (a == NULL && b == NULL) { + is_equal = true; + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + status = lttng_map_key_get_token_count(a, &a_count); + assert(status == LTTNG_MAP_KEY_STATUS_OK); + status = lttng_map_key_get_token_count(b, &b_count); + assert(status == LTTNG_MAP_KEY_STATUS_OK); + + if (a_count != b_count) { + goto end; + } + + for (i = 0; i < a_count; i++) { + const struct lttng_map_key_token *token_a = NULL, *token_b = NULL; + + token_a = lttng_map_key_get_token_at_index(a, i); + token_b = lttng_map_key_get_token_at_index(b, i); + + /* JORAJ TODO: is order important for the map key token? */ + if(token_a->type != token_b->type) { + goto end; + } + + if(!token_a->equal(token_a, token_b)) { + goto end; + } + } + + is_equal = true; + +end: + return is_equal; + + +} + +static void map_key_destroy_ref(struct urcu_ref *ref) +{ + struct lttng_map_key *key = container_of(ref, struct lttng_map_key, ref); + + lttng_dynamic_pointer_array_reset(&key->tokens); + free(key); +} + +LTTNG_HIDDEN +void lttng_map_key_get(struct lttng_map_key *key) +{ + urcu_ref_get(&key->ref); +} + +LTTNG_HIDDEN +void lttng_map_key_put(struct lttng_map_key *key) +{ + if (!key) { + return; + } + + urcu_ref_put(&key->ref, map_key_destroy_ref); +} diff --git a/src/common/map-query.c b/src/common/map-query.c new file mode 100644 index 000000000..f25001f88 --- /dev/null +++ b/src/common/map-query.c @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +struct lttng_map_query *lttng_map_query_create( + enum lttng_map_query_config_cpu cpu, + enum lttng_map_query_config_buffer buffer, + enum lttng_map_query_config_app_bitness bitness) +{ + struct lttng_map_query *query = NULL; + + if ((buffer == LTTNG_MAP_QUERY_CONFIG_BUFFER_KERNEL_GLOBAL) ^ + (bitness == LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_KERNEL)) { + /* + * If any of the buffer or bitness config is set to kernel, + * they other has to as well. + */ + goto end; + } + + if (bitness != LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL && + bitness != LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_KERNEL) { + /* We currently don't support targetting a specific bitness. */ + goto end; + } + + query = zmalloc(sizeof(struct lttng_map_query)); + if (!query) { + goto end; + } + + query->config_cpu = cpu; + query->config_buffer = buffer; + query->config_bitness = bitness; + + query->sum_by_uid = false; + query->sum_by_pid = false; + query->sum_by_cpu = false; + // defaults to true for now. + query->sum_by_app_bitness = true; + + if (query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + lttng_dynamic_array_init(&query->cpu_array, sizeof(int), NULL); + } + + switch(query->config_buffer) { + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + lttng_dynamic_array_init(&query->uid_array, sizeof(uid_t), NULL); + break; + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + lttng_dynamic_array_init(&query->pid_array, sizeof(pid_t), NULL); + break; + default: + break; + } +end: + return query; +} + +enum lttng_map_query_status lttng_map_query_set_sum_by_cpu( + struct lttng_map_query *query, bool sum_by_cpu) +{ + query->sum_by_cpu = sum_by_cpu; + + return LTTNG_MAP_QUERY_STATUS_OK; +} + +enum lttng_map_query_status lttng_map_query_set_sum_by_app_bitness( + struct lttng_map_query *query, bool sum_by_app_bitness) +{ + enum lttng_map_query_status status; + + if (query->config_bitness != LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + query->sum_by_app_bitness = sum_by_app_bitness; + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_set_sum_by_pid( + struct lttng_map_query *query, bool sum_by_pid) +{ + enum lttng_map_query_status status; + + switch (query->config_buffer) { + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL: + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + break; + default: + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + query->sum_by_pid = sum_by_pid; + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_set_sum_by_uid( + struct lttng_map_query *query, bool sum_by_uid) +{ + enum lttng_map_query_status status; + + switch (query->config_buffer) { + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL: + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + break; + default: + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + query->sum_by_uid = sum_by_uid; + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_add_cpu( + struct lttng_map_query *query, int cpu_id) +{ + enum lttng_map_query_status status; + unsigned int cpu_count; + int ret; + + if (query->config_cpu != LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + lttng_map_query_get_cpu_count(query, &cpu_count); + if (cpu_count > 0) { + ERR("Only one CPU can be targeted in a query at the moment"); + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + ret = lttng_dynamic_array_add_element(&query->cpu_array, &cpu_id); + if (ret) { + status = LTTNG_MAP_QUERY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_add_uid( + struct lttng_map_query *query, uid_t uid) +{ + int ret; + unsigned int uid_count; + enum lttng_map_query_status status; + + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + lttng_map_query_get_uid_count(query, &uid_count); + if (uid_count > 0) { + ERR("Only one UID can be targeted in a query at the moment"); + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + ret = lttng_dynamic_array_add_element(&query->uid_array, &uid); + if (ret) { + status = LTTNG_MAP_QUERY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_add_pid( + struct lttng_map_query *query, pid_t pid) +{ + int ret; + unsigned int pid_count; + enum lttng_map_query_status status; + + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + lttng_map_query_get_pid_count(query, &pid_count); + if (pid_count > 0) { + ERR("Only one PID can be targeted in a query at the moment"); + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + ret = lttng_dynamic_array_add_element(&query->pid_array, &pid); + if (ret) { + status = LTTNG_MAP_QUERY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_add_key_filter( + struct lttng_map_query *query, const char *key_filter) +{ + enum lttng_map_query_status status; + + query->key_filter = strdup(key_filter); + if (!query->key_filter) { + status = LTTNG_MAP_QUERY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_config_cpu lttng_map_query_get_config_cpu( + const struct lttng_map_query *query) +{ + return query->config_cpu; +} + +enum lttng_map_query_config_buffer lttng_map_query_get_config_buffer( + const struct lttng_map_query *query) +{ + return query->config_buffer; +} + +enum lttng_map_query_config_app_bitness lttng_map_query_get_config_app_bitness( + const struct lttng_map_query *query) +{ + return query->config_bitness; +} + +bool lttng_map_query_get_config_sum_by_cpu( + const struct lttng_map_query *query) +{ + return query->sum_by_cpu; +} + +bool lttng_map_query_get_config_sum_by_pid( + const struct lttng_map_query *query) +{ + return query->sum_by_pid; +} + +bool lttng_map_query_get_config_sum_by_uid( + const struct lttng_map_query *query) +{ + return query->sum_by_uid; +} + +bool lttng_map_query_get_config_sum_by_app_bitness( + const struct lttng_map_query *query) +{ + return query->sum_by_app_bitness; +} + +enum lttng_map_query_status lttng_map_query_get_cpu_count( + const struct lttng_map_query *query, unsigned int *count) +{ + enum lttng_map_query_status status; + + assert(query); + if (query->config_cpu != LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + if (!count) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_array_get_count(&query->cpu_array); + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_cpu_at_index( + const struct lttng_map_query *query, unsigned int index, + int *cpu) +{ + enum lttng_map_query_status status; + + assert(query); + + if (query->config_cpu != LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + + *cpu = *(int *) lttng_dynamic_array_get_element(&query->cpu_array, index); + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_uid_count( + const struct lttng_map_query *query, unsigned int *count) +{ + enum lttng_map_query_status status; + + assert(query); + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + if (!count) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_array_get_count(&query->uid_array); + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_uid_at_index( + const struct lttng_map_query *query, unsigned int index, + uid_t *uid) +{ + enum lttng_map_query_status status; + + assert(query); + + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + + *uid = *(uid_t *) lttng_dynamic_array_get_element(&query->uid_array, index); + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_pid_count( + const struct lttng_map_query *query, unsigned int *count) +{ + enum lttng_map_query_status status; + + assert(query); + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + if (!count) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_array_get_count(&query->pid_array); + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_pid_at_index( + const struct lttng_map_query *query, unsigned int index, + pid_t *pid) +{ + enum lttng_map_query_status status; + + assert(query); + + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + + *pid = *(pid_t *) lttng_dynamic_array_get_element(&query->pid_array, index); + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + + +enum lttng_map_query_status lttng_map_query_get_key_filter( + const struct lttng_map_query *query, const char **key_filter) +{ + enum lttng_map_query_status status; + + if (query->key_filter == NULL) { + status = LTTNG_MAP_QUERY_STATUS_NONE; + goto end; + } + + *key_filter = query->key_filter; + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +LTTNG_HIDDEN +ssize_t lttng_map_query_create_from_payload(struct lttng_payload_view *src_view, + struct lttng_map_query **query) +{ + ssize_t ret, offset = 0; + struct lttng_map_query *local_query; + const struct lttng_map_query_comm *query_comm; + + if (!src_view || !query) { + ret = -1; + goto end; + } + + query_comm = (typeof(query_comm)) src_view->buffer.data; + offset += sizeof(*query_comm); + + local_query = lttng_map_query_create(query_comm->config_cpu, + query_comm->config_buffer, query_comm->config_app_bitness); + if (!local_query) { + ret = -1; + goto end; + } + + local_query->sum_by_cpu = query_comm->sum_by_cpu; + local_query->sum_by_pid = query_comm->sum_by_pid; + local_query->sum_by_uid = query_comm->sum_by_uid; + local_query->sum_by_app_bitness = query_comm->sum_by_app_bitness; + + if (query_comm->key_filter_length != 0) { + const char *key_filter; + struct lttng_payload_view key_filter_view = + lttng_payload_view_from_view( + src_view, offset, + query_comm->key_filter_length); + + key_filter = key_filter_view.buffer.data; + if (!lttng_buffer_view_contains_string(&key_filter_view.buffer, + key_filter, query_comm->key_filter_length)){ + ret = -1; + goto end; + } + + lttng_map_query_add_key_filter(local_query, key_filter); + + offset += query_comm->key_filter_length; + } + + if (local_query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int cpu_idx; + + assert(query_comm->cpu_count > 0); + + for (cpu_idx = 0; cpu_idx < query_comm->cpu_count; cpu_idx++) { + int cpu_id; + struct lttng_payload_view cpu_id_view = + lttng_payload_view_from_view( src_view, + offset, sizeof(cpu_id)); + cpu_id = *(int *) cpu_id_view.buffer.data; + lttng_map_query_add_cpu(local_query, cpu_id); + offset+=sizeof(cpu_id); + } + } + + switch (local_query->config_buffer){ + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + { + unsigned int pid_idx; + + assert(query_comm->pid_count > 0); + + for (pid_idx = 0; pid_idx < query_comm->pid_count; pid_idx++) { + pid_t pid; + struct lttng_payload_view pid_view = + lttng_payload_view_from_view( src_view, + offset, sizeof(pid)); + pid = *(pid_t *) pid_view.buffer.data; + lttng_map_query_add_pid(local_query, pid); + offset+=sizeof(pid); + } + break; + } + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + { + unsigned int uid_idx; + + assert(query_comm->uid_count > 0); + + for (uid_idx = 0; uid_idx < query_comm->uid_count; uid_idx++) { + uid_t uid; + struct lttng_payload_view uid_view = + lttng_payload_view_from_view( src_view, + offset, sizeof(uid)); + uid = *(uid_t *) uid_view.buffer.data; + lttng_map_query_add_uid(local_query, uid); + offset+=sizeof(uid); + } + } + default: + break; + } + + ret = offset; + *query = local_query; + local_query = NULL; +end: + return ret; +} + +LTTNG_HIDDEN +int lttng_map_query_serialize(const struct lttng_map_query *query, + struct lttng_payload *payload) +{ + int ret; + struct lttng_map_query_comm query_comm = {}; + enum lttng_map_query_status status; + + query_comm.config_cpu = (uint8_t) query->config_cpu; + query_comm.config_buffer = (uint8_t) query->config_buffer; + query_comm.config_app_bitness = (uint8_t) query->config_bitness; + + query_comm.sum_by_cpu = (uint8_t) query->sum_by_cpu; + query_comm.sum_by_uid = (uint8_t) query->sum_by_uid; + query_comm.sum_by_pid = (uint8_t) query->sum_by_pid; + query_comm.sum_by_app_bitness = (uint8_t) query->sum_by_app_bitness; + + if (query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int cpu_count; + status = lttng_map_query_get_cpu_count(query, &cpu_count); + if (status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + + query_comm.cpu_count = cpu_count; + } + + switch (query->config_buffer){ + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + { + unsigned int pid_count; + status = lttng_map_query_get_pid_count(query, &pid_count); + if (status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + query_comm.pid_count = pid_count; + break; + } + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + { + unsigned int uid_count; + status = lttng_map_query_get_uid_count(query, &uid_count); + if (status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + query_comm.uid_count = uid_count; + } + default: + break; + } + + if (query->key_filter) { + query_comm.key_filter_length = strlen(query->key_filter) + 1; + } else { + query_comm.key_filter_length = 0; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, &query_comm, + sizeof(query_comm)); + if (ret) { + goto end; + } + + /* key_filter */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, query->key_filter, + query_comm.key_filter_length); + if (ret) { + goto end; + } + + if (query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + ret = lttng_dynamic_buffer_append(&payload->buffer, + query->cpu_array.buffer.data, + query->cpu_array.buffer.size); + if (ret) { + goto end; + } + } + + switch (query->config_buffer){ + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + { + ret = lttng_dynamic_buffer_append(&payload->buffer, + query->pid_array.buffer.data, + query->pid_array.buffer.size); + if (ret) { + goto end; + } + } + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + { + ret = lttng_dynamic_buffer_append(&payload->buffer, + query->uid_array.buffer.data, + query->uid_array.buffer.size); + if (ret) { + goto end; + } + } + default: + break; + } + +end: + return ret; +} + +void lttng_map_query_destroy(struct lttng_map_query *query) +{ + assert(query); + + if (query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + lttng_dynamic_array_reset(&query->cpu_array); + } + + switch(query->config_buffer) { + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + lttng_dynamic_array_reset(&query->uid_array); + break; + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + lttng_dynamic_array_reset(&query->pid_array); + break; + default: + break; + } + free(query->key_filter); + free(query); +} diff --git a/src/common/map.c b/src/common/map.c new file mode 100644 index 000000000..c4de9a66b --- /dev/null +++ b/src/common/map.c @@ -0,0 +1,1239 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +enum lttng_map_status lttng_map_set_name(struct lttng_map *map, + const char *name) +{ + char *name_copy = NULL; + enum lttng_map_status status; + + if (!map || !name || strlen(name) == 0) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + + free(map->name); + + map->name = name_copy; + name_copy = NULL; + + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +enum lttng_map_status lttng_map_get_name(const struct lttng_map *map, + const char **name) +{ + enum lttng_map_status status; + + if (!map || !name) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + if (!map->name) { + status = LTTNG_MAP_STATUS_UNSET; + } + + *name = map->name; + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +enum lttng_map_status lttng_map_create(const char *name, + unsigned int dimension_count, + uint64_t *dimension_sizes, + enum lttng_domain_type domain, + enum lttng_buffer_type buffer_type, + enum lttng_map_bitness bitness, + enum lttng_map_boundary_policy boundary_policy, + bool coalesce_hits, + struct lttng_map **map_out) +{ + enum lttng_map_status status; + struct lttng_map *map; + + if (dimension_count != 1) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + map = zmalloc(sizeof(struct lttng_map)); + if (!map) { + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + + if (name) { + status = lttng_map_set_name(map, name); + if (status != LTTNG_MAP_STATUS_OK) { + goto free_map; + } + } else { + map->name = NULL; + } + + map->dimension_count = dimension_count; + map->dimension_sizes = zmalloc( + sizeof(*map->dimension_sizes) * dimension_count); + if (!map->dimension_sizes) { + status = LTTNG_MAP_STATUS_ERROR; + goto free_name; + } + + memcpy(map->dimension_sizes, dimension_sizes, + sizeof(*map->dimension_sizes) * dimension_count); + + map->domain = domain; + map->buffer_type = buffer_type; + map->bitness = bitness; + map->boundary_policy = boundary_policy; + map->coalesce_hits = coalesce_hits; + + lttng_map_set_is_enabled(map, true); + + urcu_ref_init(&map->ref); + + *map_out = map; + + status = LTTNG_MAP_STATUS_OK; + + goto end; +free_name: + free(map->name); +free_map: + free(map); +end: + return status; +} + +unsigned int lttng_map_get_dimension_count( + const struct lttng_map *map) +{ + assert(map); + + return map->dimension_count; +} + +enum lttng_map_status lttng_map_get_dimension_length( + const struct lttng_map *map, unsigned int dimension, + uint64_t *dimension_length) +{ + enum lttng_map_status status; + + assert(map); + + if (dimension >= map->dimension_count) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + *dimension_length = map->dimension_sizes[dimension]; + + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +enum lttng_map_bitness lttng_map_get_bitness( + const struct lttng_map *map) +{ + assert(map); + + return map->bitness; +} + +enum lttng_domain_type lttng_map_get_domain( + const struct lttng_map *map) +{ + assert(map); + + return map->domain; +} + +enum lttng_buffer_type lttng_map_get_buffer_type( + const struct lttng_map *map) +{ + assert(map); + + return map->buffer_type; +} + +enum lttng_map_boundary_policy lttng_map_get_boundary_policy( + const struct lttng_map *map) +{ + assert(map); + + return map->boundary_policy; +} + +bool lttng_map_get_coalesce_hits(const struct lttng_map *map) +{ + assert(map); + + return map->coalesce_hits; +} + +LTTNG_HIDDEN +int lttng_map_serialize(const struct lttng_map *map, + struct lttng_payload *payload) +{ + int ret; + size_t header_offset, size_before_payload, size_name; + struct lttng_map_comm map_comm = {}; + struct lttng_map_comm *header; + + if (map->name != NULL) { + size_name = strlen(map->name) + 1; + } else { + size_name = 0; + } + + map_comm.name_length = size_name; + map_comm.is_enabled = LTTNG_OPTIONAL_GET(map->is_enabled); + map_comm.bitness = map->bitness; + map_comm.domain = map->domain; + map_comm.buffer_type = map->buffer_type; + map_comm.boundary_policy = map->boundary_policy; + map_comm.dimension_count = map->dimension_count; + map_comm.coalesce_hits = map->coalesce_hits; + + header_offset = payload->buffer.size; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &map_comm, + sizeof(map_comm)); + if (ret) { + goto end; + } + + size_before_payload = payload->buffer.size; + + /* map name */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, map->name, size_name); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append( + &payload->buffer, map->dimension_sizes, + sizeof(*map->dimension_sizes) * map->dimension_count); + if (ret) { + goto end; + } + + /* Update payload size. */ + header = (typeof(header)) (payload->buffer.data + header_offset); + header->length = payload->buffer.size - size_before_payload; + +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_map_create_from_payload( + struct lttng_payload_view *src_view, + struct lttng_map **map) +{ + ssize_t ret, offset = 0; + const struct lttng_map_comm *map_comm; + enum lttng_map_status status; + unsigned int dimension_count; + uint64_t *dimension_sizes = NULL; + bool coalesce_hits; + const char *name = NULL; + enum lttng_domain_type domain; + enum lttng_buffer_type buffer_type; + enum lttng_map_bitness bitness; + enum lttng_map_boundary_policy boundary_policy; + + if (!src_view || !map) { + ret = -1; + goto end; + } + + map_comm = (typeof(map_comm)) src_view->buffer.data; + offset += sizeof(*map_comm); + + domain = map_comm->domain; + buffer_type = map_comm->buffer_type; + bitness = map_comm->bitness; + boundary_policy = map_comm->boundary_policy; + dimension_count = map_comm->dimension_count; + coalesce_hits = map_comm->coalesce_hits; + + if (map_comm->name_length != 0) { + struct lttng_payload_view name_view = + lttng_payload_view_from_view( + src_view, offset, + map_comm->name_length); + + name = name_view.buffer.data; + if (!lttng_buffer_view_contains_string(&name_view.buffer, + name, map_comm->name_length)){ + ret = -1; + goto end; + } + offset += map_comm->name_length; + } + + size_t map_dim_sizes_len = sizeof(*(*map)->dimension_sizes) * dimension_count; + + struct lttng_payload_view dimension_sizes_view = + lttng_payload_view_from_view(src_view, offset, + map_dim_sizes_len); + + dimension_sizes = zmalloc(map_dim_sizes_len); + if (!dimension_sizes) { + ret = -1; + goto end; + } + + memcpy(dimension_sizes, dimension_sizes_view.buffer.data, + map_dim_sizes_len); + + offset += map_dim_sizes_len; + + status = lttng_map_create(name, dimension_count, + dimension_sizes, domain, buffer_type, bitness, + boundary_policy, coalesce_hits, map); + if (status != LTTNG_MAP_STATUS_OK) { + ret = -1; + goto end; + } + + lttng_map_set_is_enabled(*map, map_comm->is_enabled); + + ret = offset; + +end: + free(dimension_sizes); + return ret; +} + +LTTNG_HIDDEN +void lttng_map_set_is_enabled(struct lttng_map *map, bool enabled) +{ + assert(map); + + LTTNG_OPTIONAL_SET(&map->is_enabled, enabled); +} + +int lttng_map_get_is_enabled(const struct lttng_map *map) +{ + assert(map); + return (int) LTTNG_OPTIONAL_GET(map->is_enabled); +} + +LTTNG_HIDDEN +void lttng_map_get(struct lttng_map *map) +{ + urcu_ref_get(&map->ref); +} + +static void map_destroy_ref(struct urcu_ref *ref) +{ + struct lttng_map *map = container_of(ref, struct lttng_map, ref); + + free(map->dimension_sizes); + free(map->name); + free(map); + +} + +LTTNG_HIDDEN +void lttng_map_put(struct lttng_map *map) +{ + if (!map) { + return; + } + + urcu_ref_put(&map->ref , map_destroy_ref); +} + + +void lttng_map_destroy(struct lttng_map *map) +{ + lttng_map_put(map); +} + +static void delete_map_array_element(void *ptr) +{ + struct lttng_map *map = ptr; + + lttng_map_put(map); +} + +LTTNG_HIDDEN +struct lttng_map_list *lttng_map_list_create(void) +{ + struct lttng_map_list *map_list = NULL; + + map_list = zmalloc(sizeof(*map_list)); + if (!map_list) { + goto end; + } + + lttng_dynamic_pointer_array_init(&map_list->array, + delete_map_array_element); + +end: + return map_list; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_list_add(struct lttng_map_list *map_list, + struct lttng_map *map) +{ + enum lttng_map_status status; + int ret; + + assert(map_list); + assert(map); + + lttng_map_get(map); + + ret = lttng_dynamic_pointer_array_add_pointer(&map_list->array, map); + if (ret) { + lttng_map_put(map); + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + status = LTTNG_MAP_STATUS_OK; +end: + return status; + +} + +LTTNG_HIDDEN +ssize_t lttng_map_list_create_from_payload(struct lttng_payload_view *src_view, + struct lttng_map_list **map_list) +{ + unsigned int i; + ssize_t ret, offset = 0; + const struct lttng_map_list_comm *map_list_comm; + struct lttng_map_list *local_map_list = NULL; + + map_list_comm = (typeof(map_list_comm)) src_view->buffer.data; + offset += sizeof(*map_list_comm); + + local_map_list = lttng_map_list_create(); + if (!local_map_list) { + ret = -1; + goto end; + } + + for (i = 0; i < map_list_comm->count; i++) { + struct lttng_map *map = NULL; + struct lttng_payload_view map_view = + lttng_payload_view_from_view(src_view, offset, -1); + ssize_t map_size; + + map_size = lttng_map_create_from_payload(&map_view, &map); + if (map_size < 0) { + ret = map_size; + goto end; + } + + /* Transfer ownership of the map to the collection. */ + ret = lttng_map_list_add(local_map_list, map); + lttng_map_put(map); + if (ret < 0) { + ret = -1; + goto end; + } + + offset += map_size; + } + + /* Pass ownership to caller. */ + *map_list = local_map_list; + local_map_list = NULL; + + ret = offset; +end: + lttng_map_list_destroy(local_map_list); + return ret; +} + +LTTNG_HIDDEN +int lttng_map_list_serialize(const struct lttng_map_list *map_list, + struct lttng_payload *payload) +{ + int ret; + unsigned int i, count; + enum lttng_map_status status; + struct lttng_map_list_comm map_list_comm = {}; + + status = lttng_map_list_get_count(map_list, &count); + if (status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_INVALID; + goto end; + } + + map_list_comm.count = count; + ret = lttng_dynamic_buffer_append(&payload->buffer, &map_list_comm, + sizeof(map_list_comm)); + if (ret) { + goto end; + } + for (i = 0; i < count; i++) { + const struct lttng_map *map = + lttng_map_list_get_at_index(map_list, i); + + assert(map); + + ret = lttng_map_serialize(map, payload); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +const struct lttng_map *lttng_map_list_get_at_index( + const struct lttng_map_list *map_list, unsigned int index) +{ + struct lttng_map *map = NULL; + + assert(map_list); + if (index >= lttng_dynamic_pointer_array_get_count(&map_list->array)) { + goto end; + } + + map = (struct lttng_map *) + lttng_dynamic_pointer_array_get_pointer( + &map_list->array, index); +end: + return map; +} + +enum lttng_map_status lttng_map_list_get_count( + const struct lttng_map_list *map_list, unsigned int *count) +{ + enum lttng_map_status status = LTTNG_MAP_STATUS_OK; + + if (!map_list || !count) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&map_list->array); + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +void lttng_map_list_destroy(struct lttng_map_list *map_list) +{ + if (!map_list) { + return; + } + + lttng_dynamic_pointer_array_reset(&map_list->array); + free(map_list); +} + +struct lttng_map_key_value_pair *lttng_map_key_value_pair_create(const char *key, + int64_t value) +{ + struct lttng_map_key_value_pair *key_value; + + key_value = zmalloc(sizeof(struct lttng_map_key_value_pair)); + if (!key_value) { + goto end; + } + + key_value->key = strdup(key); + if (!key_value->key) { + free(key_value); + key_value = NULL; + goto end; + } + key_value->value = value; + +end: + return key_value; +} + +enum lttng_map_status lttng_map_key_value_pair_get_key( + const struct lttng_map_key_value_pair *key_value, + const char **key) +{ + assert(key_value); + assert(key_value->key); + + *key = key_value->key; + return LTTNG_MAP_STATUS_OK; +} + +enum lttng_map_status lttng_map_key_value_pair_get_value( + const struct lttng_map_key_value_pair *key_value, + int64_t *value) +{ + assert(key_value); + *value = key_value->value; + return LTTNG_MAP_STATUS_OK; +} + +LTTNG_HIDDEN +void lttng_map_key_value_pair_set_has_overflowed( + struct lttng_map_key_value_pair *key_value) +{ + assert(key_value); + + key_value->has_overflowed = true; +} + +LTTNG_HIDDEN +void lttng_map_key_value_pair_set_has_underflowed( + struct lttng_map_key_value_pair *key_value) +{ + assert(key_value); + + key_value->has_underflowed = true; +} + +bool lttng_map_key_value_pair_get_has_overflowed( + const struct lttng_map_key_value_pair *key_value) +{ + assert(key_value); + + return key_value->has_overflowed; +} + +bool lttng_map_key_value_pair_get_has_underflowed( + const struct lttng_map_key_value_pair *key_value) +{ + assert(key_value); + + return key_value->has_underflowed; +} + +LTTNG_HIDDEN +ssize_t lttng_map_key_value_pair_create_from_payload( + struct lttng_payload_view *src_view, + struct lttng_map_key_value_pair **key_value) +{ + const struct lttng_map_key_value_pair_comm *kv_pair_comm; + struct lttng_map_key_value_pair *kv_pair; + ssize_t ret, offset = 0; + const char *key; + int64_t value; + + if (!src_view || !key_value) { + ret = -1; + goto end; + } + + kv_pair_comm = (typeof(kv_pair_comm)) src_view->buffer.data; + offset += sizeof(*kv_pair_comm); + + if (kv_pair_comm->key_length == 0) { + ret = -1; + goto end; + } + + value = kv_pair_comm->value; + + struct lttng_payload_view key_view = + lttng_payload_view_from_view(src_view, offset, + kv_pair_comm->key_length); + key = key_view.buffer.data; + if (!lttng_buffer_view_contains_string(&key_view.buffer, + key, kv_pair_comm->key_length)) { + ret = -1; + goto end; + } + + offset += kv_pair_comm->key_length; + + kv_pair = lttng_map_key_value_pair_create(key, value); + if (!kv_pair) { + ret = -1; + goto end; + } + + kv_pair->has_overflowed = kv_pair_comm->has_overflowed; + kv_pair->has_underflowed = kv_pair_comm->has_underflowed; + + *key_value = kv_pair; + + ret = offset; + +end: + return ret; +} + +LTTNG_HIDDEN +int lttng_map_key_value_pair_serialize( + const struct lttng_map_key_value_pair *key_value, + struct lttng_payload *payload) +{ + int ret; + size_t key_len; + struct lttng_map_key_value_pair_comm kv_pair_comm = {0}; + + assert(key_value); + assert(key_value->key); + + key_len = strlen(key_value->key) + 1; + + kv_pair_comm.key_length = key_len; + kv_pair_comm.value = key_value->value; + kv_pair_comm.has_overflowed = key_value->has_overflowed; + kv_pair_comm.has_underflowed = key_value->has_underflowed; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &kv_pair_comm, + sizeof(kv_pair_comm)); + if (ret) { + goto end; + } + + /* Append key.*/ + ret = lttng_dynamic_buffer_append( + &payload->buffer, key_value->key, key_len); + if (ret) { + goto end; + } + +end: + return ret; +} + +void lttng_map_key_value_pair_destroy(struct lttng_map_key_value_pair *key_value) +{ + if (!key_value) { + return; + } + + free(key_value->key); + free(key_value); +} + +static void delete_map_key_value_pair_array_element(void *ptr) +{ + struct lttng_map_key_value_pair *key_value = ptr; + lttng_map_key_value_pair_destroy(key_value); +} + +LTTNG_HIDDEN +struct lttng_map_key_value_pair_list *lttng_map_key_value_pair_list_create( + enum lttng_map_key_value_pair_list_type type, + bool summed_all_cpus) +{ + struct lttng_map_key_value_pair_list *map_key_values = NULL; + + map_key_values = zmalloc(sizeof(*map_key_values)); + if (!map_key_values) { + goto end; + } + + map_key_values->type = type; + map_key_values->summed_all_cpus = summed_all_cpus; + + lttng_dynamic_pointer_array_init(&map_key_values->array, + delete_map_key_value_pair_array_element); + +end: + return map_key_values; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_set_identifier( + struct lttng_map_key_value_pair_list *kv_pair_list, + uint64_t identifier) +{ + enum lttng_map_status status; + assert(kv_pair_list); + + switch (kv_pair_list->type) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + kv_pair_list->id = identifier; + status = LTTNG_MAP_STATUS_OK; + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED: + ERR("Cannot set an identifier for an UST per-pid aggregation key value pair list"); + status = LTTNG_MAP_STATUS_INVALID; + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL: + ERR("Cannot set an identifier for a kernel key value pair list"); + status = LTTNG_MAP_STATUS_INVALID; + break; + default: + ERR("Unknown key value par list type %d", kv_pair_list->type); + abort(); + } + + return status; +} + +LTTNG_HIDDEN +bool lttng_map_key_value_pair_list_get_summed_all_cpu( + const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + assert(kv_pair_list); + + return kv_pair_list->summed_all_cpus; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_set_cpu( + struct lttng_map_key_value_pair_list *kv_pair_list, + uint64_t cpu) +{ + assert(kv_pair_list); + + kv_pair_list->cpu = cpu; + + return LTTNG_MAP_STATUS_OK; +} + +LTTNG_HIDDEN +uint64_t lttng_map_key_value_pair_list_get_cpu( + const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + assert(kv_pair_list); + + return kv_pair_list->cpu; +} + +LTTNG_HIDDEN +enum lttng_map_key_value_pair_list_type lttng_map_key_value_pair_list_get_type( + const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + assert(kv_pair_list); + + return kv_pair_list->type; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_append_key_value( + struct lttng_map_key_value_pair_list *kv_pair_list, + struct lttng_map_key_value_pair *key_value) +{ + int ret; + enum lttng_map_status status; + + assert(kv_pair_list); + assert(key_value); + + ret = lttng_dynamic_pointer_array_add_pointer(&kv_pair_list->array, + key_value); + if (ret) { + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_STATUS_OK; + +end: + return status; +} + +LTTNG_HIDDEN +uint64_t lttng_map_key_value_pair_list_get_identifer( + const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + assert(kv_pair_list); + + switch (kv_pair_list->type) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED: + ERR("No identifier for UST per-pid aggregation key value pair lists"); + abort(); + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL: + ERR("No identifier for kernel key value pair lists"); + abort(); + break; + default: + ERR("Unknown key value par list type %d", kv_pair_list->type); + abort(); + } + + return kv_pair_list->id; +} + +const struct lttng_map_key_value_pair *lttng_map_key_value_pair_list_get_at_index( + const struct lttng_map_key_value_pair_list *kv_pair_list, + unsigned int index) +{ + struct lttng_map_key_value_pair *key_value = NULL; + + assert(kv_pair_list); + if (index >= lttng_dynamic_pointer_array_get_count(&kv_pair_list->array)) { + goto end; + } + + key_value = (struct lttng_map_key_value_pair *) + lttng_dynamic_pointer_array_get_pointer( + &kv_pair_list->array, index); +end: + return key_value; +} + +enum lttng_map_status lttng_map_key_value_pair_list_get_count( + const struct lttng_map_key_value_pair_list *kv_pair_list, + unsigned int *count) +{ + enum lttng_map_status status; + + if (!kv_pair_list || !count) { + status = LTTNG_MAP_STATUS_INVALID;; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&kv_pair_list->array); + + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +void lttng_map_key_value_pair_list_destroy(struct lttng_map_key_value_pair_list *kv_pair_list) +{ + if (!kv_pair_list) { + return; + } + + lttng_dynamic_pointer_array_reset(&kv_pair_list->array); + free(kv_pair_list); +} + +int lttng_map_key_value_pair_list_serialize( + const struct lttng_map_key_value_pair_list *kv_pair_list, + struct lttng_payload *payload) +{ + int ret; + unsigned int i, count; + enum lttng_map_status status; + struct lttng_map_key_value_pair_list_comm kv_pair_list_comm = {}; + + kv_pair_list_comm.id = kv_pair_list->id; + kv_pair_list_comm.cpu = kv_pair_list->cpu; + kv_pair_list_comm.type = (uint8_t) kv_pair_list->type; + kv_pair_list_comm.summed_all_cpus = (uint8_t) kv_pair_list->summed_all_cpus; + + status = lttng_map_key_value_pair_list_get_count(kv_pair_list, &count); + if (status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_INVALID; + goto end; + } + + kv_pair_list_comm.count = count; + ret = lttng_dynamic_buffer_append(&payload->buffer, &kv_pair_list_comm, + sizeof(kv_pair_list_comm)); + if (ret) { + goto end; + } + for (i = 0; i < count; i++) { + const struct lttng_map_key_value_pair *kv_pair = + lttng_map_key_value_pair_list_get_at_index(kv_pair_list, i); + + assert(kv_pair); + + ret = lttng_map_key_value_pair_serialize(kv_pair, payload); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_map_key_value_pair_list_create_from_payload( + struct lttng_payload_view *src_view, + struct lttng_map_key_value_pair_list **kv_pair_list) +{ + ssize_t ret, offset = 0; + unsigned int i; + enum lttng_map_status status; + const struct lttng_map_key_value_pair_list_comm *kv_pair_list_comm; + struct lttng_map_key_value_pair_list *local_key_values = NULL; + + kv_pair_list_comm = (typeof(kv_pair_list_comm)) src_view->buffer.data; + offset += sizeof(*kv_pair_list_comm); + + local_key_values = lttng_map_key_value_pair_list_create( + kv_pair_list_comm->type, kv_pair_list_comm->summed_all_cpus); + if (!local_key_values) { + ret = -1; + goto end; + } + + local_key_values->cpu = kv_pair_list_comm->cpu; + + switch (lttng_map_key_value_pair_list_get_type(local_key_values)) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + status = lttng_map_key_value_pair_list_set_identifier(local_key_values, + kv_pair_list_comm->id); + if (status != LTTNG_MAP_STATUS_OK) { + ret = -1; + goto end; + } + break; + default: + break; + } + + for (i = 0; i < kv_pair_list_comm->count; i++) { + struct lttng_map_key_value_pair *kv_pair = NULL; + struct lttng_payload_view kv_view = + lttng_payload_view_from_view(src_view, offset, -1); + ssize_t kv_size; + + kv_size = lttng_map_key_value_pair_create_from_payload( + &kv_view, &kv_pair); + if (kv_size < 0) { + ret = kv_size; + goto end; + } + + /* Transfer ownership of the key-value to the collection. */ + status = lttng_map_key_value_pair_list_append_key_value(local_key_values, + kv_pair); + if (status != LTTNG_MAP_STATUS_OK) { + ret = -1; + goto end; + } + + offset += kv_size; + } + + /* Pass ownership to caller. */ + *kv_pair_list = local_key_values; + local_key_values = NULL; + + ret = offset; +end: + lttng_map_key_value_pair_list_destroy(local_key_values); + return ret; +} + +static void delete_map_key_value_pair_list_array_element(void *ptr) +{ + struct lttng_map_key_value_pair_list *kv_list = ptr; + lttng_map_key_value_pair_list_destroy(kv_list); +} + +LTTNG_HIDDEN +struct lttng_map_content *lttng_map_content_create( + enum lttng_buffer_type type) +{ + struct lttng_map_content *map_content = NULL; + + map_content = zmalloc(sizeof(*map_content)); + if (!map_content) { + goto end; + } + + map_content->type = type; + + lttng_dynamic_pointer_array_init(&map_content->array, + delete_map_key_value_pair_list_array_element); + +end: + return map_content; +} + +enum lttng_map_status lttng_map_content_get_count( + const struct lttng_map_content *map_content, + unsigned int *count) +{ + enum lttng_map_status status = LTTNG_MAP_STATUS_OK; + + if (!map_content || !count) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&map_content->array); + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +enum lttng_buffer_type lttng_map_content_get_buffer_type( + const struct lttng_map_content *map_content) +{ + assert(map_content); + + return map_content->type; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_content_append_key_value_list( + struct lttng_map_content *map_content, + struct lttng_map_key_value_pair_list *kv_list) +{ + int ret; + enum lttng_map_status status; + + assert(map_content); + assert(kv_list); + + ret = lttng_dynamic_pointer_array_add_pointer(&map_content->array, + kv_list); + if (ret) { + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_STATUS_OK; + +end: + return status; +} + +const struct lttng_map_key_value_pair_list *lttng_map_content_get_at_index( + const struct lttng_map_content *map_content, + unsigned int index) +{ + struct lttng_map_key_value_pair_list *kv_pair_list = NULL; + + assert(map_content); + if (index >= lttng_dynamic_pointer_array_get_count(&map_content->array)) { + goto end; + } + + kv_pair_list = (struct lttng_map_key_value_pair_list *) + lttng_dynamic_pointer_array_get_pointer( + &map_content->array, index); +end: + return kv_pair_list; +} + +LTTNG_HIDDEN +ssize_t lttng_map_content_create_from_payload( + struct lttng_payload_view *src_view, + struct lttng_map_content **map_content) +{ + ssize_t ret, offset = 0; + unsigned int i; + struct lttng_map_content_comm *map_content_comm; + struct lttng_map_content *local_map_content; + + map_content_comm = (typeof(map_content_comm)) src_view->buffer.data; + offset += sizeof(*map_content_comm); + + local_map_content = lttng_map_content_create(map_content_comm->type); + if (!local_map_content) { + ret = -1; + goto end; + } + + for (i = 0; i < map_content_comm->count; i++) { + struct lttng_map_key_value_pair_list *kv_pair_list = NULL; + struct lttng_payload_view kv_list_view = + lttng_payload_view_from_view(src_view, offset, -1); + ssize_t kv_list_size; + + kv_list_size = lttng_map_key_value_pair_list_create_from_payload( + &kv_list_view, &kv_pair_list); + if (kv_list_size < 0) { + ret = kv_list_size; + goto end; + } + + /* Transfer ownership of the key-value to the collection. */ + ret = lttng_map_content_append_key_value_list(local_map_content, + kv_pair_list); + if (ret < 0) { + ret = -1; + goto end; + } + + offset += kv_list_size; + } + + /* Pass ownership to caller. */ + *map_content = local_map_content; + local_map_content = NULL; + + ret = offset; +end: + lttng_map_content_destroy(local_map_content); + return ret; +} + +LTTNG_HIDDEN +int lttng_map_content_serialize( + const struct lttng_map_content *map_content, + struct lttng_payload *payload) +{ + int ret; + unsigned int i, count; + enum lttng_map_status status; + struct lttng_map_content_comm map_content_comm = {}; + + status = lttng_map_content_get_count(map_content, &count); + if (status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_INVALID; + goto end; + } + + map_content_comm.count = count; + map_content_comm.type = lttng_map_content_get_buffer_type(map_content); + + ret = lttng_dynamic_buffer_append(&payload->buffer, &map_content_comm, + sizeof(map_content_comm)); + if (ret) { + goto end; + } + for (i = 0; i < count; i++) { + const struct lttng_map_key_value_pair_list *kv_pair_list = + lttng_map_content_get_at_index(map_content, i); + + assert(kv_pair_list); + + ret = lttng_map_key_value_pair_list_serialize(kv_pair_list, payload); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +void lttng_map_content_destroy(struct lttng_map_content *map_content) +{ + if (!map_content) { + return; + } + + lttng_dynamic_pointer_array_reset(&map_content->array); + free(map_content); +} diff --git a/src/common/notification.c b/src/common/notification.c index c347b3cea..a4e971bd5 100644 --- a/src/common/notification.c +++ b/src/common/notification.c @@ -122,7 +122,7 @@ ssize_t lttng_notification_create_from_payload( notification_size, -1); evaluation_size = lttng_evaluation_create_from_payload( - &evaluation_view, &evaluation); + condition, &evaluation_view, &evaluation); } if (evaluation_size < 0) { diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index 4c1ba5cce..e65d358ad 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -107,6 +107,11 @@ enum lttcomm_sessiond_command { LTTNG_CREATE_SESSION_EXT = 49, LTTNG_CLEAR_SESSION = 50, LTTNG_LIST_TRIGGERS = 51, + LTTNG_ADD_MAP = 52, + LTTNG_ENABLE_MAP = 53, + LTTNG_DISABLE_MAP = 54, + LTTNG_LIST_MAPS = 55, + LTTNG_LIST_MAP_VALUES = 56, }; static inline @@ -197,6 +202,16 @@ const char *lttcomm_sessiond_command_str(enum lttcomm_sessiond_command cmd) return "LTTNG_CLEAR_SESSION"; case LTTNG_LIST_TRIGGERS: return "LTTNG_LIST_TRIGGERS"; + case LTTNG_ADD_MAP: + return "LTTNG_ADD_MAP"; + case LTTNG_ENABLE_MAP: + return "LTTNG_ENABLE_MAP"; + case LTTNG_DISABLE_MAP: + return "LTTNG_DISABLE_MAP"; + case LTTNG_LIST_MAPS: + return "LTTNG_LIST_MAPS"; + case LTTNG_LIST_MAP_VALUES: + return "LTTNG_LIST_MAP_VALUES"; default: abort(); } @@ -413,6 +428,18 @@ struct lttcomm_session_msg { struct lttng_channel chan; struct lttng_channel_extended extended; } LTTNG_PACKED channel; + /* Add map */ + struct { + uint32_t length; + } LTTNG_PACKED add_map; + /* Enable map */ + struct { + char map_name[LTTNG_SYMBOL_NAME_LEN]; + } LTTNG_PACKED enable_map; + /* Disable map */ + struct { + char map_name[LTTNG_SYMBOL_NAME_LEN]; + } LTTNG_PACKED disable_map; /* Context */ struct { char channel_name[LTTNG_SYMBOL_NAME_LEN]; @@ -508,6 +535,10 @@ struct lttcomm_session_msg { uint64_t session_descriptor_size; /* An lttng_session_descriptor follows. */ } LTTNG_PACKED create_session; + struct { + uint32_t map_length; + uint32_t query_length; + } LTTNG_PACKED list_map_values; } u; /* Count of fds sent. */ uint32_t fd_count; diff --git a/src/bin/lttng-sessiond/shm.c b/src/common/shm.c similarity index 86% rename from src/bin/lttng-sessiond/shm.c rename to src/common/shm.c index 8b7744221..737f73e08 100644 --- a/src/bin/lttng-sessiond/shm.c +++ b/src/common/shm.c @@ -189,3 +189,41 @@ char *shm_ust_get_mmap(char *shm_path, int global) error: return NULL; } + +/* + * shm_create_anonymous is never called concurrently within a process. + */ +int shm_create_anonymous(const char *owner_name) +{ + char tmp_name[NAME_MAX]; + int shmfd, ret; + + ret = snprintf(tmp_name, NAME_MAX, "/shm-%s-%d", owner_name, getpid()); + if (ret < 0) { + PERROR("snprintf"); + return -1; + } + /* + * Allocate shm, and immediately unlink its shm oject, keeping only the + * file descriptor as a reference to the object. + */ + shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700); + if (shmfd < 0) { + PERROR("shm_open"); + goto error_shm_open; + } + ret = shm_unlink(tmp_name); + if (ret < 0 && errno != ENOENT) { + PERROR("shm_unlink"); + goto error_shm_release; + } + return shmfd; + +error_shm_release: + ret = close(shmfd); + if (ret) { + PERROR("close"); + } +error_shm_open: + return -1; +} diff --git a/src/bin/lttng-sessiond/shm.h b/src/common/shm.h similarity index 85% rename from src/bin/lttng-sessiond/shm.h rename to src/common/shm.h index 94d2b72a1..d714506b8 100644 --- a/src/bin/lttng-sessiond/shm.h +++ b/src/common/shm.h @@ -11,4 +11,6 @@ char *shm_ust_get_mmap(char *shm_path, int global); +int shm_create_anonymous(const char *owner_name); + #endif /* _LTT_SHM_H */ diff --git a/src/common/trigger.c b/src/common/trigger.c index 71162e79b..9e0333be9 100644 --- a/src/common/trigger.c +++ b/src/common/trigger.c @@ -7,13 +7,14 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -92,7 +93,6 @@ const struct lttng_condition *lttng_trigger_get_const_condition( return trigger ? trigger->condition : NULL; } - /* * Note: the lack of reference counting 'get' on the action object is normal. * This API was exposed as such in 2.11. The client is not expected to call @@ -395,8 +395,13 @@ bool lttng_trigger_is_equal( } /* - * Name is not taken into account since it is cosmetic only. + * FIXME: frdeso: this is a change of behavior. + * See internal tracker issue 1028. */ + if (strcmp(a->name, b->name) != 0) { + return false; + } + if (!lttng_condition_is_equal(a->condition, b->condition)) { return false; } @@ -909,9 +914,9 @@ enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction( /* Apply to any domain. */ type = LTTNG_DOMAIN_NONE; break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: /* Return the domain of the event rule. */ - c_status = lttng_condition_event_rule_get_rule( + c_status = lttng_condition_on_event_get_rule( trigger->condition, &event_rule); assert(c_status == LTTNG_CONDITION_STATUS_OK); type = lttng_event_rule_get_domain_type(event_rule); @@ -949,11 +954,11 @@ enum lttng_error_code lttng_trigger_generate_bytecode( } switch (lttng_condition_get_type(condition)) { - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: { struct lttng_event_rule *event_rule; const enum lttng_condition_status condition_status = - lttng_condition_event_rule_borrow_rule_mutable( + lttng_condition_on_event_borrow_rule_mutable( condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); @@ -966,7 +971,7 @@ enum lttng_error_code lttng_trigger_generate_bytecode( } /* Generate the capture bytecode. */ - ret = lttng_condition_event_rule_generate_capture_descriptor_bytecode( + ret = lttng_condition_on_event_generate_capture_descriptor_bytecode( condition); if (ret != LTTNG_OK) { goto end; @@ -1013,3 +1018,88 @@ end: lttng_payload_reset(©_buffer); return copy; } + + +static +bool action_type_needs_tracer_notifier(enum lttng_action_type action_type) +{ + switch (action_type) { + case LTTNG_ACTION_TYPE_NOTIFY: + case LTTNG_ACTION_TYPE_START_SESSION: + case LTTNG_ACTION_TYPE_STOP_SESSION: + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + return true; + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + return false; + case LTTNG_ACTION_TYPE_GROUP: + case LTTNG_ACTION_TYPE_UNKNOWN: + default: + abort(); + } +} + +static +bool action_needs_tracer_notifier(const struct lttng_action *action) +{ + bool needs_tracer_notifier = false; + unsigned int i, count; + enum lttng_action_status action_status; + enum lttng_action_type action_type; + + assert(action); + /* If there is only one action. Check if it needs a tracer notifier. */ + action_type = lttng_action_get_type(action); + if (action_type != LTTNG_ACTION_TYPE_GROUP) { + needs_tracer_notifier = action_type_needs_tracer_notifier( + action_type); + goto end; + } + + /* + * Iterate over all the actions of the action group and check if any of + * them needs a tracer notifier. + */ + action_status = lttng_action_group_get_count(action, &count); + assert(action_status == LTTNG_ACTION_STATUS_OK); + for (i = 0; i < count; i++) { + const struct lttng_action *inner_action = + lttng_action_group_get_at_index(action, i); + + action_type = lttng_action_get_type(inner_action); + if (action_type_needs_tracer_notifier(action_type)) { + needs_tracer_notifier = true; + goto end; + } + } + +end: + return needs_tracer_notifier; +} + +LTTNG_HIDDEN +bool lttng_trigger_needs_tracer_notifier(const struct lttng_trigger *trigger) +{ + bool needs_tracer_notifier = false; + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const struct lttng_action *action = + lttng_trigger_get_const_action(trigger); + + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_ON_EVENT: + needs_tracer_notifier = action_needs_tracer_notifier(action); + goto end; + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + goto end; + case LTTNG_CONDITION_TYPE_UNKNOWN: + default: + abort(); + } +end: + return needs_tracer_notifier; +} diff --git a/src/common/ust-consumer/ust-consumer.c b/src/common/ust-consumer/ust-consumer.c index 6b195c77d..017ea5fb8 100644 --- a/src/common/ust-consumer/ust-consumer.c +++ b/src/common/ust-consumer/ust-consumer.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include "ust-consumer.h" @@ -352,48 +353,6 @@ error_alloc: return ret; } -/* - * create_posix_shm is never called concurrently within a process. - */ -static -int create_posix_shm(void) -{ - char tmp_name[NAME_MAX]; - int shmfd, ret; - - ret = snprintf(tmp_name, NAME_MAX, "/ust-shm-consumer-%d", getpid()); - if (ret < 0) { - PERROR("snprintf"); - return -1; - } - /* - * Allocate shm, and immediately unlink its shm oject, keeping - * only the file descriptor as a reference to the object. - * We specifically do _not_ use the / at the beginning of the - * pathname so that some OS implementations can keep it local to - * the process (POSIX leaves this implementation-defined). - */ - shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700); - if (shmfd < 0) { - PERROR("shm_open"); - goto error_shm_open; - } - ret = shm_unlink(tmp_name); - if (ret < 0 && errno != ENOENT) { - PERROR("shm_unlink"); - goto error_shm_release; - } - return shmfd; - -error_shm_release: - ret = close(shmfd); - if (ret) { - PERROR("close"); - } -error_shm_open: - return -1; -} - static int open_ust_stream_fd(struct lttng_consumer_channel *channel, int cpu, const struct lttng_credentials *session_credentials) { @@ -401,7 +360,7 @@ static int open_ust_stream_fd(struct lttng_consumer_channel *channel, int cpu, int ret; if (!channel->shm_path[0]) { - return create_posix_shm(); + return shm_create_anonymous("ust-consumer"); } ret = get_stream_shm_path(shm_path, channel->shm_path, cpu); if (ret) { diff --git a/src/common/utils.c b/src/common/utils.c index 134d2054f..df409aa4c 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -1486,6 +1486,16 @@ fopen_error: return ret; } +LTTNG_HIDDEN +int utils_get_number_of_possible_cpus(void) +{ + /* + * Return the number of configured cpus as opposed to number of online + * cpus. + */ + return sysconf(_SC_NPROCESSORS_CONF); +} + /* * Returns an estimate of the number of bytes of memory available based on the * the information in `/proc/meminfo`. The number returned by this function is diff --git a/src/common/utils.h b/src/common/utils.h index 570216d53..6fb5b2dfd 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -53,6 +53,7 @@ int utils_truncate_stream_file(int fd, off_t length); int utils_show_help(int section, const char *page_name, const char *help_msg); int utils_get_memory_available(size_t *value); int utils_get_memory_total(size_t *value); +int utils_get_number_of_possible_cpus(void); int utils_change_working_directory(const char *path); enum lttng_error_code utils_user_id_from_name( const char *user_name, uid_t *user_id); diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 6deab7848..8cb1e9b4e 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -10,6 +10,7 @@ * */ +#include "lttng/domain.h" #define _LGPL_SOURCE #include #include @@ -38,6 +39,8 @@ #include #include #include +#include +#include #include #include #include @@ -1212,10 +1215,16 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, } ret = lttng_dynamic_buffer_append(&payload.buffer, - *(exclusion_list + i), LTTNG_SYMBOL_NAME_LEN); + exclusion_list[i], exclusion_len); if (ret) { goto mem_error; } + + for (int i = 0; i < (LTTNG_SYMBOL_NAME_LEN - exclusion_len); i++) { + char c = 0; + + lttng_dynamic_buffer_append(&payload.buffer, &c, 1); + } } /* Add filter expression next. */ @@ -1636,6 +1645,178 @@ end: return ret; } +enum lttng_error_code lttng_add_map(struct lttng_handle *handle, + struct lttng_map *map) +{ + + int ret; + enum lttng_error_code ret_code; + struct lttcomm_session_msg lsm = { + .cmd_type = LTTNG_ADD_MAP, + }; + struct lttcomm_session_msg *message_lsm; + struct lttng_payload message; + struct lttng_payload reply; + + lttng_payload_init(&message); + lttng_payload_init(&reply); + + if (!map) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + lsm.domain.type = lttng_map_get_domain(map); + + lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + message_lsm = (struct lttcomm_session_msg *) message.buffer.data; + + ret = lttng_map_serialize(map, &message); + if (ret < 0) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + + message_lsm->u.add_map.length = (uint32_t) message.buffer.size - sizeof(lsm); + + { + struct lttng_payload_view message_view = + lttng_payload_view_from_payload(&message, 0, -1); + + message_lsm->fd_count = lttng_payload_view_get_fd_handle_count( + &message_view); + + ret = lttng_ctl_ask_sessiond_payload(&message_view, &reply); + if (ret < 0) { + ret_code = ret; + goto end; + } + } + + ret_code = LTTNG_OK; + +end: + lttng_payload_reset(&message); + lttng_payload_reset(&reply); + return ret_code; +} + +enum lttng_error_code lttng_enable_map(struct lttng_handle *handle, + const char *map_name) +{ + int ret; + enum lttng_error_code ret_code; + struct lttcomm_session_msg lsm; + struct lttng_payload message; + struct lttng_payload_view lsm_view = + lttng_payload_view_init_from_buffer( + (const char *) &lsm, 0, sizeof(lsm)); + struct lttng_payload reply; + + lttng_payload_init(&message); + lttng_payload_init(&reply); + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_ENABLE_MAP; + + COPY_DOMAIN_PACKED(lsm.domain, handle->domain); + + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(lsm.u.enable_map.map_name, map_name, + sizeof(lsm.u.enable_map.map_name)); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply); + if (ret < 0) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + + ret_code = LTTNG_OK; + +end: + lttng_payload_reset(&message); + lttng_payload_reset(&reply); + return ret_code; +} + +enum lttng_error_code lttng_disable_map(struct lttng_handle *handle, + const char *map_name) +{ + int ret; + enum lttng_error_code ret_code; + struct lttcomm_session_msg lsm; + struct lttng_payload message; + struct lttng_payload_view lsm_view = + lttng_payload_view_init_from_buffer( + (const char *) &lsm, 0, sizeof(lsm)); + struct lttng_payload reply; + + lttng_payload_init(&message); + lttng_payload_init(&reply); + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_DISABLE_MAP; + + COPY_DOMAIN_PACKED(lsm.domain, handle->domain); + + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(lsm.u.disable_map.map_name, map_name, + sizeof(lsm.u.disable_map.map_name)); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply); + if (ret < 0) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + + ret_code = LTTNG_OK; + +end: + lttng_payload_reset(&message); + lttng_payload_reset(&reply); + return ret_code; +} + /* * All tracing will be stopped for registered events of the channel. * Returns size of returned session payload data or a negative error code. @@ -2276,6 +2457,61 @@ end: return ret; } +enum lttng_error_code lttng_list_maps(struct lttng_handle *handle, + struct lttng_map_list **map_list) +{ + int ret; + enum lttng_error_code ret_code = LTTNG_OK; + struct lttcomm_session_msg lsm = { .cmd_type = LTTNG_LIST_MAPS }; + struct lttng_map_list *local_map_list = NULL; + struct lttng_payload reply; + struct lttng_payload_view lsm_view = + lttng_payload_view_init_from_buffer( + (const char *) &lsm, 0, sizeof(lsm)); + + lttng_payload_init(&reply); + + if (handle == NULL) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + COPY_DOMAIN_PACKED(lsm.domain, handle->domain); + + ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply); + if (ret < 0) { + ret_code = (enum lttng_error_code) -ret; + goto end; + } + + { + struct lttng_payload_view reply_view = + lttng_payload_view_from_payload( + &reply, 0, reply.buffer.size); + + ret = lttng_map_list_create_from_payload( + &reply_view, &local_map_list); + if (ret < 0) { + ret_code = LTTNG_ERR_FATAL; + goto end; + } + } + + *map_list = local_map_list; + local_map_list = NULL; +end: + lttng_payload_reset(&reply); + lttng_map_list_destroy(local_map_list); + return ret_code; +} + /* * Ask the session daemon for all available events of a session channel. * Sets the contents of the events array. @@ -3352,6 +3588,101 @@ end: return ret_code; } +/* + * Ask the session daemon for all values for a given map. + * On error, returns a negative value. + */ +enum lttng_error_code lttng_list_map_content( + struct lttng_handle *handle, const struct lttng_map *map, + const struct lttng_map_query *map_query, + struct lttng_map_content **map_content) +{ + struct lttcomm_session_msg lsm = { + .cmd_type = LTTNG_LIST_MAP_VALUES, + }; + struct lttcomm_session_msg *message_lsm; + struct lttng_payload message; + struct lttng_payload reply; + enum lttng_error_code ret; + struct lttng_map_content *local_map_content = NULL; + uint32_t map_length, map_query_length; + + lttng_payload_init(&message); + lttng_payload_init(&reply); + + if (!map || !map_query) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + lsm.domain.type = handle->domain.type; + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); + if (ret) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_map_serialize(map, &message); + if (ret < 0) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + map_length = (uint32_t) message.buffer.size - sizeof(lsm); + + ret = lttng_map_query_serialize(map_query, &message); + if (ret < 0) { + ret = -LTTNG_ERR_UNK; + goto end; + } + map_query_length = (uint32_t) message.buffer.size - map_length - sizeof(lsm); + + message_lsm = (struct lttcomm_session_msg *) message.buffer.data; + message_lsm->u.list_map_values.map_length = map_length; + message_lsm->u.list_map_values.query_length = map_query_length; + { + struct lttng_payload_view message_view = + lttng_payload_view_from_payload( + &message, 0, -1); + + ret = lttng_ctl_ask_sessiond_payload(&message_view, &reply); + if (ret < 0) { + goto end; + } + } + + + { + struct lttng_payload_view reply_view = + lttng_payload_view_from_payload( + &reply, 0, reply.buffer.size); + ret = lttng_map_content_create_from_payload( + &reply_view, &local_map_content); + if (ret < 0) { + ret = LTTNG_ERR_FATAL; + goto end; + } + } + + *map_content = local_map_content; + local_map_content = NULL; + + ret = LTTNG_OK; + goto end; +end: + lttng_payload_reset(&reply); + lttng_payload_reset(&message); + lttng_map_content_destroy(local_map_content); + return ret; +} + /* * lib constructor. */ diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am index 817918f68..ec91e3b56 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -27,13 +27,17 @@ TESTS = tools/filtering/test_invalid_filter \ tools/crash/test_crash \ tools/regen-metadata/test_ust \ tools/regen-statedump/test_ust \ + tools/map/test_map_kernel \ tools/notification/test_notification_ust_error \ tools/notification/test_notification_ust_buffer_usage \ + tools/notification/test_notification_ust_capture \ tools/notification/test_notification_ust_event_rule_condition_exclusion \ tools/notification/test_notification_kernel_error \ tools/notification/test_notification_kernel_buffer_usage \ + tools/notification/test_notification_kernel_capture \ tools/notification/test_notification_kernel_instrumentation \ tools/notification/test_notification_kernel_syscall \ + tools/notification/test_notification_notifier_discarded_count \ tools/notification/test_notification_kernel_userspace_probe \ tools/notification/test_notification_multi_app \ tools/rotation/test_ust \ @@ -68,6 +72,7 @@ TESTS += ust/before-after/test_before_after \ ust/blocking/test_blocking \ ust/multi-lib/test_multi_lib \ ust/rotation-destroy-flush/test_rotation_destroy_flush \ + tools/map/test_map_ust \ tools/metadata/test_ust \ tools/relayd-grouping/test_ust diff --git a/tests/regression/kernel/Makefile.am b/tests/regression/kernel/Makefile.am index 8547efd67..f06f51528 100644 --- a/tests/regression/kernel/Makefile.am +++ b/tests/regression/kernel/Makefile.am @@ -1,11 +1,18 @@ # SPDX-License-Identifier: GPL-2.0-only -EXTRA_DIST = test_event_basic test_all_events test_syscall \ - test_clock_override test_rotation_destroy_flush \ - test_select_poll_epoll test_lttng_logger \ - test_userspace_probe test_callstack \ - test_syscall validate_select_poll_epoll.py \ - test_ns_contexts test_ns_contexts_change +EXTRA_DIST = test_all_events test_syscall \ + test_callstack \ + test_clock_override \ + test_event_basic \ + test_kernel_function \ + test_lttng_logger \ + test_ns_contexts \ + test_ns_contexts_change + test_rotation_destroy_flush \ + test_select_poll_epoll \ + test_syscall \ + test_userspace_probe \ + validate_select_poll_epoll.py noinst_PROGRAMS = select_poll_epoll select_poll_epoll_SOURCES = select_poll_epoll.c diff --git a/tests/regression/kernel/test_kernel_function b/tests/regression/kernel/test_kernel_function new file mode 100755 index 000000000..47deb9222 --- /dev/null +++ b/tests/regression/kernel/test_kernel_function @@ -0,0 +1,62 @@ +#!/bin/bash +# +# Copyright (C) 2013 Christian Babeux +# +# SPDX-License-Identifier: GPL-2.0-only +# + +TEST_DESC="Kernel tracer - function event" + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../.. +NUM_TESTS=6 + +source $TESTDIR/utils/utils.sh + +function test_kernel_function_basic() +{ + local TRACE_PATH=$(mktemp -d) + local SESSION_NAME="kernel_function_basic" + local EVENT_NAME="my_event_name" + local TARGET_SYMBOL="lttng_test_filter_event_write" + + create_lttng_session_ok $SESSION_NAME $TRACE_PATH + + lttng_enable_kernel_function_event_ok $SESSION_NAME "$TARGET_SYMBOL" "$EVENT_NAME" + + start_lttng_tracing_ok + + echo 1 > /proc/lttng-test-filter-event + + stop_lttng_tracing_ok + + validate_trace "${EVENT_NAME}_entry" $TRACE_PATH + validate_trace "${EVENT_NAME}_return" $TRACE_PATH + + destroy_lttng_session_ok $SESSION_NAME + + rm -rf $TRACE_PATH +} + +# MUST set TESTDIR before calling those functions +plan_tests $NUM_TESTS + +print_test_banner "$TEST_DESC" + +if [ "$(id -u)" == "0" ]; then + isroot=1 +else + isroot=0 +fi + +skip $isroot "Root access is needed. Skipping all tests." $NUM_TESTS || +{ + start_lttng_sessiond_notap + validate_lttng_modules_present + modprobe lttng-test + + test_kernel_function_basic + + modprobe --remove lttng-test + stop_lttng_sessiond_notap +} diff --git a/tests/regression/tools/Makefile.am b/tests/regression/tools/Makefile.am index d561a6479..1aa0b6c6a 100644 --- a/tests/regression/tools/Makefile.am +++ b/tests/regression/tools/Makefile.am @@ -2,4 +2,4 @@ SUBDIRS = streaming filtering health tracefile-limits snapshots live exclusion save-load mi \ wildcard crash regen-metadata regen-statedump notification rotation \ - base-path metadata working-directory relayd-grouping clear tracker trigger + base-path map metadata working-directory relayd-grouping clear tracker trigger diff --git a/tests/regression/tools/map/Makefile.am b/tests/regression/tools/map/Makefile.am new file mode 100644 index 000000000..8fb83c33f --- /dev/null +++ b/tests/regression/tools/map/Makefile.am @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-only + +AM_CPPFLAGS += -I$(top_srcdir)/tests -I$(srcdir) + +noinst_SCRIPTS = \ + map_base_test.sh \ + test_map_kernel + +EXTRA_DIST = \ + map_base_test.sh \ + test_map_kernel + +if HAVE_LIBLTTNG_UST_CTL +EXTRA_DIST += test_map_ust +noinst_SCRIPTS += test_map_ust +endif + +all-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + cp -f $(srcdir)/$$script $(builddir); \ + done; \ + fi + +clean-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + rm -f $(builddir)/$$script; \ + done; \ + fi diff --git a/tests/regression/tools/map/map_base_test.sh b/tests/regression/tools/map/map_base_test.sh new file mode 100644 index 000000000..ae7584936 --- /dev/null +++ b/tests/regression/tools/map/map_base_test.sh @@ -0,0 +1,578 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../.. + +TMPDIR=$(mktemp -d) + +SH_TAP=1 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +function view_map_ok() { + local map_name="$1" + local key="$2" + local expected_value="$3" + local extracted_value + local temp_view_output + + temp_view_output=$(mktemp -t map_view_output.XXXXXX) + + "$FULL_LTTNG_BIN" view-map "$map_name" --key="$key" > "$temp_view_output" + ok $? "Map '$map_name' viewed succesfully" + + grep -q " $key " "$temp_view_output" + ok $? "Key '$key' found in view-map output" + + # Get value + # TODO: this is based on the text output, ideally when mi is availabe we + # who should use it to parse the value! + # Sample output + # | key | 5| + # Keep white space surrounding the key so to avoid grepping a substring + # in a larger key. + extracted_value=$(grep " $key " "$temp_view_output" | tr -d " " | cut -d "|" -f3) + # Necessary since the returned value can be non existent + extracted_value=${extracted_value:-"-1"} + + is "$extracted_value" "$expected_value" "Key value is $expected_value as expected" + + rm -f "$temp_view_output" +} + +function test_map_view_empty() +{ + local domain="$1" + local bitness="$2" + local buf_option="$3" + + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + + diag "Map view empty: $domain bitness $bitness $buf_option" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + "$FULL_LTTNG_BIN" view-map "$MAP_NAME" > /dev/null + ok $? "Map enabled viewed succesfully" + + "$FULL_LTTNG_BIN" disable-map "$domain" "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" view-map "$MAP_NAME" > /dev/null + ok $? "Map disabled viewed succesfully" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_formated_keys() +{ + local domain="$1" + local event_name="$2" + local key_format="$3" + local expected_key="$4" + local test_app="$5" + + local bitness="32" + # buf option left empty for use with both UST and kernel domain. + local buf_option="" + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + + diag "Map with $domain formated key. event-name: \"$event_name\", key format: \"$key_format\", expecting: \"$expected_key\"" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$key_format" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$expected_key" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_n_triggers_n_keys() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + local number_of_trigger=5 + + diag "Map $domain with $number_of_trigger triggers with all different keys" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + for i in $(seq 1 $number_of_trigger); do + cur_trigger_name="${TRIGGER_NAME}${i}" + lttng_add_trigger_ok "$cur_trigger_name" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" \ + --map "$MAP_NAME" \ + --key "${KEY}${i}" + done + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + for i in $(seq 1 $number_of_trigger); do + view_map_ok "$MAP_NAME" "$KEY${i}" "$NR_ITER" + done + + for i in $(seq 1 $number_of_trigger); do + lttng_remove_trigger_ok "$TRIGGER_NAME${i}" + done + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_n_triggers_1_key() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + local number_of_trigger=5 + + diag "Map $domain with $number_of_trigger triggers all with the same key" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + for i in $(seq 1 $number_of_trigger); do + cur_trigger_name="${TRIGGER_NAME}${i}" + lttng_add_trigger_ok "$cur_trigger_name" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" \ + --map "$MAP_NAME" \ + --key "${KEY}" + done + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER * number_of_trigger))" + + for i in $(seq 1 $number_of_trigger); do + lttng_remove_trigger_ok "$TRIGGER_NAME${i}" + done + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_n_triggers_1_key_coalesced() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + local number_of_trigger=5 + + diag "Map $domain with $number_of_trigger triggers all with the same key" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" "--coalesce-hits" + + for i in $(seq 1 $number_of_trigger); do + cur_trigger_name="${TRIGGER_NAME}${i}" + lttng_add_trigger_ok "$cur_trigger_name" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" \ + --map "$MAP_NAME" \ + --key "${KEY}" + done + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + # With the `coalesce-hits` map option two enablers on the same event + # with the same key will only increment the counter once. + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER))" + + for i in $(seq 1 $number_of_trigger); do + lttng_remove_trigger_ok "$TRIGGER_NAME${i}" + done + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_disable_enable() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map $domain disable-enable --bitness $bitness" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + "$FULL_LTTNG_BIN" disable-map "$domain" -s "$SESSION_NAME" "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + # The values in the map should not have changed since the map is + # disabled. + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + "$FULL_LTTNG_BIN" enable-map "$domain" -s "$SESSION_NAME" "$MAP_NAME" > /dev/null + ok $? "Map enabled succesfully" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER * 2))" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_add_remove_add_trigger() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map $domain add-remove-add the same trigger" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER * 2))" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_creation_after_trigger() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map $domain creation after trigger creation" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_remove_trigger_before_stop() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map remove trigger before stop" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + stop_lttng_tracing_ok $SESSION_NAME + + # Confirm that the map content is unchanged after a stop. + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_two_incr_value_two_keys() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY1="romados" + local KEY2="pitarifique" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map remove trigger before stop" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY1" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY2" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY1" "$NR_ITER" + view_map_ok "$MAP_NAME" "$KEY2" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_filter() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local event_name="$2" + local filter_field="$3" + local test_app="$4" + local buf_option="" + local bitness="32" + + diag "Map $domain filtering $event_name filter: \"$filter_field == 0\"" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" --filter "$filter_field==0"\ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "1" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_clear() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + local bitness="32" + + diag "Map $domain clear" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_clear_session_ok "$SESSION_NAME" + + view_map_ok "$MAP_NAME" "$KEY" "0" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} diff --git a/tests/regression/tools/map/test_map_kernel b/tests/regression/tools/map/test_map_kernel new file mode 100755 index 000000000..3317e25e8 --- /dev/null +++ b/tests/regression/tools/map/test_map_kernel @@ -0,0 +1,322 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../.. +NR_ITER=5 +KERNEL_EVENT_NAME="lttng_test_filter_event" +KERNEL_TEST_FILE_NAME="/proc/lttng-test-filter-event" +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_SYSCALL_BIN="$TESTAPP_PATH/gen-syscall-events/gen-syscall-events" +TESTAPP_USERSPACE_BINARY="$TESTAPP_PATH/userspace-probe-elf-binary/.libs/userspace-probe-elf-binary" + +KERNEL_NUM_TESTS=421 +NUM_TESTS=$(($KERNEL_NUM_TESTS)) + +TMPDIR=$(mktemp -d) + +SH_TAP=1 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +source "$CURDIR/map_base_test.sh" + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +plan_tests $NUM_TESTS + +function kernel_test_app() +{ + /bin/echo -n "$NR_ITER" > "$KERNEL_TEST_FILE_NAME" +} + +function kernel_syscall_test_app() +{ + for i in $(seq 1 $NR_ITER) + do + "$TESTAPP_SYSCALL_BIN" /dev/null /proc/cpuinfo /proc/cmdline + done +} + +function kernel_userspace_test_app() +{ + for i in $(seq 1 $NR_ITER) + do + "$TESTAPP_USERSPACE_BINARY" + done +} + +function test_map_kernel_create() +{ + local MAP_NAME="my_map_name" + local MAP_NAME_2="my_map_name2" + local MAP_NAME_3="my_map_name3" + local MAP_NAME_4="my_map_name4" + local SESSION_NAME="my_session_name" + + create_lttng_session_ok "$SESSION_NAME" + + "$FULL_LTTNG_BIN" add-map --kernel --bitness 32 --session "wrong_session_name" "$MAP_NAME" > /dev/null + isnt $? 0 "Map creation failed on wrong session name" + + "$FULL_LTTNG_BIN" add-map --kernel --bitness 42 --session "$SESSION_NAME" "$MAP_NAME" > /dev/null + isnt $? 0 "Map creation failed \"--bitness\" wrong value as expected" + + "$FULL_LTTNG_BIN" add-map --kernel --session "SESS_DOESNT_EXIST" "$MAP_NAME" > /dev/null + isnt $? 0 "Failed to add map to session that doesn't exist" + + "$FULL_LTTNG_BIN" disable-map --kernel "MAP_DOESNT_EXIST" > /dev/null + isnt $? 0 "Failed to disable map that doesn't exist" + + "$FULL_LTTNG_BIN" add-map --kernel --bitness 64 --session "$SESSION_NAME" "$MAP_NAME" > /dev/null + ok $? "Map with 64bit bitness created succesfully" + + "$FULL_LTTNG_BIN" disable-map --kernel "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" add-map --kernel --bitness 32 --session "$SESSION_NAME" "$MAP_NAME_2" > /dev/null + ok $? "Map with 32bit bitness created succesfully" + + "$FULL_LTTNG_BIN" add-map --kernel --session "$SESSION_NAME" "$MAP_NAME_2" > /dev/null + isnt $? 0 "Duplicated map fails to create as expected" + + "$FULL_LTTNG_BIN" disable-map --kernel "$MAP_NAME_2" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" add-map --kernel --session "$SESSION_NAME" "$MAP_NAME_3" > /dev/null + ok $? "Map with default bitness created succesfully" + + "$FULL_LTTNG_BIN" disable-map --kernel "$MAP_NAME_3" > /dev/null + ok $? "Map removed succesfully" + + "$FULL_LTTNG_BIN" add-map --kernel --session "$SESSION_NAME" --max-key-count 212 "$MAP_NAME_4" > /dev/null + ok $? "Map with max key count created succesfully" + + "$FULL_LTTNG_BIN" disable-map --kernel "$MAP_NAME_4" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" enable-map --kernel "$MAP_NAME_4" > /dev/null + ok $? "Map enabled succesfully" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_kernel_probe() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME_1="my_trigger_name_1" + local TRIGGER_NAME_2="my_trigger_name_2" + local TARGET_SYMBOL="lttng_test_filter_event_write" + local event_name_1="my_probe1" + local event_name_2="my_probe2" + local KEY="foo" + + diag "Test map kernel probe" + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "--kernel" "32" "" + + lttng_add_trigger_ok "$TRIGGER_NAME_1" \ + --condition \ + on-event --kernel --probe="$TARGET_SYMBOL" "$event_name_1" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + lttng_add_trigger_ok "$TRIGGER_NAME_2" \ + --condition \ + on-event --kernel --probe="$TARGET_SYMBOL" "$event_name_2" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "\${EVENT_NAME}" + + start_lttng_tracing_ok $SESSION_NAME + + kernel_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "1" + view_map_ok "$MAP_NAME" "$event_name_2" "1" + + lttng_remove_trigger_ok "$TRIGGER_NAME_1" + lttng_remove_trigger_ok "$TRIGGER_NAME_2" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_kernel_function() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name_1" + local TARGET_SYMBOL="lttng_test_filter_event_write" + local event_name="my_probe1" + local KEY="foo" + + diag "Test map kernel function" + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "--kernel" "32" "" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --kernel --function="$TARGET_SYMBOL" "$event_name" \ + --action incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" \ + --action incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "\${EVENT_NAME}" + + start_lttng_tracing_ok $SESSION_NAME + + kernel_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "${KEY}" "2" + view_map_ok "$MAP_NAME" "${event_name}_entry" "1" + view_map_ok "$MAP_NAME" "${event_name}_return" "1" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_kernel_syscall() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME_1="my_trigger_name_1" + local TRIGGER_NAME_2="my_trigger_name_2" + local FILE_1="/proc/cmdline" + local FILE_2="/proc/cpuinfo" + local TARGET_SYSCALL=openat + local KEY="foo" + + diag "Test map kernel syscall" + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "--kernel" "32" "" + + lttng_add_trigger_ok "$TRIGGER_NAME_1" \ + --condition \ + on-event --kernel --syscall "$TARGET_SYSCALL" --filter "filename == \"$FILE_1\"" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + lttng_add_trigger_ok "$TRIGGER_NAME_2" \ + --condition \ + on-event --kernel --syscall "$TARGET_SYSCALL" --filter "filename == \"$FILE_2\"" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "\${EVENT_NAME}" + + start_lttng_tracing_ok $SESSION_NAME + + kernel_syscall_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + # When using filtering on filename we only get hits on the entry event + # as it's the one having the filename argument. + view_map_ok "$MAP_NAME" "${KEY}" "$NR_ITER" + view_map_ok "$MAP_NAME" "syscall_entry_$TARGET_SYSCALL" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME_1" + lttng_remove_trigger_ok "$TRIGGER_NAME_2" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_kernel_userspace_probe() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME_1="my_trigger_name_1" + local TEST_FUNCTION="test_function" + local KEY="foo" + + diag "Test map kernel userspace probe" + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "--kernel" "32" "" + + lttng_add_trigger_ok "$TRIGGER_NAME_1" \ + --condition \ + on-event --kernel --userspace-probe="elf:$TESTAPP_USERSPACE_BINARY:$TEST_FUNCTION" event_name \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + kernel_userspace_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME_1" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +if [ "$(id -u)" == "0" ]; then + + start_lttng_sessiond_notap + + validate_lttng_modules_present + + modprobe lttng-test + + test_map_kernel_create + + test_map_formated_keys "--kernel" "$KERNEL_EVENT_NAME" "\${EVENT_NAME}" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_formated_keys "--kernel" "$KERNEL_EVENT_NAME" "pitarifique-\${EVENT_NAME}" "pitarifique-$KERNEL_EVENT_NAME" kernel_test_app + + test_map_view_empty "--kernel" "64" "" + test_map_view_empty "--kernel" "32" "" + + test_map_n_triggers_n_keys "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_n_triggers_n_keys "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_n_triggers_1_key "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_n_triggers_1_key "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_n_triggers_1_key_coalesced "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_n_triggers_1_key_coalesced "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_disable_enable "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_disable_enable "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_add_remove_add_trigger "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_add_remove_add_trigger "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_creation_after_trigger "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_creation_after_trigger "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_remove_trigger_before_stop "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_remove_trigger_before_stop "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_two_incr_value_two_keys "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_clear "--kernel" 32 "$KERNEL_EVENT_NAME" kernel_test_app + test_map_clear "--kernel" 64 "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_kernel_probe + test_map_kernel_function + + test_map_kernel_syscall + + test_map_kernel_userspace_probe + + test_map_filter "--kernel" "$KERNEL_EVENT_NAME" "intfield" kernel_test_app + + modprobe --remove lttng-test + + stop_lttng_sessiond_notap +else + # Kernel tests are skipped. + skip 0 "Root access is needed. Skipping all kernel notification tests." $KERNEL_NUM_TESTS +fi + + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/map/test_map_query.c b/tests/regression/tools/map/test_map_query.c new file mode 100644 index 000000000..04e868961 --- /dev/null +++ b/tests/regression/tools/map/test_map_query.c @@ -0,0 +1,125 @@ +#include +#include + +#include +#include +#include +#include + +int main() { + int ret; + enum lttng_map_query_status query_status; + enum lttng_error_code ret_code; + unsigned int map_count, list_count; + enum lttng_map_status map_status; + struct lttng_map_content *map_content = NULL; + struct lttng_map_list *map_list = NULL; + const struct lttng_map *map = NULL; + struct lttng_domain *domains = NULL; + const struct lttng_map_key_value_pair_list *kv_list; + const struct lttng_map_key_value_pair *kv_pair; + const char *key; + int64_t value; + int nb_domains; + + + nb_domains = lttng_list_domains("mysession", &domains); + + struct lttng_handle *handle = lttng_create_handle("mysession", &domains[0]); + + struct lttng_map_query *map_query = lttng_map_query_create( + LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET, + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL, + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL); + + if (!map_query) { + printf("Error creating the map query\n"); + ret = -1; + goto end; + } + + query_status = lttng_map_query_add_cpu(map_query, 0); + if (query_status != LTTNG_MAP_QUERY_STATUS_OK) { + printf("Error setting the targeted cpu\n"); + ret = -1; + goto end; + } + + query_status = lttng_map_query_add_key_filter(map_query, + "total number of hits"); + if (query_status != LTTNG_MAP_QUERY_STATUS_OK) { + printf("Error setting the targeted key\n"); + ret = -1; + goto end; + } + + ret_code = lttng_list_maps(handle, &map_list); + if (ret_code != LTTNG_OK) { + printf("Error getting list of all maps\n"); + ret = -1; + goto end; + } + + map_status = lttng_map_list_get_count(map_list, &map_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + printf("Error getting the number of maps\n"); + ret = -1; + goto end; + } + + if (map_count < 1) { + printf("Error: expecting at least 1 map.\n"); + ret = -1; + goto end; + } + + map = lttng_map_list_get_at_index(map_list, 0); + if (!map) { + printf("Error getting map at index 0\n"); + ret = -1; + goto end; + } + + ret_code = lttng_list_map_content(handle, map, map_query, &map_content); + if (ret_code != LTTNG_OK) { + printf("Error executing the query on map\n"); + ret = -1; + goto end; + } + + map_status = lttng_map_content_get_count(map_content, &list_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + printf("Error getting the number of key value pair list\n"); + ret = -1; + goto end; + } + + if (list_count < 1) { + printf("Error: expecting at least 1 list.\n"); + ret = -1; + goto end; + } + + kv_list = lttng_map_content_get_at_index(map_content, 0); + if (!kv_list) { + printf("Error getting key value pair list at index 0\n"); + ret = -1; + goto end; + } + + kv_pair = lttng_map_key_value_pair_list_get_at_index(kv_list, 0); + if (!kv_pair) { + printf("Error getting key value pair at index 0\n"); + ret = -1; + goto end; + } + + lttng_map_key_value_pair_get_key(kv_pair, &key); + lttng_map_key_value_pair_get_value(kv_pair, &value); + + printf("Key: \"%s\", value: %"PRId64"\n", key, value); + + ret = 0; +end: + return ret; +} diff --git a/tests/regression/tools/map/test_map_ust b/tests/regression/tools/map/test_map_ust new file mode 100755 index 000000000..b6c21c240 --- /dev/null +++ b/tests/regression/tools/map/test_map_ust @@ -0,0 +1,416 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../.. +NR_ITER=5 +NR_USEC_WAIT=1 +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" + +GEN_UST_NEVENTS_BIN="$TESTAPP_PATH/gen-ust-nevents/gen-ust-nevents" + +UST_EVENT_NAME="tp:tptest" + +UST_NUM_TESTS=474 +NUM_TESTS=$(($UST_NUM_TESTS)) + +TMPDIR=$(mktemp -d) + +SH_TAP=1 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +source "$CURDIR/map_base_test.sh" + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +if [ ! -x "$TESTAPP_BIN" ]; then + BAIL_OUT "No UST events binary detected." +fi + +plan_tests $NUM_TESTS + + +function ust_test_app() +{ + $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT +} + +function test_map_ust_per_uid_create() +{ + local MAP_NAME="my_map_name" + local MAP_NAME_2="my_map_name2" + local MAP_NAME_3="my_map_name3" + local SESSION_NAME="my_session_name" + + diag "Map creation" + + create_lttng_session_ok "$SESSION_NAME" + + "$FULL_LTTNG_BIN" add-map --userspace --bitness 32 --session "wrong_session_name" "$MAP_NAME" > /dev/null + isnt $? 0 "Map creation failed on wrong session name" + + "$FULL_LTTNG_BIN" add-map --userspace --bitness 42 --session "$SESSION_NAME" "$MAP_NAME" > /dev/null + isnt $? 0 "Map creation failed \"--bitness\" wrong value as expected" + + "$FULL_LTTNG_BIN" add-map --userspace --session "SESS_DOESNT_EXIST" "$MAP_NAME" > /dev/null + isnt $? 0 "Failed to add map to session that doesn't exist" + + "$FULL_LTTNG_BIN" disable-map --userspace --session "$SESSION_NAME" "MAP_DOESNT_EXIST" > /dev/null + isnt $? 0 "Failed to disable map that doesn't exist" + + "$FULL_LTTNG_BIN" add-map --userspace --bitness 64 --session "$SESSION_NAME" "$MAP_NAME" > /dev/null + ok $? "Map with 64bit bitness created succesfully" + + "$FULL_LTTNG_BIN" disable-map --userspace "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_ust_per_pid_create() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + + diag "Map per-pid creation" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" --userspace 64 --per-pid + + "$FULL_LTTNG_BIN" disable-map --userspace "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" enable-map --userspace "$MAP_NAME" > /dev/null + ok $? "Map enabled succesfully" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_base_scenario() +{ + local domain="$1" + local bitness="$2" + local buf_option="$3" + local event_name="$4" + + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + + diag "Map base tracing scenario: $domain bitness $bitness $buf_option" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + ust_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_ust_two_apps() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="--userspace" + local bitness="64" + local buf_option="--per-uid" + + diag "Map with two apps" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --userspace "$UST_EVENT_NAME" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + ust_test_app + ust_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER * 2))" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_ust_with_events() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="--userspace" + local bitness="64" + local buf_option="--per-uid" + + diag "Map with regular events" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + enable_ust_lttng_event_ok "$SESSION_NAME" "*" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --userspace "$UST_EVENT_NAME" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + ust_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_ust_per_pid_dead_app_aggregation() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME1="my_trigger_name1" + local TRIGGER_NAME2="my_trigger_name2" + local KEY1="foo" + local KEY2="ashton" + local domain="--userspace" + local bitness="64" + local buf_option="--per-pid" + + local file_sync_before_exit=$(mktemp -u) + local file_sync_before_exit_touch=$(mktemp -u) + + # In per-pid, test that running apps and dead apps aggrgated values are + # listed in their own map. + + diag "Map per-pid user with dead app aggregation" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME1" \ + --condition \ + on-event --userspace "tp:tptest1" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY1" + + lttng_add_trigger_ok "$TRIGGER_NAME2" \ + --condition \ + on-event --userspace "$UST_EVENT_NAME" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY2" + + start_lttng_tracing_ok $SESSION_NAME + + # Two apps will have run completely when we call view-map. + $GEN_UST_NEVENTS_BIN -i $NR_ITER -w $NR_USEC_WAIT + $GEN_UST_NEVENTS_BIN -i $NR_ITER -w $NR_USEC_WAIT + + # One app will be done generating events but is still running when we + # call view-map. + + $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT \ + --sync-before-exit-touch $file_sync_before_exit_touch \ + --sync-before-exit $file_sync_before_exit & + + # Wait for the before exit sync point. This ensure that we went over the + # last tracepoint. + while [ ! -f "${file_sync_before_exit_touch}" ]; do + sleep 0.1 + done + + stop_lttng_tracing_ok $SESSION_NAME + + # After the apps are dead, we should see map key value pairs in the + # dead map aggregation listing. Two apps ran and exited, so we should + # have NR_ITER * 2 hits. + view_map_ok "$MAP_NAME" "$KEY1" "$(( $NR_ITER * 2 ))" + + # One app is still running and is done generating events, we should see + # NR_ITER hits. + view_map_ok "$MAP_NAME" "$KEY2" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME1" + lttng_remove_trigger_ok "$TRIGGER_NAME2" + + touch "$file_sync_before_exit" + wait + + destroy_lttng_session_ok $SESSION_NAME + + rm -f ${file_sync_before_exit} + rm -f ${file_sync_before_exit_touch} +} + +function test_map_ust_exclusion() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="--userspace" + local bitness="64" + local buf_option="--per-uid" + local exclusion="tp:tptest1,tp:tptest2,tp:tptest3,tp:tptest4" + + diag "Map per-pid user with dead app aggregation" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --userspace "tp:tptest*" --exclude="$exclusion" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$GEN_UST_NEVENTS_BIN" -i "$NR_ITER" -w 1 + + stop_lttng_tracing_ok $SESSION_NAME + + # After the map is dead, we should still see map key value pairs in the + # dead map aggregation listing. + view_map_ok "$MAP_NAME" "$KEY" "5" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + + +function test_map_ust_clear_per_pid() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="--userspace" + local bitness="64" + local buf_option="--per-pid" + + diag "Map UST per-pid clear" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --userspace "tp:tptest1" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$GEN_UST_NEVENTS_BIN" -i "$NR_ITER" -w 1 + + stop_lttng_tracing_ok $SESSION_NAME + + # After the map is dead, we should still see map key value pairs in the + # dead map aggregation listing. + view_map_ok "$MAP_NAME" "$KEY" "5" + + lttng_clear_session_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "0" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +start_lttng_sessiond_notap + +test_map_ust_per_pid_dead_app_aggregation +test_map_n_triggers_n_keys "--userspace" "64" "$UST_EVENT_NAME" ust_test_app +test_map_n_triggers_n_keys "--userspace" "32" "$UST_EVENT_NAME" ust_test_app + +test_map_n_triggers_1_key "--userspace" "64" "$UST_EVENT_NAME" ust_test_app +test_map_n_triggers_1_key "--userspace" "32" "$UST_EVENT_NAME" ust_test_app + +test_map_n_triggers_1_key_coalesced "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_n_triggers_1_key_coalesced "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_ust_per_uid_create +test_map_ust_per_pid_create + +test_map_view_empty "--userspace" "64" "--per-uid" +test_map_view_empty "--userspace" "64" "--per-pid" +test_map_view_empty "--userspace" "32" "--per-uid" +test_map_view_empty "--userspace" "32" "--per-pid" + +test_map_base_scenario "--userspace" "64" "--per-uid" "$UST_EVENT_NAME" +test_map_base_scenario "--userspace" "32" "--per-uid" "$UST_EVENT_NAME" +test_map_base_scenario "--userspace" "64" "--per-pid" "$UST_EVENT_NAME" +test_map_base_scenario "--userspace" "32" "--per-pid" "$UST_EVENT_NAME" + +test_map_formated_keys "--userspace" "$UST_EVENT_NAME" "\${PROVIDER_NAME}:\${EVENT_NAME}" "$UST_EVENT_NAME" ust_test_app +test_map_formated_keys "--userspace" "$UST_EVENT_NAME" "pitarifique-\${EVENT_NAME}" "pitarifique-tptest" ust_test_app + +test_map_disable_enable "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_disable_enable "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_add_remove_add_trigger "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_add_remove_add_trigger "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_creation_after_trigger "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_creation_after_trigger "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_remove_trigger_before_stop "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_remove_trigger_before_stop "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_ust_two_apps +test_map_ust_with_events +test_map_two_incr_value_two_keys "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_clear "--userspace" 32 "$UST_EVENT_NAME" ust_test_app +test_map_clear "--userspace" 64 "$UST_EVENT_NAME" ust_test_app +test_map_ust_clear_per_pid + +test_map_ust_exclusion + +test_map_filter "--userspace" "$UST_EVENT_NAME" "intfield" ust_test_app + +stop_lttng_sessiond_notap + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/Makefile.am b/tests/regression/tools/notification/Makefile.am index 3aca442f2..0dceea572 100644 --- a/tests/regression/tools/notification/Makefile.am +++ b/tests/regression/tools/notification/Makefile.am @@ -9,18 +9,22 @@ noinst_PROGRAMS = base_client notification rotation if NO_SHARED -CLEANFILES = libpause_consumer.so libpause_consumer.so.debug +CLEANFILES = libpause_consumer.so libpause_consumer.so.debug libpause_sessiond.so libpause_sessiond.so.debug EXTRA_DIST = \ base_client.c \ consumer_testpoints.c \ + sessiond_testpoints.c \ notification.c \ test_notification_kernel_buffer_usage \ + test_notification_kernel_capture \ test_notification_kernel_error \ test_notification_kernel_instrumentation \ test_notification_kernel_syscall \ test_notification_kernel_userspace_probe \ test_notification_multi_app \ + test_notification_notifier_discarded_count \ test_notification_ust_buffer_usage \ + test_notification_ust_capture \ test_notification_ust_error \ test_notification_ust_event_rule_condition_exclusion \ util_event_generator.sh @@ -38,7 +42,14 @@ libpause_consumer_la_LIBADD = \ $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ $(DL_LIBS) libpause_consumer_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS) -noinst_LTLIBRARIES = libpause_consumer.la + +libpause_sessiond_la_SOURCES = sessiond_testpoints.c +libpause_sessiond_la_LIBADD = \ + $(top_builddir)/src/common/libcommon.la \ + $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ + $(DL_LIBS) +libpause_sessiond_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS) +noinst_LTLIBRARIES = libpause_sessiond.la libpause_consumer.la base_client_SOURCES = base_client.c base_client_LDADD = $(LIB_LTTNG_CTL) @@ -56,6 +67,7 @@ noinst_SCRIPTS = \ test_notification_kernel_syscall \ test_notification_kernel_userspace_probe \ test_notification_multi_app \ + test_notification_notifier_discarded_count \ test_notification_ust_buffer_usage \ test_notification_ust_error \ test_notification_ust_event_rule_condition_exclusion \ @@ -64,12 +76,15 @@ noinst_SCRIPTS = \ EXTRA_DIST = \ test_notification_kernel_buffer_usage \ + test_notification_kernel_capture \ test_notification_kernel_error \ test_notification_kernel_instrumentation \ test_notification_kernel_syscall \ test_notification_kernel_userspace_probe \ test_notification_multi_app \ + test_notification_notifier_discarded_count \ test_notification_ust_buffer_usage \ + test_notification_ust_capture \ test_notification_ust_error \ test_notification_ust_event_rule_condition_exclusion \ test_rotation \ diff --git a/tests/regression/tools/notification/base_client.c b/tests/regression/tools/notification/base_client.c index 70ad763ab..c215b5cf4 100644 --- a/tests/regression/tools/notification/base_client.c +++ b/tests/regression/tools/notification/base_client.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,7 @@ static const char *channel_name = NULL; static double threshold_ratio = 0.0; static uint64_t threshold_bytes = 0; static bool is_threshold_ratio = false; +static bool use_action_group = false; static enum lttng_condition_type buffer_usage_type = LTTNG_CONDITION_TYPE_UNKNOWN; static enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; @@ -50,6 +52,7 @@ int parse_arguments(char **argv) const char *buffer_usage_threshold_type = NULL; const char *buffer_usage_threshold_value = NULL; const char *nr_expected_notifications_string = NULL; + const char *use_action_group_value = NULL; session_name = argv[1]; channel_name = argv[2]; @@ -58,6 +61,7 @@ int parse_arguments(char **argv) buffer_usage_threshold_type = argv[5]; buffer_usage_threshold_value = argv[6]; nr_expected_notifications_string = argv[7]; + use_action_group_value = argv[8]; /* Parse arguments */ /* Domain type */ @@ -98,6 +102,11 @@ int parse_arguments(char **argv) /* Number of notification to expect */ sscanf(nr_expected_notifications_string, "%d", &nr_expected_notifications); + /* Put notify action in a group. */ + if (!strcasecmp("1", use_action_group_value)) { + use_action_group = true; + } + return 0; error: return 1; @@ -107,6 +116,7 @@ int main(int argc, char **argv) { int ret = 0; enum lttng_condition_status condition_status; + enum lttng_action_status action_status; enum lttng_notification_channel_status nc_status; struct lttng_notification_channel *notification_channel = NULL; struct lttng_condition *condition = NULL; @@ -120,7 +130,7 @@ int main(int argc, char **argv) */ setbuf(stdout, NULL); - if (argc < 8) { + if (argc < 9) { printf("error: Missing arguments for tests\n"); ret = 1; goto end; @@ -196,11 +206,39 @@ int main(int argc, char **argv) goto end; } - action = lttng_action_notify_create(); - if (!action) { - printf("error: Could not create action notify\n"); - ret = 1; - goto end; + if (use_action_group) { + struct lttng_action *notify, *group; + + group = lttng_action_group_create(); + if (!group) { + printf("error: Could not create action group\n"); + ret = 1; + goto end; + } + notify = lttng_action_notify_create(); + if (!notify) { + lttng_action_destroy(group); + printf("error: Could not create action notify\n"); + ret = 1; + goto end; + } + action_status = lttng_action_group_add_action(group, notify); + if (action_status != LTTNG_ACTION_STATUS_OK) { + printf("error: Could not add action notify to action group\n"); + lttng_action_destroy(group); + lttng_action_destroy(notify); + ret = 1; + goto end; + } + + action = group; + } else { + action = lttng_action_notify_create(); + if (!action) { + printf("error: Could not create action notify\n"); + ret = 1; + goto end; + } } trigger = lttng_trigger_create(condition, action); @@ -265,7 +303,7 @@ int main(int argc, char **argv) goto end; default: /* Unhandled conditions / errors. */ - printf("error: Unknown notification channel status\n"); + printf("error: Unknown notification channel status (%d) \n", status); ret = 1; goto end; } diff --git a/tests/regression/tools/notification/notification.c b/tests/regression/tools/notification/notification.c index 73415c51e..28c8e58ce 100644 --- a/tests/regression/tools/notification/notification.c +++ b/tests/regression/tools/notification/notification.c @@ -28,11 +28,553 @@ #include +/* A callback to populate the condition capture descriptor */ +typedef int (*condition_capture_desc_cb)(struct lttng_condition *condition); + +/* A callback for captured field validation */ +typedef int (*validate_cb)(const struct lttng_event_field_value *event_field, unsigned iteration); + int nb_args = 0; int named_pipe_args_start = 0; pid_t app_pid = 0; const char *app_state_file = NULL; +enum field_type { + FIELD_TYPE_PAYLOAD, + FIELD_TYPE_CONTEXT, + FIELD_TYPE_APP_CONTEXT, + FIELD_TYPE_ARRAY_FIELD, +}; + +struct capture_base_field_tuple { + char* field_name; + enum field_type field_type; + bool expected_ust; // Do we expect a capture? + bool expected_kernel; // Do we expect a capture? + validate_cb validate_ust; + validate_cb validate_kernel; +}; + +static +const char *field_value_type_to_str(enum lttng_event_field_value_type type) +{ + switch (type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN: + return "UNKNOWN"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID: + return "INVALID"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: + return "UNSIGNED INT"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: + return "SIGNED INT"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + return "UNSIGNED ENUM"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + return "SIGNED ENUM"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL: + return "REAL"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: + return "STRING"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: + return "ARRAY"; + default: + abort(); + } +} + +static int validate_type( + const struct lttng_event_field_value *event_field, + enum lttng_event_field_value_type expect) +{ + int ret; + enum lttng_event_field_value_type value; + + value = lttng_event_field_value_get_type(event_field); + if (value == LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID) { + ret = 1; + goto end; + } + + ret = (expect == value); + ok(ret, "Expected field type: %s got %s", + field_value_type_to_str(expect), + field_value_type_to_str(value)); + + ret = !ret; + +end: + return ret; +} + +/* + * Validate unsigned captured field against the iteration number. + * The iteration number is always unsigned and will always be compared to value + * under MAX_UINT. + */ +static int validate_unsigned_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + uint64_t value; + enum lttng_event_field_value_status status; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field , &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ret = (value == (uint64_t) iteration); + ok (ret, "Expected unsigned int of value: %u got %" PRIu64, iteration, value); + + ret = !ret; + +end: + + return ret; +} + +/* + * Validate signed captured field. + * Value should be -1. + */ +static int validate_signed_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + int64_t expected = -1; + int64_t value; + enum lttng_event_field_value_status status; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_signed_int_get_value( + event_field , &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_signed_int_get_value"); + ret = 1; + goto end; + } + + ret = (value == expected); + ok(ret, "Expected signed int of value: %" PRId64 " got %" PRId64, expected, value); + + ret = !ret; + +end: + + return ret; +} + +/* + * Validate array of unsigned int. + */ +static int validate_array_unsigned_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + unsigned int expected = 3; + unsigned int count; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); + if (ret) { + goto end; + } + + status = lttng_event_field_value_array_get_length(event_field, &count); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; + } + + ret = (count == expected); + ok(ret, "Expected %d subelements got %d", expected, count); + if (!ret) { + ret = 1; + goto end; + } + + for (unsigned int i = 1; i < count + 1; i++) { + const struct lttng_event_field_value *value; + status = lttng_event_field_value_array_get_element_at_index( + event_field, i - 1, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_element_at_index"); + ret = 1; + goto end; + } + ret = validate_unsigned_int_field(value, i); + if (ret) { + goto end; + } + } + + ret = 0; +end: + + return ret; +} +static int validate_array_unsigned_int_field_at_index( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + uint64_t expected_value = 2; + enum lttng_event_field_value_status status; + uint64_t value; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field , &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ret = (value == expected_value); + ok (ret, "Expected unsigned int of value: %u got %" PRIu64, + expected_value, value); + + ret = 0; +end: + return ret; +} + +/* + * Validate sequence for a string (seqfield1): + * + * Value: "test" in utf8 [116, 101, 115, 116] + */ +static int validate_seqfield1( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + unsigned int count; + unsigned int expect[4] = {116, 101, 115, 116}; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); + if (ret) { + goto end; + } + + status = lttng_event_field_value_array_get_length(event_field, &count); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; + } + + ret = (count == 4); + ok(ret, "Expected 4 subelement got %d", count); + if (!ret) { + ret = 1; + goto end; + } + + for (unsigned int i = 0; i < count ; i++) { + const struct lttng_event_field_value *value; + status = lttng_event_field_value_array_get_element_at_index( + event_field, i, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_element_at_index"); + ret = 1; + goto end; + } + ret = validate_unsigned_int_field(value, expect[i]); + if (ret) { + goto end; + } + } + + ret = 0; +end: + + return ret; +} + +static int validate_string( + const struct lttng_event_field_value *event_field, + const char *expect) +{ + int ret; + const char *value = NULL; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_STRING); + if (ret) { + goto end; + } + + value = lttng_event_field_value_string_get_value(event_field); + if (!value) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; + } + + ok(!strcmp(value, expect), "Expected string: \"%s\" got \"%s\"", expect, value); + + ret = 0; +end: + + return ret; +} + +/* + * Validate string. Expected value is "test". + */ +static int validate_string_test( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + const char *expect = "test"; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, expect); + return ret; +} + +/* + * Validate escaped string. Expected value is "\*". + */ +static int validate_string_escaped( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + const char *expect = "\\*"; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, expect); + return ret; +} + +/* + * Validate real field. + */ +static int validate_real( + const struct lttng_event_field_value *event_field, + double expect) +{ + int ret; + double value; + enum lttng_event_field_value_status status; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_REAL); + if (ret) { + goto end; + } + + status = lttng_event_field_value_real_get_value(event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_real_get_value"); + ret = 1; + goto end; + } + + ret = (value == expect); + ok(ret, "Real expected: %f got: %f", expect, value); + + ret = !ret; +end: + return ret; +} + +/* + * Validate floatfield. + */ +static int validate_floatfield( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + double expect = 2222.0; + + /* Unused */ + (void) iteration; + + ret = validate_real(event_field, expect); + return ret; +} + +/* + * Validate doublefield. + */ +static int validate_doublefield( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + double expect = 2.0; + + /* Unused */ + (void) iteration; + + ret = validate_real(event_field, expect); + return ret; +} + +/* + * Validate enum0: enum0 = ( "AUTO: EXPECT 0" : container = 0 ) + */ +static int validate_enum0(const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + uint64_t value; + uint64_t expected_value = 0; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ok(value == expected_value, + "Enum value expected: %" PRIu64 " got %" PRIu64, + expected_value, value); + +end: + return ret; +} + +/* + * Validate enumnegative: enumnegative = ( "AUTO: EXPECT 0" : container = 0 ) + * + * We expect 2 labels here. + */ +static int validate_enumnegative( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + int64_t value; + int64_t expected_value = -1; + + /* Unused */ + (void) iteration; + + ret = validate_type( + event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM); + if (ret) { + goto end; + } + + status = lttng_event_field_value_signed_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ok(value == expected_value, + "Enum value expected: %" PRId64 " got %" PRId64, + expected_value, value); + +end: + return ret; +} + +static int validate_context_procname_ust( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, "gen-ust-events"); + return ret; +} + +static int validate_context_procname_kernel( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, "echo"); + return ret; +} + +struct capture_base_field_tuple test_capture_base_fields[] = { + {"DOESNOTEXIST", FIELD_TYPE_PAYLOAD, false, false, NULL, NULL}, + {"intfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field}, + {"longfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field}, + {"signedfield", FIELD_TYPE_PAYLOAD, true, true, validate_signed_int_field, validate_signed_int_field}, + {"arrfield1", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"arrfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test}, + {"arrfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"seqfield1", FIELD_TYPE_PAYLOAD, true, true, validate_seqfield1, validate_seqfield1}, + {"seqfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test}, + {"seqfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"seqfield4", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"arrfield1[1]", FIELD_TYPE_ARRAY_FIELD, true, true, validate_array_unsigned_int_field_at_index, validate_array_unsigned_int_field_at_index}, + {"stringfield", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test}, + {"stringfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_escaped, validate_string_escaped}, + {"floatfield", FIELD_TYPE_PAYLOAD, true, false, validate_floatfield, validate_floatfield}, + {"doublefield", FIELD_TYPE_PAYLOAD, true, false, validate_doublefield, validate_doublefield}, + {"enum0", FIELD_TYPE_PAYLOAD, true, true, validate_enum0, validate_enum0}, + {"enumnegative", FIELD_TYPE_PAYLOAD, true, true, validate_enumnegative, validate_enumnegative}, + {"$ctx.procname", FIELD_TYPE_CONTEXT, true, true, validate_context_procname_ust, validate_context_procname_kernel}, +}; + static const char *get_notification_trigger_name( struct lttng_notification *notification) { @@ -46,7 +588,7 @@ static const char *get_notification_trigger_name( } switch (lttng_evaluation_get_type(evaluation)) { - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: { status = lttng_evaluation_event_rule_get_trigger_name( evaluation, &name); @@ -904,6 +1446,7 @@ static void create_tracepoint_event_rule_trigger(const char *event_pattern, unsigned int exclusion_count, const char * const *exclusions, enum lttng_domain_type domain_type, + condition_capture_desc_cb capture_desc_cb, struct lttng_condition **condition, struct lttng_trigger **trigger) { @@ -960,9 +1503,16 @@ static void create_tracepoint_event_rule_trigger(const char *event_pattern, ok(success, "Setting tracepoint event rule exclusions"); } - tmp_condition = lttng_condition_event_rule_create(event_rule); + tmp_condition = lttng_condition_on_event_create(event_rule); ok(tmp_condition, "Condition event rule object creation"); + if (capture_desc_cb) { + ret = capture_desc_cb(tmp_condition); + if (ret) { + assert("Generating the condition capture descriptor"); + } + } + tmp_action = lttng_action_notify_create(); ok(tmp_action, "Action event rule object creation"); @@ -1033,7 +1583,7 @@ static void test_tracepoint_event_rule_notification( } create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, - NULL, domain_type, &condition, &trigger); + NULL, domain_type, NULL, &condition, &trigger); notification_channel = lttng_notification_channel_create( lttng_session_daemon_notification_endpoint); @@ -1101,7 +1651,7 @@ static void test_tracepoint_event_rule_notification_filter( ok(notification_channel, "Notification channel object creation"); create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, - 0, NULL, domain_type, &ctrl_condition, &ctrl_trigger); + 0, NULL, domain_type, NULL, &ctrl_condition, &ctrl_trigger); nc_status = lttng_notification_channel_subscribe( notification_channel, ctrl_condition); @@ -1113,7 +1663,7 @@ static void test_tracepoint_event_rule_notification_filter( * `intfield` is even. */ create_tracepoint_event_rule_trigger(pattern, trigger_name, - "(intfield & 1) == 0", 0, NULL, domain_type, &condition, + "(intfield & 1) == 0", 0, NULL, domain_type, NULL, &condition, &trigger); nc_status = lttng_notification_channel_subscribe( @@ -1201,7 +1751,8 @@ static void test_tracepoint_event_rule_notification_exclusion( ok(notification_channel, "Notification channel object creation"); create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, - 0, NULL, domain_type, &ctrl_condition, &ctrl_trigger); + 0, NULL, domain_type, NULL, &ctrl_condition, + &ctrl_trigger); nc_status = lttng_notification_channel_subscribe( notification_channel, ctrl_condition); @@ -1209,7 +1760,8 @@ static void test_tracepoint_event_rule_notification_exclusion( "Subscribe to tracepoint event rule condition"); create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 4, - exclusions, domain_type, &condition, &trigger); + exclusions, domain_type, NULL, &condition, + &trigger); nc_status = lttng_notification_channel_subscribe( notification_channel, condition); @@ -1310,20 +1862,15 @@ static void test_kprobe_event_rule_notification( lttng_session_daemon_notification_endpoint); ok(notification_channel, "Notification channel object creation"); - event_rule = lttng_event_rule_kprobe_create(); + event_rule = lttng_event_rule_kernel_probe_create(location); ok(event_rule, "kprobe event rule object creation"); - event_rule_status = lttng_event_rule_kprobe_set_location( - event_rule, location); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting kprobe event rule location: '%s'", symbol_name); - - event_rule_status = lttng_event_rule_kprobe_set_name( + event_rule_status = lttng_event_rule_kernel_probe_set_event_name( event_rule, trigger_name); ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, "Setting kprobe event rule name: '%s'", trigger_name); - condition = lttng_condition_event_rule_create(event_rule); + condition = lttng_condition_on_event_create(event_rule); ok(condition, "Condition event rule object creation"); /* Register the trigger for condition. */ @@ -1424,20 +1971,15 @@ static void test_uprobe_event_rule_notification( lttng_session_daemon_notification_endpoint); ok(notification_channel, "Notification channel object creation"); - event_rule = lttng_event_rule_uprobe_create(); + event_rule = lttng_event_rule_userspace_probe_create(probe_location); ok(event_rule, "kprobe event rule object creation"); - event_rule_status = lttng_event_rule_uprobe_set_location( - event_rule, probe_location); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting uprobe event rule location"); - - event_rule_status = lttng_event_rule_uprobe_set_name( + event_rule_status = lttng_event_rule_userspace_probe_set_event_name( event_rule, trigger_name); ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, "Setting uprobe event rule name: '%s'", trigger_name); - condition = lttng_condition_event_rule_create(event_rule); + condition = lttng_condition_on_event_create(event_rule); ok(condition, "Condition event rule object creation"); /* Register the trigger for condition. */ @@ -1529,7 +2071,7 @@ static void test_syscall_event_rule_notification( ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, "Setting syscall event rule pattern: '%s'", syscall_name); - condition = lttng_condition_event_rule_create(event_rule); + condition = lttng_condition_on_event_create(event_rule); ok(condition, "Condition syscall event rule object creation"); /* Register the trigger for condition. */ @@ -1624,7 +2166,7 @@ static void test_syscall_event_rule_notification_filter( ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, "Setting filter: '%s'", filter_pattern); - condition = lttng_condition_event_rule_create(event_rule); + condition = lttng_condition_on_event_create(event_rule); ok(condition, "Condition event rule object creation"); /* Register the triggers for condition */ @@ -1681,6 +2223,241 @@ end: return; } +static int generate_capture_descr(struct lttng_condition *condition) +{ + int ret; + struct lttng_event_expr *expr = NULL; + unsigned int basic_field_size; + enum lttng_condition_status cond_status; + + basic_field_size = sizeof(test_capture_base_fields) / sizeof(*test_capture_base_fields); + for (int i = 0; i < basic_field_size; i++) { + + diag("Adding capture descriptor \"%s\"", test_capture_base_fields[i].field_name); + + switch (test_capture_base_fields[i].field_type) { + case FIELD_TYPE_PAYLOAD: + expr = lttng_event_expr_event_payload_field_create( + test_capture_base_fields[i].field_name); + break; + case FIELD_TYPE_CONTEXT: + expr = lttng_event_expr_channel_context_field_create( + test_capture_base_fields[i].field_name); + break; + case FIELD_TYPE_ARRAY_FIELD: + { + int nb_matches; + unsigned int index; + char field_name[256]; + struct lttng_event_expr *array_expr = NULL; + nb_matches = sscanf(test_capture_base_fields[i].field_name, + "%[^[][%u]", field_name, &index); + if (nb_matches != 2) { + ret = 1; + goto end; + } + array_expr = lttng_event_expr_event_payload_field_create( + field_name); + + expr = lttng_event_expr_array_field_element_create( + array_expr, index); + break; + } + case FIELD_TYPE_APP_CONTEXT: + fail("Application context not tested yet."); + default: + ret = 1; + goto end; + } + if (expr == NULL) { + fail("Creating capture expression"); + ret = -1; + goto end; + } + cond_status = lttng_condition_on_event_append_capture_descriptor( + condition, expr); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + fail("Appending capture_descriptor"); + ret = -1; + lttng_event_expr_destroy(expr); + goto end; + } + } + + ret = 0; + +end: + return ret; +} + +static int validator_notification_trigger_capture( + enum lttng_domain_type domain, + struct lttng_notification *notification, + const int iteration) +{ + int ret; + unsigned int capture_count, i; + enum lttng_evaluation_status evaluation_status; + enum lttng_event_field_value_status event_field_value_status; + const struct lttng_evaluation *evaluation; + const struct lttng_event_field_value *captured_fields; + bool at_least_one_error = false; + + evaluation = lttng_notification_get_evaluation(notification); + if (evaluation == NULL) { + fail("lttng_notification_get_evaluation"); + ret = 1; + goto end; + } + + /* TODO: it seems weird that lttng_evaluation_get_captured_values return + * INVALID if no capture were present. might be better to return + * something with more meaning. Another question is how we link the + * notion of capture and the descriptor from the perspective of a + * client. Is it really invalid to ask for captured value when there might + * not be any? + */ + evaluation_status = lttng_evaluation_get_captured_values(evaluation, &captured_fields); + if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { + diag("lttng_evaluation_get_captured_values"); + ret = 1; + goto end; + } + + event_field_value_status = + lttng_event_field_value_array_get_length(captured_fields, + &capture_count); + if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + assert(0 && "get count of captured field"); + } + + for (i = 0; i < capture_count; i++) { + const struct lttng_event_field_value *captured_field = NULL; + validate_cb validate; + bool expected; + + diag("Validating capture \"%s\"", test_capture_base_fields[i].field_name); + event_field_value_status = lttng_event_field_value_array_get_element_at_index(captured_fields, i, &captured_field); + + switch(domain) { + case LTTNG_DOMAIN_UST: + expected = test_capture_base_fields[i].expected_ust; + break; + case LTTNG_DOMAIN_KERNEL: + expected = test_capture_base_fields[i].expected_kernel; + break; + default: + assert(0 && "Domain invalid for this test"); + } + + if (!expected) { + ok(event_field_value_status == LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE, "No payload captured"); + continue; + } + + if (domain == LTTNG_DOMAIN_UST) { + validate = test_capture_base_fields[i].validate_ust; + } else { + validate = test_capture_base_fields[i].validate_kernel; + } + + if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + const char *reason; + if (event_field_value_status == + LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) { + reason = "Expected a capture but it is unavailable"; + } else { + reason = "lttng_event_field_value_array_get_element_at_index"; + } + fail(reason); + ret = 1; + goto end; + } + diag("Captured field of type %s", + field_value_type_to_str( + lttng_event_field_value_get_type(captured_field))); + + assert(validate); + ret = validate(captured_field, iteration); + if (ret) { + at_least_one_error = true; + } + } + + ret = at_least_one_error; + +end: + return ret; +} + +static void test_tracepoint_event_rule_notification_capture( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + + int i, ret; + struct lttng_condition *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "my_precious"; + const char *pattern; + + if (domain_type == LTTNG_DOMAIN_UST) { + pattern = "tp:tptest"; + } else { + pattern = "lttng_test_filter_event"; + } + + create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, + NULL, domain_type, generate_capture_descr, &condition, + &trigger); + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + /* Get 3 notifications */ + for (i = 0; i < 3; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + if (ret) { + lttng_notification_destroy(notification); + goto end; + } + + ret = validator_notification_trigger_capture(domain_type, notification, i); + if (ret) { + lttng_notification_destroy(notification); + goto end; + } + + lttng_notification_destroy(notification); + } + +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + return; +} + int main(int argc, const char *argv[]) { int test_scenario; @@ -1798,7 +2575,7 @@ int main(int argc, const char *argv[]) } case 4: { - plan_tests(13); + plan_tests(12); /* Test cases that need the kernel tracer. */ assert(domain_type == LTTNG_DOMAIN_KERNEL); @@ -1831,7 +2608,7 @@ int main(int argc, const char *argv[]) { const char *testapp_path, *test_symbol_name; - plan_tests(13); + plan_tests(12); if (argc < 7) { fail("Missing parameter for tests to run %d", argc); @@ -1851,6 +2628,26 @@ int main(int argc, const char *argv[]) break; } + case 7: + { + switch(domain_type) { + case LTTNG_DOMAIN_UST: + plan_tests(222); + break; + case LTTNG_DOMAIN_KERNEL: + plan_tests(216); + break; + default: + assert(0); + } + + diag("Test tracepoint event rule notification captures for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_capture(domain_type); + + break; + } + default: abort(); } diff --git a/tests/regression/tools/notification/sessiond_testpoints.c b/tests/regression/tools/notification/sessiond_testpoints.c new file mode 100644 index 000000000..0f6e2083f --- /dev/null +++ b/tests/regression/tools/notification/sessiond_testpoints.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *pause_pipe_path; +static struct lttng_pipe *pause_pipe; +static int *notifier_notif_consumption_state;; + +int lttng_opt_verbose; +int lttng_opt_mi; +int lttng_opt_quiet; + +static +void __attribute__((destructor)) pause_pipe_fini(void) +{ + int ret; + + if (pause_pipe_path) { + ret = unlink(pause_pipe_path); + if (ret) { + PERROR("unlink pause pipe"); + } + } + + free(pause_pipe_path); + lttng_pipe_destroy(pause_pipe); +} + +/* + */ +int __testpoint_sessiond_thread_notification(void); +int __testpoint_sessiond_thread_notification(void) +{ + int ret = 0; + const char *pause_pipe_path_prefix; + + pause_pipe_path_prefix = lttng_secure_getenv( + "NOTIFIER_PAUSE_PIPE_PATH"); + if (!pause_pipe_path_prefix) { + ret = -1; + goto end; + } + + notifier_notif_consumption_state = dlsym(NULL, "notifier_consumption_paused"); + assert(notifier_notif_consumption_state); + + ret = asprintf(&pause_pipe_path, "%s", pause_pipe_path_prefix); + if (ret < 1) { + ERR("Failed to allocate pause pipe path"); + goto end; + } + + DBG("Creating pause pipe at %s", pause_pipe_path); + pause_pipe = lttng_pipe_named_open(pause_pipe_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, O_NONBLOCK); + if (!pause_pipe) { + ERR("Failed to create pause pipe at %s", pause_pipe_path); + ret = -1; + goto end; + } + + /* Only the read end of the pipe is useful to us. */ + ret = lttng_pipe_write_close(pause_pipe); +end: + return ret; +} + +int __testpoint_sessiond_handle_notifier_event_pipe(void); +int __testpoint_sessiond_handle_notifier_event_pipe(void) +{ + int ret = 0; + uint8_t value; + bool value_read = false; + + if (!pause_pipe) { + ret = -1; + goto end; + } + + /* Purge pipe and only consider the freshest value. */ + do { + ret = lttng_pipe_read(pause_pipe, &value, sizeof(value)); + if (ret == sizeof(value)) { + value_read = true; + } + } while (ret == sizeof(value)); + + ret = (errno == EAGAIN) ? 0 : -errno; + + if (value_read) { + *notifier_notif_consumption_state = !!value; + DBG("Message received on pause pipe: %s data consumption", + *notifier_notif_consumption_state ? "paused" : "resumed"); + } +end: + return ret; +} diff --git a/tests/regression/tools/notification/test_notification_kernel_capture b/tests/regression/tools/notification/test_notification_kernel_capture new file mode 100755 index 000000000..9b6e7ac57 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_capture @@ -0,0 +1,55 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +NUM_TESTS=104 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_basic_error_path +{ + kernel_event_generator_run_once_per_transition generate_filter_events \ + "$TESTAPP_STATE_PATH" 10 & + APP_PID=$! + + "$CURDIR/notification" 7 LTTNG_DOMAIN_KERNEL $APP_PID \ + "$TESTAPP_STATE_PATH" + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + + +if [ "$(id -u)" == "0" ]; then + validate_lttng_modules_present + + modprobe lttng-test + + start_lttng_sessiond_notap + + test_basic_error_path + + stop_lttng_sessiond_notap + rmmod lttng-test + +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_multi_app b/tests/regression/tools/notification/test_notification_multi_app index afac94d10..5d5427c9d 100755 --- a/tests/regression/tools/notification/test_notification_multi_app +++ b/tests/regression/tools/notification/test_notification_multi_app @@ -54,8 +54,9 @@ function start_client { local buffer_usage_threshold_type=$6 local buffer_usage_threshold_value=$7 local nr_expected_notification=$8 + local use_action_group=$9 - ${CURDIR}/base_client ${session_name} ${channel_name} ${domain_type} ${buffer_usage_type} ${buffer_usage_threshold_type} ${buffer_usage_threshold_value} ${nr_expected_notification} > ${output_file} & + ${CURDIR}/base_client ${session_name} ${channel_name} ${domain_type} ${buffer_usage_type} ${buffer_usage_threshold_type} ${buffer_usage_threshold_value} ${nr_expected_notification} ${use_action_group} > ${output_file} & pid=$! app_pids+=("$pid") @@ -177,8 +178,8 @@ function test_multi_app () for (( i = 0; i < $nr_client_app; i++ )); do low_app_output_file=$output_dir/${low_output_file_pattern}${i} high_app_output_file=$output_dir/${high_output_file_pattern}${i} - start_client $low_app_output_file $SESSION_NAME $CHANNEL_NAME $domain_string LOW RATIO 0.0 $nr_notification_expected - start_client $high_app_output_file $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 $nr_notification_expected + start_client $low_app_output_file $SESSION_NAME $CHANNEL_NAME $domain_string LOW RATIO 0.0 $nr_notification_expected $(( $i % 2)) + start_client $high_app_output_file $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 $nr_notification_expected $(( $i % 2)) done wait_for_message $output_dir "${low_output_file_pattern}" "sync: ready" @@ -362,7 +363,7 @@ function test_on_register_evaluation () high_app_output_file=${high_output_file_pattern}.first_receiver high_app_output_path=$output_dir/${high_app_output_file} - start_client $high_app_output_path $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 1 + start_client $high_app_output_path $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 1 0 wait_for_message $output_dir "${high_app_output_file}" "sync: ready" @@ -379,7 +380,7 @@ function test_on_register_evaluation () # notification on subscription high_app_output_file=${high_output_file_pattern}.second_receiver high_app_output_path=$output_dir/${high_app_output_file} - start_client $high_app_output_path $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 1 + start_client $high_app_output_path $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 1 0 wait_for_message $output_dir "${high_app_output_file}" "sync: ready" wait_for_message $output_dir "${high_app_output_file}" "notification: high 0" diff --git a/tests/regression/tools/notification/test_notification_notifier_discarded_count b/tests/regression/tools/notification/test_notification_notifier_discarded_count new file mode 100755 index 000000000..620a8a861 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_notifier_discarded_count @@ -0,0 +1,254 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" + +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f "${CURDIR}/.libs/libpause_sessiond.so") + +SH_TAP=1 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" +FULL_LTTNG_SESSIOND_BIN="${TESTDIR}/../src/bin/lttng-sessiond/lttng-sessiond" + +UST_NUM_TESTS=16 +KERNEL_NUM_TESTS=15 +NUM_TESTS=$(($UST_NUM_TESTS + $KERNEL_NUM_TESTS)) + +plan_tests $NUM_TESTS + +function test_kernel_notifier_discarded_count +{ + local sessiond_pipe=() + local trigger_name="my_trigger" + local list_triggers_stdout=$(mktemp -t list_triggers_stdout.XXXXXX) + + # Used on sessiond launch. + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + NOTIFIER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + diag "Kernel event notifer error counter" + + start_lttng_sessiond_notap + + # This is needed since the testpoint create a pipe with the sessiond + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + sessiond_pipe+=("$f") + done + + lttng_add_trigger_ok "$trigger_name" \ + --condition on-event --kernel lttng_test_filter_event \ + --action notify + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + # Stop consumption of notifier tracer notifications. + echo -n 1 > $sessiond_pipe + + # The notifier ring buffer configuration is currently made of 16 4096 + # bytes subbuffers. Each kernel notification is at least 42 bytes long. + # To fill it, we need to generate (16 * 4096)/42 = 1561 notifications. + # That number is a bit larger than what we need since some of the space + # is lost in subbuffer boundaries. + echo -n "200000" > /proc/lttng-test-filter-event + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. To avoid + # false positive. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded" + ok $? "Tracer notification discarded line printed" + + # Confirm that the number of tracer notifications discarded is not zero. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + isnt $? 0 "Discarded tracer notification number non-zero as expected" + + lttng_remove_trigger_ok "$trigger_name" + + # Confirm that no notifier is enabled. + list_triggers_line_count=$("$FULL_LTTNG_BIN" list-triggers | wc -l) + is "$list_triggers_line_count" "0" "No \`on-event\` kernel notifier enabled as expected" + + # Enable another notifier and list it to confirm the counter was cleared. + lttng_add_trigger_ok "$trigger_name" \ + --condition on-event --kernel lttng_test_filter_event \ + --action notify + + # Confirm that the discarded notification line is present. + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + lttng_remove_trigger_ok "$trigger_name" + + stop_lttng_sessiond_notap + + unset LTTNG_SESSIOND_ENV_VARS + + rm -f "$list_triggers_stdout" +} + +function test_kernel_notifier_discarded_count_max_bucket +{ + start_lttng_sessiond "" "--event-notifier-error-number-of-bucket=3" + + diag "Kernel event notifer error counter bucket limit" + for i in $(seq 3); do + lttng_add_trigger_ok "$i" \ + --condition on-event --kernel my_event_that_doesnt_need_to_really_exist_$i \ + --action notify + done + + for i in $(seq 4 5); do + lttng_add_trigger_fail "$i" \ + --condition on-event --kernel my_event_that_doesnt_need_to_really_exist_$i \ + --action notify + done + + stop_lttng_sessiond_notap +} + +function test_ust_notifier_discarded_count +{ + local sessiond_pipe=() + local trigger_name="my_trigger" + local list_triggers_stdout=$(mktemp -t list_triggers_stdout.XXXXXX) + local NR_ITER=2000 + local NR_USEC_WAIT=0 + + + diag "UST event notifer error counter" + # Used on sessiond launch. + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + NOTIFIER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + start_lttng_sessiond_notap + + # This is needed since the testpoint create a pipe with the sessiond + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + sessiond_pipe+=("$f") + done + + lttng_add_trigger_ok "$trigger_name" \ + --condition on-event --userspace tp:tptest \ + --action notify + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + # Stop consumption of notifier tracer notifications. + echo -n 1 > $sessiond_pipe + + # The notifier ring buffer configuration is currently made of 16 4096 + # bytes subbuffers. Each userspace notification is at least 42 bytes long. + # To fill it, we need to generate (16 * 4096)/42 = 1561 notifications. + # That number is a bit larger than what we need since some of the space + # is lost in subbuffer boundaries. + $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT + ok $? "Generating $NR_ITER tracer notifications" + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. To avoid + # false positive. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded" + ok $? "Tracer notification discarded line printed" + + # Confirm that the number of tracer notifications discarded is not zero. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + isnt $? 0 "Discarded tracer notification number non-zero as expected" + + # Remove the notifier. + lttng_remove_trigger_ok "$trigger_name" + + # Confirm that no notifier is enabled. + list_triggers_line_count=$("$FULL_LTTNG_BIN" list-triggers | wc -l) + is "$list_triggers_line_count" "0" "No \`on-event\` userspace notifier enabled as expected" + + # Enable another notifier and list it to confirm the counter was cleared. + lttng_add_trigger_ok "$trigger_name" \ + --condition on-event --userspace tp:tptest \ + --action notify + + # Confirm that the discarded notification line is present. + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + lttng_remove_trigger_ok "$trigger_name" + + stop_lttng_sessiond_notap + + unset LTTNG_SESSIOND_ENV_VARS + + rm -f "$list_triggers_stdout" +} +function test_ust_notifier_discarded_count_max_bucket +{ + start_lttng_sessiond "" "--event-notifier-error-number-of-bucket=3" + + diag "UST event notifer error counter bucket limit" + for i in $(seq 3); do + lttng_add_trigger_ok "$i" \ + --condition on-event --userspace my_event_that_doesnt_need_to_really_exist_$i \ + --action notify + done + + for i in $(seq 4 5); do + lttng_add_trigger_fail "$i" \ + --condition on-event --userspace my_event_that_doesnt_need_to_really_exist_$i \ + --action notify + done + + stop_lttng_sessiond_notap +} + +test_ust_notifier_discarded_count +test_ust_notifier_discarded_count_max_bucket + +if [ "$(id -u)" == "0" ]; then + + validate_lttng_modules_present + + modprobe lttng-test + + test_kernel_notifier_discarded_count + + test_kernel_notifier_discarded_count_max_bucket + + modprobe --remove lttng-test + + rm -rf "${sessiond_pipe[@]}" 2> /dev/null +else + # Kernel tests are skipped. + skip 0 "Root access is needed. Skipping all kernel notification tests." $KERNEL_NUM_TESTS +fi + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_ust_capture b/tests/regression/tools/notification/test_notification_ust_capture new file mode 100755 index 000000000..5003c2894 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_ust_capture @@ -0,0 +1,40 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +TESTAPP_PATH="$TESTDIR/utils/testapp" + +GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events" +GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME" + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_basic_error_path +{ + ust_event_generator_run_once_per_transition \ + "$GEN_UST_EVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" 3 10 & + APP_PID=$! + + "$CURDIR/notification" 7 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH" + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + +start_lttng_sessiond_notap + +test_basic_error_path + +stop_lttng_sessiond_notap diff --git a/tests/regression/tools/save-load/Makefile.am b/tests/regression/tools/save-load/Makefile.am index 3361744e9..469102f8e 100644 --- a/tests/regression/tools/save-load/Makefile.am +++ b/tests/regression/tools/save-load/Makefile.am @@ -3,7 +3,8 @@ noinst_SCRIPTS = test_save test_load test_autoload EXTRA_DIST = $(noinst_SCRIPTS) load-42.lttng load-42-complex.lttng \ load-42-trackers.lttng tracker_legacy_none.lttng \ - tracker_legacy_all.lttng tracker_legacy_selective.lttng + tracker_legacy_all.lttng tracker_legacy_selective.lttng \ + load-42-maps.lttng SUBDIRS = configuration diff --git a/tests/regression/tools/save-load/load-42-maps.lttng b/tests/regression/tools/save-load/load-42-maps.lttng new file mode 100644 index 000000000..82aaecd50 --- /dev/null +++ b/tests/regression/tools/save-load/load-42-maps.lttng @@ -0,0 +1,67 @@ + + + + load-42-maps + + + UST + PER_UID + + + + ze-map-32 + true + 32 + OVERFLOW + false + + + 4096 + + + + + ze-map-64 + true + 64 + OVERFLOW + false + + + 4096 + + + + + + + + JUL + PER_UID + + + + + LOG4J + PER_UID + + + + + PYTHON + PER_UID + + + + + false + + + true + + /tmp/lttng/load-42-maps + + + + + diff --git a/tests/regression/tools/save-load/test_load b/tests/regression/tools/save-load/test_load index 4f18dec2d..1870bf0b4 100755 --- a/tests/regression/tools/save-load/test_load +++ b/tests/regression/tools/save-load/test_load @@ -16,7 +16,7 @@ EVENT_NAME="tp:tptest" DIR=$(readlink -f $TESTDIR) -NUM_TESTS=75 +NUM_TESTS=78 source $TESTDIR/utils/utils.sh @@ -101,6 +101,7 @@ function test_all_load() destroy_lttng_session_ok $SESSION_NAME destroy_lttng_session_ok "$SESSION_NAME-complex" destroy_lttng_session_ok "$SESSION_NAME-trackers" + destroy_lttng_session_ok "$SESSION_NAME-maps" destroy_lttng_session_ok "tracker_legacy_all" destroy_lttng_session_ok "tracker_legacy_none" destroy_lttng_session_ok "tracker_legacy_selective" @@ -164,6 +165,25 @@ function test_trackers() rm -f ${mi_output_file} } +function test_maps() +{ + diag "Test maps loading" + + lttng_load_ok "-i $CURDIR/$SESSION_NAME-maps.lttng" + + local mi_output_file=$(mktemp) + if [ $? -ne 0 ]; then + break; + fi + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN --mi XML list "$SESSION_NAME-maps" > $mi_output_file + + # TODO: once map information is in the MI, do some mad testing with sick xpath expressions. + + destroy_lttng_session_ok "$SESSION_NAME-maps" + rm -f ${mi_output_file} +} + function test_override_url_normal() { local local_url_override="file:///tmp/override/to/here" @@ -354,6 +374,7 @@ TESTS=( test_all_load test_overwrite test_trackers + test_maps test_override_session_name test_override_url_normal test_override_url_snapshot diff --git a/tests/regression/tools/save-load/test_save b/tests/regression/tools/save-load/test_save index 0f3e596a0..c4bc515e3 100755 --- a/tests/regression/tools/save-load/test_save +++ b/tests/regression/tools/save-load/test_save @@ -12,13 +12,17 @@ TESTDIR=$CURDIR/../../../ SESSION_NAME="save-42" CHANNEL_NAME="chan-save" EVENT_NAME="tp:tptest" +MAP_32_NAME="ze-map-32" +MAP_64_NAME="ze-map-64" DIR=$(readlink -f $TESTDIR) -NUM_TESTS=41 +NUM_TESTS=46 source $TESTDIR/utils/utils.sh +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + # MUST set TESTDIR before calling those functions plan_tests $NUM_TESTS @@ -54,12 +58,25 @@ function test_basic_save() create_lttng_session_ok $SESSION_NAME $TRACE_PATH enable_ust_lttng_channel_ok $SESSION_NAME $CHANNEL_NAME enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME $CHANNEL_NAME + lttng_add_map_ok $MAP_32_NAME $SESSION_NAME -u 32 "" "" + lttng_add_map_ok $MAP_64_NAME $SESSION_NAME -u 64 "" "" lttng_track_ok "-p 666 -u -s $SESSION_NAME" lttng_save $SESSION_NAME "-o $TRACE_PATH" is_session_saved $TRACE_PATH $SESSION_NAME + local session_file="$TRACE_PATH/$SESSION_NAME.lttng" + + local map_count=$(xmllint --xpath 'count(/sessions/session[1]/domains/domain[./type = "UST"]/maps/map)' "$session_file") + is "$map_count" 2 + + local bitness_32=$(xmllint --xpath '/sessions/session[1]/domains/domain[./type = "UST"]/maps/map[./name = "ze-map-32"]/bitness/text()' "$session_file") + is "$bitness_32" 32 + + local bitness_64=$(xmllint --xpath '/sessions/session[1]/domains/domain[./type = "UST"]/maps/map[./name = "ze-map-64"]/bitness/text()' "$session_file") + is "$bitness_64" 64 + destroy_lttng_session_ok $SESSION_NAME } diff --git a/tests/regression/tools/trigger/start-stop/test_start_stop b/tests/regression/tools/trigger/start-stop/test_start_stop index cecea3b19..a6fc7bd02 100755 --- a/tests/regression/tools/trigger/start-stop/test_start_stop +++ b/tests/regression/tools/trigger/start-stop/test_start_stop @@ -22,47 +22,6 @@ NUM_TESTS=18 NR_ITER=5 NR_USEC_WAIT=5 -function lttng_add_trigger_ust() -{ - local expected_to_fail="$1" - local trigger_name="$2" - shift 2 - - "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" "$@" 1> /dev/null 2> /dev/null - ret=$? - if [[ $expected_to_fail -eq "1" ]]; then - test "$ret" -ne "0" - ok $? "Add trigger $trigger_name failed as expected" - else - ok $ret "Add trigger $trigger_name" - fi -} - -function lttng_remove_trigger_ust() -{ - local expected_to_fail="$1" - local trigger_name="$2" - - "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" 1> /dev/null 2> /dev/null - ret=$? - if [[ $expected_to_fail -eq "1" ]]; then - test "$ret" -ne "0" - ok $? "Remove trigger $trigger_name failed as expected" - else - ok $ret "Remove trigger $trigger_name" - fi -} - -function lttng_add_trigger_ust_ok() -{ - lttng_add_trigger_ust 0 "$@" -} - -function lttng_remove_trigger_ust_ok() -{ - lttng_remove_trigger_ust 0 "$@" -} - function lttng_session_is_active() { local SESSION_NAME="$1" @@ -96,7 +55,7 @@ function test_start_session_action() # Add `start-session` action to an event-rule condition _followed_ by # a `notify` action. - lttng_add_trigger_ust_ok \ + lttng_add_trigger_ok \ $TRIGGER_NAME \ --condition on-event -u "tp:tptest" \ --action start-session $SESSION_NAME \ @@ -124,7 +83,7 @@ function test_start_session_action() lttng_session_is_active $SESSION_NAME # Tearing down. - lttng_remove_trigger_ust_ok $TRIGGER_NAME + lttng_remove_trigger_ok $TRIGGER_NAME stop_lttng_tracing_ok $SESSION_NAME destroy_lttng_session_ok $SESSION_NAME @@ -150,7 +109,7 @@ function test_stop_session_action() # Add `stop-session` action to an event-rule condition _followed_ by # a `notify` action. - lttng_add_trigger_ust_ok \ + lttng_add_trigger_ok \ $TRIGGER_NAME \ --condition on-event -u "tp:tptest" \ --action stop-session $SESSION_NAME \ @@ -178,7 +137,7 @@ function test_stop_session_action() lttng_session_is_inactive $SESSION_NAME # Tearing down. - lttng_remove_trigger_ust_ok $TRIGGER_NAME + lttng_remove_trigger_ok $TRIGGER_NAME destroy_lttng_session_ok $SESSION_NAME rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH" diff --git a/tests/regression/tools/trigger/test_add_trigger_cli b/tests/regression/tools/trigger/test_add_trigger_cli index db325a90d..b3a51dd47 100755 --- a/tests/regression/tools/trigger/test_add_trigger_cli +++ b/tests/regression/tools/trigger/test_add_trigger_cli @@ -101,20 +101,20 @@ test_success "--fire-every" \ skip $ist_root "non-root user: skipping kprobe tests" 9 || { test_success "--condition on-event probe by symbol" \ - --condition on-event -k --probe=lttng_channel_enable my_channel_enable \ + --condition on-event -k --probe=lttng_event_container_enable my_channel_enable \ --action notify - channel_enable_addr=$(grep ' t lttng_channel_enable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') - channel_disable_addr=$(grep ' t lttng_channel_disable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') + channel_enable_addr=$(grep 'lttng_event_container_enable' /proc/kallsyms | cut -f 1 -d ' ') + channel_disable_addr=$(grep 'lttng_event_container_disable' /proc/kallsyms | cut -f 1 -d ' ') # We need to find a valid offset. base_symbol="" offset=0 if [[ 0x$channel_enable_addr -lt 0x$channel_disable_addr ]]; then - base_symbol="lttng_channel_enable" + base_symbol="lttng_event_container_enable" offset=$(( 0x$channel_disable_addr - 0x$channel_enable_addr )) else - base_symbol="lttng_channel_disable" + base_symbol="lttng_event_container_disable" offset=$(( 0x$channel_enable_addr - 0x$channel_disable_addr )) fi diff --git a/tests/regression/tools/trigger/test_list_triggers_cli b/tests/regression/tools/trigger/test_list_triggers_cli index e09d7cdc6..88b2e87f3 100755 --- a/tests/regression/tools/trigger/test_list_triggers_cli +++ b/tests/regression/tools/trigger/test_list_triggers_cli @@ -23,7 +23,7 @@ TESTDIR="$CURDIR/../../.." # shellcheck source=../../../utils/utils.sh source "$TESTDIR/utils/utils.sh" -plan_tests 40 +plan_tests 54 FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" @@ -41,12 +41,6 @@ else ist_root=0 fi -function add_trigger () -{ - "${FULL_LTTNG_BIN}" add-trigger "$@" - ok $? "add trigger \`$*\`: exit code is 0" -} - function list_triggers () { local test_name="$1" @@ -68,9 +62,9 @@ test_top_level_options () start_lttng_sessiond_notap - add_trigger --id hello --condition on-event -u test-id --action notify - add_trigger --fire-once-after 123 --condition on-event -u test-fire-once-after --action notify - add_trigger --fire-every 124 --condition on-event -u test-fire-every --action notify + lttng_add_trigger_ok "hello" --condition on-event -u test-id --action notify + lttng_add_trigger_ok "T0" --fire-once-after 123 --condition on-event -u test-fire-once-after --action notify + lttng_add_trigger_ok "T1" --fire-every 124 --condition on-event -u test-fire-every --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: T0 @@ -78,6 +72,7 @@ test_top_level_options () firing policy: once after 123 occurences condition: event rule hit rule: test-fire-once-after (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify - id: T1 @@ -85,12 +80,14 @@ test_top_level_options () firing policy: after every 124 occurences condition: event rule hit rule: test-fire-every (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify - id: hello user id: ${uid} condition: event rule hit rule: test-id (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify EOF @@ -105,41 +102,88 @@ test_on_event_tracepoint () # shellcheck disable=SC2119 start_lttng_sessiond_notap - add_trigger --condition on-event -u -a --action notify - add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify - add_trigger --condition on-event 'hello*' -u -x 'hello2,hello3,hello4' --action notify - add_trigger --id BCD --condition on-event -u gerboise --loglevel INFO --action notify - add_trigger --condition on-event -u lemming --loglevel-only WARNING --action notify + lttng_add_trigger_ok "C" --condition on-event -u -a --action notify + lttng_add_trigger_ok "A" --condition on-event aaa -u --filter 'p == 2' --action notify + lttng_add_trigger_ok "D" --condition on-event 'hello*' -u -x 'hello2,hello3,hello4' --action notify + lttng_add_trigger_ok "B" --condition on-event -u gerboise --loglevel INFO --action notify + lttng_add_trigger_ok "E" --condition on-event -u lemming --loglevel-only WARNING --action notify + lttng_add_trigger_ok "F" --condition on-event -u capture-payload-field --capture a --action notify + lttng_add_trigger_ok "G" --condition on-event -u capture-array --capture 'a[2]' --capture '$ctx.tourlou[18]' --action notify + lttng_add_trigger_ok "H" --condition on-event -u capture-chan-ctx --capture '$ctx.vpid' --action notify + lttng_add_trigger_ok "I" --condition on-event -u capture-app-ctx --capture '$app.iga:active_clients' --action notify + cat > "${tmp_expected_stdout}" <<- EOF - - id: ABC + - id: A user id: ${uid} condition: event rule hit rule: aaa (type: tracepoint, domain: ust, filter: p == 2) + tracer notifications discarded: 0 actions: notify - - id: BCD + - id: B user id: ${uid} condition: event rule hit rule: gerboise (type: tracepoint, domain: ust, log level <= TRACE_INFO) + tracer notifications discarded: 0 actions: notify - - id: T0 + - id: C user id: ${uid} condition: event rule hit rule: * (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify - - id: T1 + - id: D user id: ${uid} condition: event rule hit rule: hello* (type: tracepoint, domain: ust, exclusions: hello2,hello3,hello4) + tracer notifications discarded: 0 actions: notify - - id: T2 + - id: E user id: ${uid} condition: event rule hit rule: lemming (type: tracepoint, domain: ust, log level == TRACE_WARNING) + tracer notifications discarded: 0 + actions: + notify + - id: F + user id: ${uid} + condition: event rule hit + rule: capture-payload-field (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + captures: + - a + actions: + notify + - id: G + user id: ${uid} + condition: event rule hit + rule: capture-array (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + captures: + - a[2] + - \$ctx.tourlou[18] + actions: + notify + - id: H + user id: ${uid} + condition: event rule hit + rule: capture-chan-ctx (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + captures: + - \$ctx.vpid + actions: + notify + - id: I + user id: ${uid} + condition: event rule hit + rule: capture-app-ctx (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + captures: + - \$app.iga:active_clients actions: notify EOF @@ -157,43 +201,46 @@ test_on_event_probe () # shellcheck disable=SC2119 start_lttng_sessiond_notap - channel_enable_addr=$(grep ' t lttng_channel_enable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') - channel_disable_addr=$(grep ' t lttng_channel_disable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') + channel_enable_addr=$(grep 'lttng_event_container_enable' /proc/kallsyms | cut -f 1 -d ' ') + channel_disable_addr=$(grep 'lttng_event_container_disable' /proc/kallsyms | cut -f 1 -d ' ') # We need to find a valid offset. base_symbol="" offset=0 if [[ 0x$channel_enable_addr -lt 0x$channel_disable_addr ]]; then - base_symbol="lttng_channel_enable" + base_symbol="lttng_event_container_enable" offset=$(( 0x$channel_disable_addr - 0x$channel_enable_addr )) else - base_symbol="lttng_channel_disable" + base_symbol="lttng_event_container_disable" offset=$(( 0x$channel_enable_addr - 0x$channel_disable_addr )) fi offset_hex="0x$(printf '%x' $offset)" - add_trigger --condition on-event -k --probe=lttng_channel_enable my_channel_enable --action notify - add_trigger --condition on-event -k --probe="${base_symbol}+${offset_hex}" my_channel_enable --action notify - add_trigger --condition on-event -k --probe="0x${channel_enable_addr}" my_channel_enable --action notify + lttng_add_trigger_ok "T0" --condition on-event -k --probe=lttng_event_container_enable my_channel_enable --action notify + lttng_add_trigger_ok "T1" --condition on-event -k --probe="${base_symbol}+${offset_hex}" my_channel_enable --action notify + lttng_add_trigger_ok "T2" --condition on-event -k --probe="0x${channel_enable_addr}" my_channel_enable --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: T0 user id: ${uid} condition: event rule hit - rule: my_channel_enable (type: probe, location: lttng_channel_enable) + rule: my_channel_enable (type: probe, location: lttng_event_container_enable) + tracer notifications discarded: 0 actions: notify - id: T1 user id: ${uid} condition: event rule hit rule: my_channel_enable (type: probe, location: ${base_symbol}+${offset_hex}) + tracer notifications discarded: 0 actions: notify - id: T2 user id: ${uid} condition: event rule hit rule: my_channel_enable (type: probe, location: 0x${channel_enable_addr}) + tracer notifications discarded: 0 actions: notify EOF @@ -208,13 +255,14 @@ test_on_event_userspace_probe () # shellcheck disable=SC2119 start_lttng_sessiond_notap - add_trigger --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe --action notify + lttng_add_trigger_ok "T0" --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: T0 user id: ${uid} condition: event rule hit rule: ma-probe (type: userspace probe, location: ${uprobe_elf_binary}:test_function) + tracer notifications discarded: 0 actions: notify EOF @@ -229,20 +277,22 @@ test_on_event_syscall () # shellcheck disable=SC2119 start_lttng_sessiond_notap - add_trigger --condition on-event -k --syscall open --action notify - add_trigger --condition on-event -k --syscall ptrace --filter 'a > 2' --action notify + lttng_add_trigger_ok "T0" --condition on-event -k --syscall open --action notify + lttng_add_trigger_ok "T1" --condition on-event -k --syscall ptrace --filter 'a > 2' --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: T0 user id: ${uid} condition: event rule hit rule: open (type: syscall) + tracer notifications discarded: 0 actions: notify - id: T1 user id: ${uid} condition: event rule hit rule: ptrace (type: syscall, filter: a > 2) + tracer notifications discarded: 0 actions: notify EOF @@ -256,14 +306,14 @@ test_snapshot_action () { start_lttng_sessiond_notap - add_trigger --condition on-event -u some-event --action snapshot-session ze-session - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --path /some/path - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --url file:///some/other/path - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --url net://1.2.3.4 - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --url net://1.2.3.4:1234:1235 - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1111 --data-url=tcp://1.2.3.4:1112 - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --path /some/path --max-size=1234 - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --path /some/path --name=meh + lttng_add_trigger_ok "T0" --condition on-event -u some-event --action snapshot-session ze-session + lttng_add_trigger_ok "T1" --condition on-event -u some-event --action snapshot-session ze-session --path /some/path + lttng_add_trigger_ok "T2" --condition on-event -u some-event --action snapshot-session ze-session --url file:///some/other/path + lttng_add_trigger_ok "T3" --condition on-event -u some-event --action snapshot-session ze-session --url net://1.2.3.4 + lttng_add_trigger_ok "T4" --condition on-event -u some-event --action snapshot-session ze-session --url net://1.2.3.4:1234:1235 + lttng_add_trigger_ok "T5" --condition on-event -u some-event --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1111 --data-url=tcp://1.2.3.4:1112 + lttng_add_trigger_ok "T6" --condition on-event -u some-event --action snapshot-session ze-session --path /some/path --max-size=1234 + lttng_add_trigger_ok "T7" --condition on-event -u some-event --action snapshot-session ze-session --path /some/path --name=meh cat > "${tmp_expected_stdout}" <<- EOF @@ -271,48 +321,56 @@ test_snapshot_action () user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\` - id: T1 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, path: /some/path - id: T2 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, path: /some/other/path - id: T3 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, url: net://1.2.3.4 - id: T4 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, url: net://1.2.3.4:1234:1235 - id: T5 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, control url: tcp://1.2.3.4:1111, data url: tcp://1.2.3.4:1112 - id: T6 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, path: /some/path, max size: 1234 - id: T7 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, path: /some/path, name: meh EOF @@ -322,12 +380,90 @@ test_snapshot_action () stop_lttng_sessiond_notap } +test_on_event_kernel_incr_value () +{ + local session="session_doesnt_need_to_exist" + local map="map_doesnt_need_to_exist" + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + lttng_add_trigger_ok "T0" --condition on-event -k some-event --action incr-value -s $session -m $map --key string + lttng_add_trigger_ok "T1" --condition on-event -k some-event2 --action incr-value -s $session -m $map --key prefix_$\{EVENT_NAME\} + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T0 + user id: ${uid} + condition: event rule hit + rule: some-event (type: tracepoint, domain: kernel) + tracer notifications discarded: 0 + actions: + increment value: + session: \`${session}\` + map: \`${map}\` + key: \`string\` + - id: T1 + user id: ${uid} + condition: event rule hit + rule: some-event2 (type: tracepoint, domain: kernel) + tracer notifications discarded: 0 + actions: + increment value: + session: \`${session}\` + map: \`${map}\` + key: \`prefix_\${EVENT_NAME}\` + EOF + + list_triggers "on-event kernel incr-value" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_ust_incr_value () +{ + local session="session_doesnt_need_to_exist" + local map="map_doesnt_need_to_exist" + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + lttng_add_trigger_ok "T0" --condition on-event -u some-event --action incr-value -s $session -m $map --key string + lttng_add_trigger_ok "T1" --condition on-event -u some-event2 --action incr-value -s $session -m $map --key prefix_$\{EVENT_NAME\} + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T0 + user id: ${uid} + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + actions: + increment value: + session: \`${session}\` + map: \`${map}\` + key: \`string\` + - id: T1 + user id: ${uid} + condition: event rule hit + rule: some-event2 (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + actions: + increment value: + session: \`${session}\` + map: \`${map}\` + key: \`prefix_\${EVENT_NAME}\` + EOF + + list_triggers "on-event UST incr-value" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + test_top_level_options test_on_event_tracepoint skip $ist_root "non-root user: skipping kprobe tests" 6 || test_on_event_probe skip $ist_root "non-root user: skipping uprobe tests" 4 || test_on_event_userspace_probe skip $ist_root "non-root user: skipping syscall tests" 5 || test_on_event_syscall test_snapshot_action +skip $ist_root "non-root user: skipping incr-value tests" 5 || test_on_event_kernel_incr_value +test_on_event_ust_incr_value # Cleanup rm -f "${tmp_stdout}" diff --git a/tests/regression/tools/trigger/test_remove_trigger_cli b/tests/regression/tools/trigger/test_remove_trigger_cli index 168227a4a..582cce7ef 100755 --- a/tests/regression/tools/trigger/test_remove_trigger_cli +++ b/tests/regression/tools/trigger/test_remove_trigger_cli @@ -34,12 +34,6 @@ tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX) uid=$(id --user) gid=$(id --group) -function add_trigger () -{ - "${FULL_LTTNG_BIN}" add-trigger "$@" - ok $? "add trigger \`$*\`: exit code is 0" -} - function list_triggers () { local test_name="$1" @@ -74,20 +68,22 @@ function remove_trigger () start_lttng_sessiond_notap # Add a few triggers -add_trigger --condition on-event -u -a --action notify -add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify +lttng_add_trigger_ok "ABC" --condition on-event aaa -u --filter 'p == 2' --action notify +lttng_add_trigger_ok "DEF" --condition on-event -u -a --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: ABC user id: ${uid} condition: event rule hit rule: aaa (type: tracepoint, domain: ust, filter: p == 2) + tracer notifications discarded: 0 actions: notify -- id: T0 +- id: DEF user id: ${uid} condition: event rule hit rule: * (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify EOF @@ -96,16 +92,17 @@ list_triggers "two triggers left" "${tmp_expected_stdout}" remove_trigger "ABC" cat > "${tmp_expected_stdout}" <<- EOF -- id: T0 +- id: DEF user id: ${uid} condition: event rule hit rule: * (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify EOF list_triggers "one trigger left" "${tmp_expected_stdout}" -remove_trigger "T0" +remove_trigger "DEF" list_triggers "no triggers left" "/dev/null" diff --git a/tests/regression/tools/trigger/utils/notification-client.c b/tests/regression/tools/trigger/utils/notification-client.c index aecc478bd..47f1d67aa 100644 --- a/tests/regression/tools/trigger/utils/notification-client.c +++ b/tests/regression/tools/trigger/utils/notification-client.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include "utils.h" @@ -69,7 +69,7 @@ static bool is_expected_trigger_name(const char *expected_trigger_name, case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: { const char *trigger_name; enum lttng_evaluation_status evaluation_status; diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 01b298152..a88fe0b6c 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -8,27 +8,33 @@ LOG_DRIVER_FLAGS='--merge' LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ $(top_srcdir)/config/tap-driver.sh -TESTS = test_kernel_data \ - test_session \ - test_uri \ - test_utils_parse_size_suffix \ - test_utils_parse_time_suffix \ - test_utils_expand_path \ - test_utils_compat_poll \ - test_utils_compat_pthread \ - test_string_utils \ - test_notification \ - test_event_rule \ - test_directory_handle \ - test_relayd_backward_compat_group_by_session \ +TESTS = \ ini_config/test_ini_config \ - test_fd_tracker \ - test_uuid \ + test_action \ test_buffer_view \ + test_directory_handle \ test_event_expr_to_bytecode \ + test_event_rule \ + test_fd_tracker \ + test_kernel_data \ + test_kernel_probe \ + test_log_level_rule \ + test_map \ + test_map_key \ + test_map_query \ + test_notification \ test_payload \ + test_relayd_backward_compat_group_by_session \ + test_session \ + test_string_utils \ test_unix_socket \ - test_kernel_probe + test_uri \ + test_utils_compat_poll \ + test_utils_compat_pthread \ + test_utils_expand_path \ + test_utils_parse_size_suffix \ + test_utils_parse_time_suffix \ + test_uuid LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la @@ -41,19 +47,33 @@ LIBRELAYD=$(top_builddir)/src/common/relayd/librelayd.la LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la # Define test programs -noinst_PROGRAMS = test_uri test_session test_kernel_data \ - test_utils_parse_size_suffix test_utils_parse_time_suffix \ - test_utils_expand_path test_utils_compat_poll test_utils_compat_pthread \ - test_string_utils test_notification test_directory_handle \ - test_relayd_backward_compat_group_by_session \ - test_fd_tracker test_uuid \ - test_buffer_view \ - test_payload \ - test_unix_socket \ - test_kernel_probe \ - test_condition \ - test_event_expr_to_bytecode \ - test_event_rule +noinst_PROGRAMS = \ + test_action \ + test_buffer_view \ + test_condition \ + test_directory_handle \ + test_event_expr_to_bytecode \ + test_event_rule \ + test_fd_tracker \ + test_kernel_data \ + test_kernel_probe \ + test_log_level_rule \ + test_map \ + test_map_key \ + test_map_query \ + test_notification \ + test_payload \ + test_relayd_backward_compat_group_by_session \ + test_session \ + test_string_utils \ + test_unix_socket \ + test_uri \ + test_utils_compat_poll \ + test_utils_compat_pthread \ + test_utils_expand_path \ + test_utils_parse_size_suffix \ + test_utils_parse_time_suffix \ + test_uuid if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS += test_ust_data @@ -70,7 +90,6 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) $(top_builddir)/src/bin/lttng-sessiond/condition-internal.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/save.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/notification-thread-commands.$(OBJEXT) \ - $(top_builddir)/src/bin/lttng-sessiond/shm.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/kernel.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/ht-cleanup.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/notification-thread.$(OBJEXT) \ @@ -79,6 +98,7 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) $(top_builddir)/src/bin/lttng-sessiond/channel.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/agent.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/kernel-consumer.$(OBJEXT) \ + $(top_builddir)/src/bin/lttng-sessiond/map.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/trace-kernel.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/rotation-thread.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/context.$(OBJEXT) \ @@ -87,6 +107,7 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) $(top_builddir)/src/bin/lttng-sessiond/fd-limit.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/notification-thread-events.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/event.$(OBJEXT) \ + $(top_builddir)/src/bin/lttng-sessiond/event-notifier-error-accounting.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/timer.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/snapshot.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/sessiond-config.$(OBJEXT) \ @@ -200,6 +221,18 @@ test_directory_handle_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) $(DL_LIBS) test_string_utils_SOURCES = test_string_utils.c test_string_utils_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSTRINGUTILS) $(DL_LIBS) +# Map api +test_map_SOURCES = test_map.c +test_map_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) + +# Map key api +test_map_key_SOURCES = test_map_key.c +test_map_key_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) + +# Map query api +test_map_query_SOURCES = test_map_query.c +test_map_query_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) + # Notification api test_notification_SOURCES = test_notification.c test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS) @@ -208,6 +241,10 @@ test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS) test_event_rule_SOURCES = test_event_rule.c test_event_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) +# Action api +test_action_SOURCES = test_action.c +test_action_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) +# # Condition api test_condition_SOURCES = test_condition.c test_condition_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) @@ -244,3 +281,7 @@ test_kernel_probe_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) # Event expression to bytecode test test_event_expr_to_bytecode_SOURCES = test_event_expr_to_bytecode.c test_event_expr_to_bytecode_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(LIBCOMMON) + +# Log level rule api +test_log_level_rule_SOURCES = test_log_level_rule.c +test_log_level_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) diff --git a/tests/unit/test_action.c b/tests/unit/test_action.c new file mode 100644 index 000000000..6e526c5ee --- /dev/null +++ b/tests/unit/test_action.c @@ -0,0 +1,120 @@ +/* + * test_action.c + * + * Unit tests for the action API. + * + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 14 + +static +void test_action_incr_value(void) +{ + int ret; + struct lttng_action *action = NULL; + struct lttng_action *action_from_buffer = NULL; + const char *map_name = "my_map_name"; + const char *session_name = "my_session_name"; + const char *first_part_key = "first_part_🥇_"; + const char *second_part_key = "_🥈_second_part"; + struct lttng_map_key *key = NULL; + enum lttng_action_status action_status; + enum lttng_map_key_status key_status; + struct lttng_payload buffer; + const struct lttng_map_key *key_from_buffer; + const struct lttng_map_key_token *token; + + lttng_payload_init(&buffer); + + /* Test key creation */ + key = lttng_map_key_create(); + ok(key, "Key created"); + + key_status = lttng_map_key_append_token_string(key, first_part_key); + ok(key_status == LTTNG_MAP_KEY_STATUS_OK, "Key append first string"); + + key_status = lttng_map_key_append_token_variable(key, LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME); + ok(key_status == LTTNG_MAP_KEY_STATUS_OK, "Key append event name variable"); + + key_status = lttng_map_key_append_token_string(key, second_part_key); + ok(key_status == LTTNG_MAP_KEY_STATUS_OK, "Key append second string"); + + /*Test incr value action creation */ + action = lttng_action_incr_value_create(); + ok(action, "Incr-value action created"); + + action_status = lttng_action_incr_value_set_session_name(action, session_name); + ok(action_status == LTTNG_ACTION_STATUS_OK, "incr-value set session name"); + + action_status = lttng_action_incr_value_set_map_name(action, map_name); + ok(action_status == LTTNG_ACTION_STATUS_OK, "incr-value set map name"); + + action_status = lttng_action_incr_value_set_key(action, key); + ok(action_status == LTTNG_ACTION_STATUS_OK, "incr-value set key"); + + /* Test incr value action serialization */ + ret = lttng_action_serialize(action, &buffer); + ok(ret == 0, "Incr value action serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + + (void) lttng_action_create_from_payload( + &view, &action_from_buffer); + } + ok(action_from_buffer, "Incr value action created from payload is non-null"); + + action_status = lttng_action_incr_value_get_key(action, &key_from_buffer); + ok(key_from_buffer, "Retrived key from incr value action"); + + token = lttng_map_key_get_token_at_index(key_from_buffer, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First key token is a string"); + + token = lttng_map_key_get_token_at_index(key_from_buffer, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Second key token is a variable"); + + token = lttng_map_key_get_token_at_index(key_from_buffer, 2); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "Third key token is a string"); + + lttng_payload_reset(&buffer); + + lttng_action_destroy(action); + lttng_action_destroy(action_from_buffer); + lttng_map_key_destroy(key); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_action_incr_value(); + return exit_status(); +} diff --git a/tests/unit/test_condition.c b/tests/unit/test_condition.c index 3b0144a0a..539e0b0b6 100644 --- a/tests/unit/test_condition.c +++ b/tests/unit/test_condition.c @@ -20,8 +20,10 @@ #include #include #include -#include +#include +#include #include +#include #include #include @@ -30,7 +32,7 @@ int lttng_opt_quiet = 1; int lttng_opt_verbose; int lttng_opt_mi; -#define NUM_TESTS 13 +#define NUM_TESTS 15 static void test_condition_event_rule(void) @@ -45,10 +47,16 @@ void test_condition_event_rule(void) const char *pattern="my_event_*"; const char *filter="msg_id == 23 && size >= 2048"; const char *exclusions[] = { "my_event_test1", "my_event_test2", "my_event_test3" }; + uint64_t _error_count = 420, _error_counter_index = 9999, error_count, error_counter_index; + struct lttng_log_level_rule *log_level_rule_at_least_as_severe = NULL; struct lttng_payload buffer; lttng_payload_init(&buffer); + /* Create log level rule */ + log_level_rule_at_least_as_severe = lttng_log_level_rule_at_least_as_severe_as_create(LTTNG_LOGLEVEL_WARNING); + assert(log_level_rule_at_least_as_severe); + tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_UST); ok(tracepoint, "tracepoint UST_DOMAIN"); @@ -58,8 +66,7 @@ void test_condition_event_rule(void) status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting filter"); - status = lttng_event_rule_tracepoint_set_log_level_range_lower_bound( - tracepoint, LTTNG_LOGLEVEL_WARNING); + status = lttng_event_rule_tracepoint_set_log_level_rule(tracepoint, log_level_rule_at_least_as_severe); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting log level range"); for (i = 0; i < 3; i++) { @@ -69,10 +76,14 @@ void test_condition_event_rule(void) "Setting exclusion pattern"); } - condition = lttng_condition_event_rule_create(tracepoint); + condition = lttng_condition_on_event_create(tracepoint); ok(condition, "Created condition"); - condition_status = lttng_condition_event_rule_get_rule( + /* Set the error count information */ + lttng_condition_on_event_set_error_count(condition, _error_count); + lttng_condition_on_event_set_error_counter_index(condition, _error_counter_index); + + condition_status = lttng_condition_on_event_get_rule( condition, &tracepoint_tmp); ok(condition_status == LTTNG_CONDITION_STATUS_OK, "Getting event rule from event rule condition"); @@ -94,10 +105,17 @@ void test_condition_event_rule(void) ok(lttng_condition_is_equal(condition, condition_from_buffer), "Serialized and de-serialized conditions are equal"); + /* Error count info is not considered in is_equal so test it separately */ + error_count = lttng_condition_on_event_get_error_count(condition_from_buffer); + error_counter_index = lttng_condition_on_event_get_error_counter_index(condition_from_buffer); + ok(error_count == _error_count, "Error count is the same. Got %" PRIu64 " Expected %" PRIu64, error_count, _error_count); + ok(error_counter_index == _error_counter_index, "Error count index is the same. Got %" PRIu64 " Expected %" PRIu64, error_count, _error_count); + lttng_payload_reset(&buffer); lttng_event_rule_destroy(tracepoint); lttng_condition_destroy(condition); lttng_condition_destroy(condition_from_buffer); + lttng_log_level_rule_destroy(log_level_rule_at_least_as_severe); } int main(int argc, const char *argv[]) diff --git a/tests/unit/test_event_rule.c b/tests/unit/test_event_rule.c index 53f16dbbd..050b9d119 100644 --- a/tests/unit/test_event_rule.c +++ b/tests/unit/test_event_rule.c @@ -18,14 +18,14 @@ #include #include #include -#include -#include +#include +#include #include #include #include #include -#include -#include +#include +#include #include #include #include @@ -37,7 +37,7 @@ int lttng_opt_quiet = 1; int lttng_opt_verbose; int lttng_opt_mi; -#define NUM_TESTS 187 +#define NUM_TESTS 144 struct tracepoint_test { enum lttng_domain_type type; @@ -47,17 +47,17 @@ struct tracepoint_test { static void test_event_rule_tracepoint_by_domain(const struct tracepoint_test *test) { - int ret; unsigned int count; struct lttng_event_rule *tracepoint = NULL; struct lttng_event_rule *tracepoint_from_buffer = NULL; enum lttng_event_rule_status status; enum lttng_domain_type domain_type, type; - enum lttng_loglevel_type log_level_type; const char *pattern="my_event_*"; const char *filter="msg_id == 23 && size >= 2048"; const char *tmp; const char *exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"}; + struct lttng_log_level_rule *log_level_rule = NULL; + const struct lttng_log_level_rule *log_level_rule_return = NULL; struct lttng_payload payload; type = test->type; @@ -65,6 +65,9 @@ void test_event_rule_tracepoint_by_domain(const struct tracepoint_test *test) lttng_payload_init(&payload); + log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); + assert(log_level_rule); + tracepoint = lttng_event_rule_tracepoint_create(type); ok(tracepoint, "tracepoint object."); @@ -84,28 +87,13 @@ void test_event_rule_tracepoint_by_domain(const struct tracepoint_test *test) ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); - status = lttng_event_rule_tracepoint_set_log_level_all(tracepoint); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting all log level."); - status = lttng_event_rule_tracepoint_get_log_level_type(tracepoint, &log_level_type); - ok(log_level_type == LTTNG_EVENT_LOGLEVEL_ALL, "getting loglevel type all."); - status = lttng_event_rule_tracepoint_get_log_level(tracepoint, &ret); - ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset loglevel value."); - - status = lttng_event_rule_tracepoint_set_log_level(tracepoint, LTTNG_LOGLEVEL_INFO); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting single loglevel."); - status = lttng_event_rule_tracepoint_get_log_level_type(tracepoint, &log_level_type); - ok(log_level_type == LTTNG_EVENT_LOGLEVEL_SINGLE, "getting loglevel type single."); - status = lttng_event_rule_tracepoint_get_log_level(tracepoint, &ret); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get loglevel value."); - ok(ret == LTTNG_LOGLEVEL_INFO, "loglevel value is equal."); - - status = lttng_event_rule_tracepoint_set_log_level_range_lower_bound(tracepoint, LTTNG_LOGLEVEL_WARNING); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel."); - status = lttng_event_rule_tracepoint_get_log_level_type(tracepoint, &log_level_type); - ok(log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE, "getting loglevel type range."); - status = lttng_event_rule_tracepoint_get_log_level(tracepoint, &ret); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get loglevel value."); - ok(ret == LTTNG_LOGLEVEL_WARNING, "loglevel valuei is equal."); + status = lttng_event_rule_tracepoint_get_log_level_rule(tracepoint, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); + + status = lttng_event_rule_tracepoint_set_log_level_rule(tracepoint, log_level_rule); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); + status = lttng_event_rule_tracepoint_get_log_level_rule(tracepoint, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); if (test->support_exclusion) { int i; @@ -154,6 +142,7 @@ void test_event_rule_tracepoint_by_domain(const struct tracepoint_test *test) lttng_payload_reset(&payload); lttng_event_rule_destroy(tracepoint); lttng_event_rule_destroy(tracepoint_from_buffer); + lttng_log_level_rule_destroy(log_level_rule); } static @@ -225,7 +214,7 @@ static void test_event_rule_syscall(void) lttng_event_rule_destroy(syscall_from_buffer); } -static void test_event_rule_uprobe(void) +static void test_event_rule_userspace_probe(void) { struct lttng_event_rule *uprobe = NULL; struct lttng_event_rule *uprobe_from_buffer = NULL; @@ -261,14 +250,10 @@ static void test_event_rule_uprobe(void) lttng_payload_init(&payload); - uprobe = lttng_event_rule_uprobe_create(); + uprobe = lttng_event_rule_userspace_probe_create(probe_location); ok(uprobe, "uprobe event rule object creation."); - status = lttng_event_rule_uprobe_set_location(uprobe, probe_location); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting uprobe event rule location."); - - status = lttng_event_rule_uprobe_get_location( + status = lttng_event_rule_userspace_probe_get_location( uprobe, &probe_location_tmp); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting uprobe event rule location."); @@ -276,10 +261,10 @@ static void test_event_rule_uprobe(void) probe_location, probe_location_tmp), "Location is equal."); - status = lttng_event_rule_uprobe_set_name(uprobe, probe_name); + status = lttng_event_rule_userspace_probe_set_event_name(uprobe, probe_name); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting uprobe event rule name: %s.", probe_name); - status = lttng_event_rule_uprobe_get_name(uprobe, &tmp); + status = lttng_event_rule_userspace_probe_get_event_name(uprobe, &tmp); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting uprobe name."); ok(!strcmp(probe_name, tmp), "Uprobe name are equal."); @@ -306,7 +291,7 @@ end: lttng_userspace_probe_location_lookup_method_destroy(lookup_method); } -static void test_event_rule_kprobe_by_location( +static void test_event_rule_kernel_probe_by_location( const struct lttng_kernel_probe_location *location) { struct lttng_event_rule *kprobe = NULL; @@ -323,21 +308,18 @@ static void test_event_rule_kprobe_by_location( lttng_payload_init(&payload); - kprobe = lttng_event_rule_kprobe_create(); + kprobe = lttng_event_rule_kernel_probe_create(location); ok(kprobe, "kprobe event rule object creation."); - status = lttng_event_rule_kprobe_set_location(kprobe, location); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting kprobe event rule location."); - status = lttng_event_rule_kprobe_get_location(kprobe, &_location); + status = lttng_event_rule_kernel_probe_get_location(kprobe, &_location); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting kprobe event rule location."); ok(lttng_kernel_probe_location_is_equal(location, _location), "Locations are equal."); - status = lttng_event_rule_kprobe_set_name(kprobe, probe_name); + status = lttng_event_rule_kernel_probe_set_event_name(kprobe, probe_name); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting kprobe event rule name: %s.", probe_name); - status = lttng_event_rule_kprobe_get_name(kprobe, &tmp); + status = lttng_event_rule_kernel_probe_get_event_name(kprobe, &tmp); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting kprobe name."); ok(!strcmp(probe_name, tmp), "kprobe name are equal."); @@ -361,7 +343,7 @@ static void test_event_rule_kprobe_by_location( lttng_event_rule_destroy(kprobe_from_buffer); } -static void test_event_rule_kprobe(void) +static void test_event_rule_kernel_probe(void) { struct lttng_kernel_probe_location *address_location = NULL; struct lttng_kernel_probe_location *symbol_location = NULL; @@ -371,8 +353,8 @@ static void test_event_rule_kprobe(void) assert(address_location); assert(symbol_location); - test_event_rule_kprobe_by_location(address_location); - test_event_rule_kprobe_by_location(symbol_location); + test_event_rule_kernel_probe_by_location(address_location); + test_event_rule_kernel_probe_by_location(symbol_location); lttng_kernel_probe_location_destroy(address_location); lttng_kernel_probe_location_destroy(symbol_location); @@ -383,7 +365,7 @@ int main(int argc, const char *argv[]) plan_tests(NUM_TESTS); test_event_rule_tracepoint(); test_event_rule_syscall(); - test_event_rule_uprobe(); - test_event_rule_kprobe(); + test_event_rule_userspace_probe(); + test_event_rule_kernel_probe(); return exit_status(); } diff --git a/tests/unit/test_log_level_rule.c b/tests/unit/test_log_level_rule.c new file mode 100644 index 000000000..9eccc7946 --- /dev/null +++ b/tests/unit/test_log_level_rule.c @@ -0,0 +1,187 @@ +/* + * Unit tests for the log level rule API. + * + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* For error.h. */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 29 + +static void test_log_level_rule_error(void) +{ + int level = 9000; + struct lttng_log_level_rule *exactly = + lttng_log_level_rule_exactly_create(level); + struct lttng_log_level_rule *at_least_as_severe = + lttng_log_level_rule_at_least_as_severe_as_create( + level); + + ok(lttng_log_level_rule_get_type(NULL) == LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN, "Get type on invalid pointer"); + + ok(lttng_log_level_rule_exactly_get_level(NULL, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (NULL, NULL) returns invalid"); + ok(lttng_log_level_rule_exactly_get_level(exactly, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (valid, NULL) returns invalid"); + ok(lttng_log_level_rule_exactly_get_level(NULL, &level) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (NULL, valid) returns invalid"); + + ok(lttng_log_level_rule_at_least_as_severe_as_get_level(NULL, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (NULL, NULL) returns invalid"); + ok(lttng_log_level_rule_at_least_as_severe_as_get_level(exactly, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (valid, NULL) returns invalid"); + ok(lttng_log_level_rule_at_least_as_severe_as_get_level(NULL, &level) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (NULL, valid) returns invalid"); + + lttng_log_level_rule_destroy(exactly); + lttng_log_level_rule_destroy(at_least_as_severe); +} + +static +void test_log_level_rule_serialize_deserialize(const struct lttng_log_level_rule *rule) +{ + struct lttng_log_level_rule *log_level_rule_from_buffer = NULL; + struct lttng_payload payload; + + lttng_payload_init(&payload); + + ok(lttng_log_level_rule_serialize(rule, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_log_level_rule_create_from_payload( + &view, &log_level_rule_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_log_level_rule_is_equal(rule, log_level_rule_from_buffer), "Serialized and from buffer are equal"); + + lttng_log_level_rule_destroy(log_level_rule_from_buffer); +} + +static +void test_log_level_rule_is_equal_exactly(void) +{ + int level = 9000, no_eq_level = 420; + struct lttng_log_level_rule *a, *b, *different_level, *different_type; + + /* Identical log level rules. */ + a = lttng_log_level_rule_exactly_create(level); + b = lttng_log_level_rule_exactly_create(level); + + /* Different level, same type. */ + different_level = lttng_log_level_rule_exactly_create(no_eq_level); + + /* Different type */ + different_type = lttng_log_level_rule_at_least_as_severe_as_create(level); + + assert(a && b && different_level && different_type); + + ok(lttng_log_level_rule_is_equal(a, a), "Same object is equal"); + ok(lttng_log_level_rule_is_equal(a, b), "Object a and b are equal"); + ok(!lttng_log_level_rule_is_equal(a, different_level), " Object of different levels are not equal"); + ok(!lttng_log_level_rule_is_equal(a, different_type), " Object of different types are not equal"); + + lttng_log_level_rule_destroy(a); + lttng_log_level_rule_destroy(b); + lttng_log_level_rule_destroy(different_level); + lttng_log_level_rule_destroy(different_type); +} + +static +void test_log_level_rule_is_equal_at_least_as_severe_as(void) +{ + int level = 9000, no_eq_level = 420; + struct lttng_log_level_rule *a, *b, *different_level, *different_type; + + /* Identical log level rules. */ + a = lttng_log_level_rule_at_least_as_severe_as_create(level); + b = lttng_log_level_rule_at_least_as_severe_as_create(level); + + /* Different level, same type. */ + different_level = lttng_log_level_rule_at_least_as_severe_as_create(no_eq_level); + + /* Different type */ + different_type = lttng_log_level_rule_exactly_create(level); + + assert(a && b && different_level && different_type); + + ok(lttng_log_level_rule_is_equal(a, a), "Same object is equal"); + ok(lttng_log_level_rule_is_equal(a, b), "Object a and b are equal"); + ok(!lttng_log_level_rule_is_equal(a, different_level), " Object of different levels are not equal"); + ok(!lttng_log_level_rule_is_equal(a, different_type), " Object of different types are not equal"); + + lttng_log_level_rule_destroy(a); + lttng_log_level_rule_destroy(b); + lttng_log_level_rule_destroy(different_level); + lttng_log_level_rule_destroy(different_type); +} + +static void test_log_level_rule_exactly(void) +{ + int level = 9000; + int _level; + struct lttng_log_level_rule *exactly = NULL; + enum lttng_log_level_rule_status status; + + exactly = lttng_log_level_rule_exactly_create(level); + + ok(exactly, "Log level exactly allocated"); + ok(lttng_log_level_rule_get_type(exactly) == + LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY, + "Log level rule exactly type"); + + status = lttng_log_level_rule_exactly_get_level(exactly, &_level); + ok(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK, "Get the level"); + ok(_level == level, "Level property is valid"); + + test_log_level_rule_is_equal_exactly(); + test_log_level_rule_serialize_deserialize(exactly); +} + +static void test_log_level_rule_at_least_as_severe_as(void) +{ + int level = 9000; + int _level; + struct lttng_log_level_rule *at_least_as_severe_as = NULL; + enum lttng_log_level_rule_status status; + + at_least_as_severe_as = lttng_log_level_rule_at_least_as_severe_as_create(level); + + ok(at_least_as_severe_as, "Log level at_least_as_severe_as allocated"); + ok(lttng_log_level_rule_get_type(at_least_as_severe_as) == + LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS, + "Log level rule at_least_as_severe_as type"); + + status = lttng_log_level_rule_at_least_as_severe_as_get_level(at_least_as_severe_as, &_level); + ok(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK, "Get the level"); + ok(_level == level, "Level property is valid"); + + test_log_level_rule_is_equal_at_least_as_severe_as(); + test_log_level_rule_serialize_deserialize(at_least_as_severe_as); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_log_level_rule_exactly(); + test_log_level_rule_at_least_as_severe_as(); + test_log_level_rule_error(); + return exit_status(); +} diff --git a/tests/unit/test_map.c b/tests/unit/test_map.c new file mode 100644 index 000000000..f8f6f8474 --- /dev/null +++ b/tests/unit/test_map.c @@ -0,0 +1,439 @@ +/* + * test_map.c + * + * Unit tests for the map API. + * + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "lttng/domain.h" +#include "lttng/map/map.h" +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#define NUM_TESTS 70 + +static +void test_map_key_value_pair_serialize_deserialize(void) +{ + struct lttng_map_key_value_pair *kv; + struct lttng_map_key_value_pair *kv_from_payload; + struct lttng_payload buffer; + enum lttng_map_status map_status; + const char *kv_from_payload_key, *key = "ma_clé"; + int64_t kv_from_payload_value, value = 133121; + int ret; + + diag("Simple lttng_map_key_value_pair tests"); + + lttng_payload_init(&buffer); + + kv = lttng_map_key_value_pair_create(key, value); + ok(kv, "Key-value pair created"); + + /* Test incr value action serialization */ + ret = lttng_map_key_value_pair_serialize(kv, &buffer); + ok(ret == 0, "Key-value pair serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + + (void) lttng_map_key_value_pair_create_from_payload( + &view, &kv_from_payload); + } + ok(kv_from_payload, "Key-value pair created from payload is non-null"); + + map_status = lttng_map_key_value_pair_get_key(kv_from_payload, + &kv_from_payload_key); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key-value pair 1 key"); + ok(strcmp(kv_from_payload_key, key) == 0, "Key-value pair from payload has correct key"); + + map_status = lttng_map_key_value_pair_get_value(kv_from_payload, + &kv_from_payload_value); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key-value pair 1 value"); + ok(kv_from_payload_value == value, "Key-value pair from payload has correct value"); + + lttng_payload_reset(&buffer); + lttng_map_key_value_pair_destroy(kv); + lttng_map_key_value_pair_destroy(kv_from_payload); +} + +static +void test_map_key_value_pair_list_serialize_deserialize(void) +{ + struct lttng_map_key_value_pair *kv; + const struct lttng_map_key_value_pair *kv_from_payload = NULL; + + struct lttng_map_key_value_pair_list *kv_pair_list; + struct lttng_map_key_value_pair_list *kv_pair_list_from_payload; + + struct lttng_payload buffer; + enum lttng_map_status map_status; + const char *kv_from_payload_key, *key1 = "ma_clé", *key2 = "autre_clé"; + int64_t kv_from_payload_value, value1 = 123456, value2 = 98765; + enum lttng_map_key_value_pair_list_type list_type = + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID; + uint64_t identifier = 3192112; + bool summed_all_cpus = true; + unsigned int kv_count; + int ret; + + diag("Simple lttng_map_key_value_pair_list tests"); + + lttng_payload_init(&buffer); + + kv_pair_list = lttng_map_key_value_pair_list_create(list_type, + summed_all_cpus); + ok(kv_pair_list, "Key-value pair_list list created"); + + map_status = lttng_map_key_value_pair_list_set_identifier(kv_pair_list, + identifier); + ok(kv_pair_list, "Key-value set identifier"); + + kv = lttng_map_key_value_pair_create(key1, value1); + ok(kv, "Key-value pair 1 created"); + + map_status = lttng_map_key_value_pair_list_append_key_value(kv_pair_list, kv); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value 1 appended to list"); + + kv = lttng_map_key_value_pair_create(key2, value2); + ok(kv, "Key-value pair 2 created"); + + map_status = lttng_map_key_value_pair_list_append_key_value(kv_pair_list, kv); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value 2 appended to list"); + + /* Test incr value action serialization */ + ret = lttng_map_key_value_pair_list_serialize(kv_pair_list, &buffer); + ok(ret == 0, "Key-value pair_list list serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + + (void) lttng_map_key_value_pair_list_create_from_payload( + &view, &kv_pair_list_from_payload); + } + ok(kv_pair_list_from_payload, "Key-value pair list created from payload is non-null"); + + ok(lttng_map_key_value_pair_list_get_summed_all_cpu(kv_pair_list_from_payload) == summed_all_cpus, + "Got the expected summed all cpu state"); + + ok(lttng_map_key_value_pair_list_get_type(kv_pair_list_from_payload) == list_type, + "Got the expected list type"); + ok(lttng_map_key_value_pair_list_get_identifer(kv_pair_list_from_payload) == identifier, + "Got the expected list identifier"); + + map_status = lttng_map_key_value_pair_list_get_count( + kv_pair_list_from_payload, &kv_count); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got key value pair count"); + ok(kv_count == 2, "Got the right key value pair count"); + + kv_from_payload = lttng_map_key_value_pair_list_get_at_index( + kv_pair_list_from_payload, 0); + ok(kv_from_payload, "Key-value pair 1 created from payload"); + + map_status = lttng_map_key_value_pair_get_key(kv_from_payload, + &kv_from_payload_key); + ok(strcmp(kv_from_payload_key, key1) == 0, "Key-value pair 1 from payload has correct key"); + + map_status = lttng_map_key_value_pair_get_value(kv_from_payload, + &kv_from_payload_value); + ok(kv_from_payload_value == value1, "Key-value pair 1 from payload has correct value"); + + kv_from_payload = lttng_map_key_value_pair_list_get_at_index( + kv_pair_list_from_payload, 1); + ok(kv_from_payload, "Key-value pair 2 created from payload"); + + map_status = lttng_map_key_value_pair_get_key(kv_from_payload, + &kv_from_payload_key); + ok(strcmp(kv_from_payload_key, key2) == 0, "Key-value pair 2 from payload has correct key"); + + map_status = lttng_map_key_value_pair_get_value(kv_from_payload, + &kv_from_payload_value); + ok(kv_from_payload_value == value2, "Key-value pair 2 from payload has correct value"); + + lttng_payload_reset(&buffer); + lttng_map_key_value_pair_list_destroy(kv_pair_list); + lttng_map_key_value_pair_list_destroy(kv_pair_list_from_payload); +} + +static +void test_map_content_serialize_deserialize(void) +{ + struct lttng_map_content *map_content, *map_content_from_payload; + enum lttng_map_status map_status; + struct lttng_payload buffer; + struct lttng_map_key_value_pair *kv1, *kv2; + const char *key1 = "ma_clé", *key2 = "autre_clé"; + uint64_t value1 = 123456, value2 = 98765; + enum lttng_map_key_value_pair_list_type list_type1 = + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID; + enum lttng_map_key_value_pair_list_type list_type2 = + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID; + enum lttng_map_key_value_pair_list_type list_type3 = + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED; + uint64_t id1 = 958323, id2 = 121942; + struct lttng_map_key_value_pair_list *kv_pair_list1, *kv_pair_list2, *kv_pair_list3; + const struct lttng_map_key_value_pair_list *kv_pair_list1_from_payload; + const struct lttng_map_key_value_pair_list *kv_pair_list2_from_payload; + const struct lttng_map_key_value_pair_list *kv_pair_list3_from_payload; + unsigned int list_count; + enum lttng_buffer_type buffer_type = LTTNG_BUFFER_PER_UID; + int ret; + + diag("Simple lttng_map_content tests"); + + lttng_payload_init(&buffer); + + kv_pair_list1 = lttng_map_key_value_pair_list_create(list_type1, true); + map_status = lttng_map_key_value_pair_list_set_identifier(kv_pair_list1, id1); + + kv_pair_list2 = lttng_map_key_value_pair_list_create(list_type2, false); + map_status = lttng_map_key_value_pair_list_set_identifier(kv_pair_list2, id2); + + kv_pair_list3 = lttng_map_key_value_pair_list_create(list_type3, true); + + kv1 = lttng_map_key_value_pair_create(key1, value1); + map_status = lttng_map_key_value_pair_list_append_key_value(kv_pair_list1, kv1); + + kv2 = lttng_map_key_value_pair_create(key2, value2); + map_status = lttng_map_key_value_pair_list_append_key_value(kv_pair_list2, kv2); + + map_content = lttng_map_content_create(buffer_type); + + map_status = lttng_map_content_append_key_value_list(map_content, kv_pair_list1); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value list 1 appended to map_content"); + + map_status = lttng_map_content_append_key_value_list(map_content, kv_pair_list2); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value list 2 appended to map_content"); + + map_status = lttng_map_content_append_key_value_list(map_content, kv_pair_list3); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value list 3 appended to map_content"); + + ret = lttng_map_content_serialize(map_content, &buffer); + ok(ret == 0, "Map list serialized"); + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_content_create_from_payload( + &view, &map_content_from_payload); + } + + ok(map_content, "map content created from payload is non-null"); + map_status = lttng_map_content_get_count(map_content_from_payload, &list_count); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got key value pair count"); + ok(list_count == 3, "Got the expected key-value list count"); + + ok(lttng_map_content_get_buffer_type(map_content_from_payload) == buffer_type, + "Got the expected buffer type"); + + kv_pair_list1_from_payload = lttng_map_content_get_at_index(map_content_from_payload, 0); + ok(kv_pair_list1_from_payload, "Key-value pair list created from payload is non-null"); + + ok(lttng_map_key_value_pair_list_get_type(kv_pair_list1_from_payload) == list_type1, + "Got the expected list type"); + + ok(lttng_map_key_value_pair_list_get_identifer(kv_pair_list1_from_payload) == id1, + "Got the expected list identifier"); + + kv_pair_list2_from_payload = lttng_map_content_get_at_index(map_content_from_payload, 1); + ok(kv_pair_list2_from_payload, "Key-value pair list created from payload is non-null"); + + ok(lttng_map_key_value_pair_list_get_type(kv_pair_list2_from_payload) == list_type2, + "Got the expected list type"); + + ok(lttng_map_key_value_pair_list_get_identifer(kv_pair_list2_from_payload) == id2, + "Got the expected list identifier"); + + kv_pair_list3_from_payload = lttng_map_content_get_at_index(map_content_from_payload, 2); + ok(kv_pair_list3_from_payload, "Key-value pair list created from payload is non-null"); + + ok(lttng_map_key_value_pair_list_get_type(kv_pair_list3_from_payload) == list_type3, + "Got the expected list type"); + + lttng_payload_reset(&buffer); + lttng_map_content_destroy(map_content); + lttng_map_content_destroy(map_content_from_payload); +} + +static +void test_map(void) +{ + int ret; + struct lttng_payload buffer; + struct lttng_map *map, *map_from_payload = NULL; + enum lttng_map_status map_status; + const char *map_name = "map_name", *map_name_from_payload; + unsigned int dimension_count = 1; + uint64_t first_dim_size = 423; + uint64_t dimension_sizes[1] = {first_dim_size}; + enum lttng_domain_type domain = LTTNG_DOMAIN_UST; + enum lttng_buffer_type buffer_type = LTTNG_BUFFER_PER_UID; + enum lttng_map_bitness bitness = LTTNG_MAP_BITNESS_32BITS; + enum lttng_map_boundary_policy boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + bool coalesce_hits = true; + + + diag("Simple lttng_map tests"); + lttng_payload_init(&buffer); + + map_status = lttng_map_create(map_name, dimension_count, + dimension_sizes, domain, buffer_type, bitness, + boundary_policy, coalesce_hits, &map); + ok(map_status == LTTNG_MAP_STATUS_OK, "Created map"); + + lttng_map_set_is_enabled(map, true); + + ret = lttng_map_serialize(map, &buffer); + ok(ret == 0, "Map serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_create_from_payload( + &view, &map_from_payload); + } + ok(map_from_payload, "Map created from payload"); + + ok(lttng_map_get_dimension_count(map_from_payload) == dimension_count, + "Got the expected dimension count from payload"); + + ok(lttng_map_get_is_enabled(map_from_payload) == 1, + "Got the expected enabled state from payload"); + + ok(lttng_map_get_bitness(map_from_payload) == bitness, + "Got the expected bitness from payload"); + + ok(lttng_map_get_domain(map_from_payload) == domain, + "Got the expected domain from payload"); + + ok(lttng_map_get_buffer_type(map_from_payload) == buffer_type, + "Got the expected buffer type from payload"); + + ok(lttng_map_get_boundary_policy(map_from_payload) == boundary_policy, + "Got the expected boundary policy from payload"); + + ok(lttng_map_get_coalesce_hits(map_from_payload) == coalesce_hits, + "Got the expected coalesce hits value from payload"); + + map_status = lttng_map_get_name(map_from_payload, &map_name_from_payload); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got map name from payload"); + ok(strcmp(map_name_from_payload, map_name) == 0, + "Got the expected map name from payload"); + + lttng_map_destroy(map); + lttng_map_destroy(map_from_payload); + lttng_payload_reset(&buffer); +} + +static +void test_map_list(void) +{ + int ret; + struct lttng_payload buffer; + enum lttng_map_status map_status; + struct lttng_map *map1, *map2; + const struct lttng_map *map1_from_payload = NULL, *map2_from_payload = NULL; + struct lttng_map_list *map_list, *map_list_from_payload = NULL; + const char *map1_name = "map_name_1", *map1_name_from_payload; + const char *map2_name = "map_name_2", *map2_name_from_payload; + unsigned int dimension_count = 1, map_count = 0; + uint64_t first_dim_size = 423; + uint64_t dimension_sizes[1] = {first_dim_size}; + enum lttng_domain_type domain = LTTNG_DOMAIN_KERNEL; + enum lttng_buffer_type buffer_type1 = LTTNG_BUFFER_PER_PID, buffer_type2 = LTTNG_BUFFER_PER_UID; + enum lttng_map_bitness bitness = LTTNG_MAP_BITNESS_64BITS; + enum lttng_map_boundary_policy boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + bool coalesce_hits = false; + + diag("Simple lttng_map_list tests"); + + lttng_payload_init(&buffer); + + map_status = lttng_map_create(map1_name, dimension_count, + dimension_sizes, domain, buffer_type1, bitness, + boundary_policy, coalesce_hits, &map1); + ok(map_status == LTTNG_MAP_STATUS_OK, "Created map 1"); + lttng_map_set_is_enabled(map1, true); + + map_status = lttng_map_create(map2_name, dimension_count, + dimension_sizes, domain, buffer_type2, bitness, + boundary_policy, coalesce_hits, &map2); + ok(map_status == LTTNG_MAP_STATUS_OK, "Created map 2"); + lttng_map_set_is_enabled(map2, true); + + map_list = lttng_map_list_create(); + ok(map_list, "Map list created"); + + map_status = lttng_map_list_add(map_list, map1); + ok(map_status == LTTNG_MAP_STATUS_OK, "Map 1 added to map list"); + + map_status = lttng_map_list_add(map_list, map2); + ok(map_status == LTTNG_MAP_STATUS_OK, "Map 1 added to map list"); + + ret = lttng_map_list_serialize(map_list, &buffer); + ok(ret == 0, "Map list serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_list_create_from_payload( + &view, &map_list_from_payload); + } + + map_status = lttng_map_list_get_count(map_list, &map_count); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got map count from payload"); + ok(map_count == 2, "Got the right map count from payload"); + + map1_from_payload = lttng_map_list_get_at_index(map_list, 0); + ok(map1_from_payload, "Got first map from payload"); + map_status = lttng_map_get_name(map1_from_payload, &map1_name_from_payload); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got map 1 name from payload"); + ok(strcmp(map1_name_from_payload, map1_name) == 0, + "Got right map 1 name from payload"); + ok(lttng_map_get_is_enabled(map1_from_payload) == 1, + "Got right map 1 enabled state from payload"); + + map2_from_payload = lttng_map_list_get_at_index(map_list, 1); + ok(map2_from_payload, "Got first map from payload"); + map_status = lttng_map_get_name(map2_from_payload, &map2_name_from_payload); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got map 2 name from payload"); + ok(strcmp(map2_name_from_payload, map2_name) == 0, + "Got right map 2 name from payload"); + ok(lttng_map_get_is_enabled(map2_from_payload) == 1, + "Got right map 2 enabled state from payload"); + + lttng_map_destroy(map1); + lttng_map_destroy(map2); + lttng_map_list_destroy(map_list); + lttng_map_list_destroy(map_list_from_payload); + lttng_payload_reset(&buffer); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + + test_map(); + test_map_list(); + test_map_key_value_pair_serialize_deserialize(); + test_map_key_value_pair_list_serialize_deserialize(); + test_map_content_serialize_deserialize(); + + return exit_status(); +} diff --git a/tests/unit/test_map_key.c b/tests/unit/test_map_key.c new file mode 100644 index 000000000..bdc156220 --- /dev/null +++ b/tests/unit/test_map_key.c @@ -0,0 +1,132 @@ +/* + * test_map_key.c + * + * Unit tests for the map-key API. + * + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#define NUM_TESTS 38 + +const char *key_str1 = "simple_string"; +const char *key_str2 = "${EVENT_NAME}"; +const char *key_str3 = "foo_${EVENT_NAME}"; +const char *key_str4 = "foo_${EVENT_NAME}_bar"; +const char *key_str5 = "${EVENT_NAME}_bar_${EVENT_NAME}"; +const char *key_str6 = "foo_${NON_EXISTING_VAR}"; +const char *key_str7 = "foo_${}"; +const char *key_str8 = "foo_${PROVIDER_NAME}"; + +static +void test_map_key(void) +{ + struct lttng_map_key *key; + enum lttng_map_key_status status; + const struct lttng_map_key_token *token; + const struct lttng_map_key_token_variable *var_token; + unsigned int count; + + key = lttng_map_key_parse_from_string(key_str6); + ok(!key, "Failed to create key from \"%s\" as expected", key_str6); + + key = lttng_map_key_parse_from_string(key_str7); + ok(!key, "Failed to create key from \"%s\" as expected", key_str7); + + key = lttng_map_key_parse_from_string(key_str1); + ok(key, "Created key from \"%s\"", key_str1); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str1"); + ok(count == 1, "Got correct token count for key_str1"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First token of string type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str2); + ok(key, "Created key from \"%s\"", key_str2); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str2"); + ok(count == 1, "Got correct token count for key_str2"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "First token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str3); + ok(key, "Created key from \"%s\"", key_str3); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str3"); + ok(count == 2, "Got correct token count for key_str3"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First token of string type"); + token = lttng_map_key_get_token_at_index(key, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Second token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str4); + ok(key, "Created key from \"%s\"", key_str4); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str4"); + ok(count == 3, "Got correct token count for key_str4"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First token of string type"); + token = lttng_map_key_get_token_at_index(key, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Second token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + token = lttng_map_key_get_token_at_index(key, 2); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "Third token of string type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str5); + ok(key, "Created key from \"%s\"", key_str5); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str5"); + ok(count == 3, "Got correct token count for key_str5"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "First token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + token = lttng_map_key_get_token_at_index(key, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "Second token of string type"); + token = lttng_map_key_get_token_at_index(key, 2); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Third token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str8); + ok(key, "Created key from \"%s\"", key_str8); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str8"); + ok(count == 2, "Got correct token count for key_str8"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First token of string type"); + token = lttng_map_key_get_token_at_index(key, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Second token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME, "PROVIDER_NAME variable type"); + lttng_map_key_destroy(key); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_map_key(); + return exit_status(); +} diff --git a/tests/unit/test_map_query.c b/tests/unit/test_map_query.c new file mode 100644 index 000000000..76dd8b633 --- /dev/null +++ b/tests/unit/test_map_query.c @@ -0,0 +1,291 @@ +/* + * test_map_query.c + * + * Unit tests for the map query API. + * + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#define NUM_TESTS 56 + +static +void test_map_query_key_filter_all_cpus_all_uids_64_no_sum(void) +{ + int ret; + struct lttng_payload buffer; + struct lttng_map_query *query, *query_from_payload = NULL; + const char *filter = "pitarifique_key"; + const char *filter_from_payload; + enum lttng_map_query_status status; + + enum lttng_map_query_config_cpu config_cpu = + LTTNG_MAP_QUERY_CONFIG_CPU_ALL; + enum lttng_map_query_config_buffer config_buffer = + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL; + enum lttng_map_query_config_app_bitness config_bitness = + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + + diag("Map query, all cpus, all uids, 64bits, no sum"); + lttng_payload_init(&buffer); + + query = lttng_map_query_create(config_cpu, config_buffer, + config_bitness); + ok(query, "Map query created succesfully"); + + status = lttng_map_query_add_key_filter(query, filter); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Key filter created succesfully"); + + /* Add a cpu manually with a _CONFIG_CPU_ALL should fail. */ + status = lttng_map_query_add_cpu(query, 121); + ok(status == LTTNG_MAP_QUERY_STATUS_INVALID, "Adding a cpu failed as expected"); + + /* Add a pid manually with a _CONFIG_BUFFER_UST_UID_ should fail. */ + status = lttng_map_query_add_pid(query, 931214); + ok(status == LTTNG_MAP_QUERY_STATUS_INVALID, "Adding a PID of uid map query failed as expected"); + + ret = lttng_map_query_serialize(query, &buffer); + ok(ret == 0, "Map query serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_query_create_from_payload( + &view, &query_from_payload); + } + + ok(query_from_payload, "Map query created from payload"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "Getting app bitness config from payload"); + + ok(lttng_map_query_get_config_buffer(query_from_payload) == + config_buffer, "Buffer config"); + + ok(lttng_map_query_get_config_cpu(query_from_payload) == + config_cpu, "CPU config"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "App bitness config"); + + status = lttng_map_query_get_key_filter(query_from_payload, &filter_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Key filter"); + ok(strcmp(filter_from_payload, filter) == 0, "Key filter"); + + lttng_map_query_destroy(query); + lttng_map_query_destroy(query_from_payload); + lttng_payload_reset(&buffer); +} + +static +void test_map_query_key_some_cpu_some_uid_summed_by_uid(void) +{ + int ret; + struct lttng_payload buffer; + struct lttng_map_query *query, *query_from_payload = NULL; + unsigned int cpu_count, uid_count; + bool sum_by_cpu = false, sum_by_uid = true; + uid_t uid1 = 12131, uid1_from_payload; + int cpu1 = 494581, cpu1_from_payload; + enum lttng_map_query_status status; + + enum lttng_map_query_config_cpu config_cpu = + LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET; + enum lttng_map_query_config_buffer config_buffer = + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET; + enum lttng_map_query_config_app_bitness config_bitness = + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + + diag("Map query some cpus, some uids, summed by uid"); + lttng_payload_init(&buffer); + + query = lttng_map_query_create(config_cpu, config_buffer, + config_bitness); + ok(query, "Map query created succesfully"); + + status = lttng_map_query_set_sum_by_cpu(query, sum_by_cpu); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Setting sum-by-cpu option"); + + status = lttng_map_query_set_sum_by_uid(query, sum_by_uid); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Setting sum-by-uid option"); + + status = lttng_map_query_add_cpu(query, cpu1); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Adding a cpu %d", cpu1); + + status = lttng_map_query_add_uid(query, uid1); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Adding a uid %d", uid1); + + /* Add a pid manually with a _CONFIG_BUFFER_UST_UID_ should fail. */ + status = lttng_map_query_add_pid(query, 931214); + ok(status == LTTNG_MAP_QUERY_STATUS_INVALID, "Adding a PID of uid map query failed as expected"); + + ret = lttng_map_query_serialize(query, &buffer); + ok(ret == 0, "Map query serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_query_create_from_payload( + &view, &query_from_payload); + } + + ok(query_from_payload, "Map query created from payload"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "Getting app bitness config from payload"); + + ok(lttng_map_query_get_config_sum_by_cpu(query_from_payload) == + sum_by_cpu, "Getting sum-by-cpu config from payload"); + + ok(lttng_map_query_get_config_sum_by_uid(query_from_payload) == + sum_by_uid, "Getting sum-by-uid config from payload"); + + ok(lttng_map_query_get_config_buffer(query_from_payload) == + config_buffer, "Buffer config"); + + ok(lttng_map_query_get_config_cpu(query_from_payload) == + config_cpu, "CPU config"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "App bitness config"); + + status = lttng_map_query_get_cpu_count(query, &cpu_count); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting cpu count"); + ok(cpu_count == 1, "Getting cpu count"); + + status = lttng_map_query_get_cpu_at_index(query, 0, &cpu1_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting cpu count"); + ok(cpu1_from_payload == cpu1, "Getting cpu value"); + + status = lttng_map_query_get_uid_count(query, &uid_count); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting uid count"); + ok(uid_count == 1, "Getting uid count"); + + status = lttng_map_query_get_uid_at_index(query, 0, &uid1_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting uid count"); + ok(uid1_from_payload == uid1, "Getting uid value"); + + lttng_map_query_destroy(query); + lttng_map_query_destroy(query_from_payload); + lttng_payload_reset(&buffer); +} + +static +void test_map_query_key_one_cpu_some_pid_summed_by_cpu(void) +{ + int ret; + struct lttng_payload buffer; + struct lttng_map_query *query, *query_from_payload = NULL; + unsigned int cpu_count, pid_count; + pid_t pid1 = 12131, pid1_from_payload; + int cpu1 = 494581, cpu1_from_payload; + bool sum_by_cpu = true, sum_by_pid = false; + enum lttng_map_query_status status; + + enum lttng_map_query_config_cpu config_cpu = + LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET; + enum lttng_map_query_config_buffer config_buffer = + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET; + enum lttng_map_query_config_app_bitness config_bitness = + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + + diag("Map query one cpu, some pid, summed by cpu"); + lttng_payload_init(&buffer); + + query = lttng_map_query_create(config_cpu, config_buffer, + config_bitness); + ok(query, "Map query created succesfully"); + + status = lttng_map_query_set_sum_by_cpu(query, sum_by_cpu); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Setting sum-by-cpu option"); + + status = lttng_map_query_set_sum_by_pid(query, sum_by_pid); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Setting sum-by-pid option"); + + status = lttng_map_query_add_cpu(query, cpu1); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Adding a cpu %d", cpu1); + + status = lttng_map_query_add_pid(query, pid1); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Adding a pid %d", pid1); + + /* Add a pid manually with a _CONFIG_BUFFER_UST_PID_ should fail. */ + status = lttng_map_query_add_uid(query, 931214); + ok(status == LTTNG_MAP_QUERY_STATUS_INVALID, "Adding a UID of pid map query failed as expected"); + + ret = lttng_map_query_serialize(query, &buffer); + ok(ret == 0, "Map query serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_query_create_from_payload( + &view, &query_from_payload); + } + + ok(query_from_payload, "Map query created from payload"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "Getting app bitness config from payload"); + + ok(lttng_map_query_get_config_sum_by_cpu(query_from_payload) == + sum_by_cpu, "Getting sum-by-cpu config from payload"); + + ok(lttng_map_query_get_config_sum_by_pid(query_from_payload) == + sum_by_pid, "Getting sum-by-pid config from payload"); + + ok(lttng_map_query_get_config_buffer(query_from_payload) == + config_buffer, "Getting buffer config from payload"); + + ok(lttng_map_query_get_config_cpu(query_from_payload) == + config_cpu, "Getting CPU config from payload"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "App bitness config from payload"); + + status = lttng_map_query_get_cpu_count(query, &cpu_count); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting cpu count from payload"); + ok(cpu_count == 1, "Getting cpu count from payload"); + + status = lttng_map_query_get_cpu_at_index(query, 0, &cpu1_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting cpu count from payload"); + ok(cpu1_from_payload == cpu1, "Getting cpu value from payload"); + + status = lttng_map_query_get_pid_count(query, &pid_count); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting pid count from payload"); + ok(pid_count == 1, "Getting pid count from payload"); + + status = lttng_map_query_get_pid_at_index(query, 0, &pid1_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting pid count from payload"); + ok(pid1_from_payload == pid1, "Getting pid value from payload"); + + lttng_map_query_destroy(query); + lttng_map_query_destroy(query_from_payload); + lttng_payload_reset(&buffer); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + + test_map_query_key_filter_all_cpus_all_uids_64_no_sum(); + test_map_query_key_some_cpu_some_uid_summed_by_uid(); + test_map_query_key_one_cpu_some_pid_summed_by_cpu(); + + return exit_status(); +} diff --git a/tests/unit/test_ust_data.c b/tests/unit/test_ust_data.c index ec61ffbad..330d49e59 100644 --- a/tests/unit/test_ust_data.c +++ b/tests/unit/test_ust_data.c @@ -125,7 +125,8 @@ static void test_create_ust_event(void) ev.type = LTTNG_EVENT_TRACEPOINT; ev.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; - ret = trace_ust_create_event(&ev, NULL, NULL, NULL, false, &event); + ret = trace_ust_create_event(0, ev.name, NULL, ev.type, ev.loglevel_type, + ev.loglevel, NULL, NULL, NULL, false, &event); ok(ret == LTTNG_OK, "Create UST event"); @@ -181,7 +182,8 @@ static void test_create_ust_event_exclusion(void) strncpy(LTTNG_EVENT_EXCLUSION_NAME_AT(exclusion, 1), random_name, LTTNG_SYMBOL_NAME_LEN); - ret = trace_ust_create_event(&ev, NULL, NULL, exclusion, false, &event); + ret = trace_ust_create_event(0, ev.name, NULL, ev.type, ev.loglevel_type, + ev.loglevel, NULL, NULL, exclusion, false, &event); exclusion = NULL; ok(ret != LTTNG_OK, "Create UST event with identical exclusion names fails"); @@ -219,7 +221,8 @@ static void test_create_ust_event_exclusion(void) strncpy(LTTNG_EVENT_EXCLUSION_NAME_AT(exclusion_copy, 1), LTTNG_EVENT_EXCLUSION_NAME_AT(exclusion, 1), LTTNG_SYMBOL_NAME_LEN); - ret = trace_ust_create_event(&ev, NULL, NULL, exclusion, false, &event); + ret = trace_ust_create_event(0, ev.name, NULL, ev.type, ev.loglevel_type, + ev.loglevel, NULL, NULL, exclusion, false, &event); exclusion = NULL; ok(ret == LTTNG_OK, "Create UST event with different exclusion names"); diff --git a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c index 9ab650933..1a2882f99 100644 --- a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c +++ b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c @@ -93,7 +93,6 @@ int main(int argc, char **argv) if (argc != 4) { fprintf(stderr, "Error: Missing argument\n"); fprintf(stderr, "USAGE: %s PATH_WAIT_FILE PATH1_TO_OPEN PATH2_TO_OPEN\n", argv[0]); - fprintf(stderr, "USAGE: %s PATH_WAIT_FILE\n", argv[0]); ret = -1; goto error; } diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh index a1e142487..de2cdc324 100644 --- a/tests/utils/utils.sh +++ b/tests/utils/utils.sh @@ -325,6 +325,28 @@ function lttng_disable_kernel_syscall_fail() lttng_disable_kernel_syscall 1 "$@" } +function lttng_enable_kernel_function_event () +{ + local expected_to_fail="$1" + local sess_name="$2" + local target="$3" + local event_name="$4" + + "$TESTDIR/../src/bin/lttng/$LTTNG_BIN" enable-event --kernel --function="$target" "$event_name" -s "$sess_name" > "$OUTPUT_DEST" 2> "$ERROR_OUTPUT_DEST" + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test $ret -ne "0" + ok $? "Enable kernel function event for session $sess_name failed as expected" + else + ok $ret "Enable kernel function event for session $sess_name" + fi +} + +function lttng_enable_kernel_function_event_ok () +{ + lttng_enable_kernel_function_event 0 "$@" +} + function lttng_enable_kernel_userspace_probe_event () { local expected_to_fail="$1" @@ -543,6 +565,10 @@ function start_lttng_sessiond_opt() local withtap=$1 local load_path=$2 + # The rest of the arguments will be passed directly to lttng-sessiond. + shift + shift + local env_vars="" local consumerd="" @@ -587,10 +613,10 @@ function start_lttng_sessiond_opt() # Have a load path ? if [ -n "$load_path" ]; then # shellcheck disable=SC2086 - env $env_vars --load "$load_path" --background "$consumerd" + env $env_vars --load "$load_path" --background "$consumerd" "$@" else # shellcheck disable=SC2086 - env $env_vars --background "$consumerd" + env $env_vars --background "$consumerd" "$@" fi #$DIR/../src/bin/lttng-sessiond/$SESSIOND_BIN --background --consumerd32-path="$DIR/../src/bin/lttng-consumerd/lttng-consumerd" --consumerd64-path="$DIR/../src/bin/lttng-consumerd/lttng-consumerd" --verbose-consumer >>/tmp/sessiond.log 2>&1 status=$? @@ -1491,7 +1517,8 @@ function lttng_load() local expected_to_fail=$1 local opts=$2 - $TESTDIR/../src/bin/lttng/$LTTNG_BIN load $opts 1> $OUTPUT_DEST 2> $ERROR_OUTPUT_DEST + diag "$TESTDIR/../src/bin/lttng/$LTTNG_BIN load $opts" + $TESTDIR/../src/bin/lttng/$LTTNG_BIN load $opts ret=$? if [[ $expected_to_fail -eq "1" ]]; then test $ret -ne "0" @@ -2104,3 +2131,85 @@ function lttng_clear_all () $TESTDIR/../src/bin/lttng/$LTTNG_BIN clear --all 1> $OUTPUT_DEST 2> $ERROR_OUTPUT_DEST ok $? "Clear all lttng sessions" } + +function lttng_add_trigger() +{ + local expected_to_fail="$1" + local trigger_name="$2" + shift 2 + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN add-trigger --id "$trigger_name" "$@" 1> /dev/null 2> /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Add trigger $trigger_name failed as expected" + else + ok $ret "Add trigger $trigger_name" + fi +} + +function lttng_remove_trigger() +{ + local expected_to_fail="$1" + local trigger_name="$2" + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN remove-trigger "$trigger_name" 1> /dev/null 2> /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Remove trigger $trigger_name failed as expected" + else + ok $ret "Remove trigger $trigger_name" + fi +} + +function lttng_add_trigger_ok() +{ + lttng_add_trigger 0 "$@" +} + +function lttng_add_trigger_fail() +{ + lttng_add_trigger 1 "$@" +} + +function lttng_remove_trigger_ok() +{ + lttng_remove_trigger 0 "$@" +} + +function lttng_add_map() +{ + local expected_to_fail="$1" + local map_name="$2" + local session_name="$3" + local domain="$4" + local bitness="$5" + local buf_option="$6" + local coalesced="$7" + + local args=("$domain" "--bitness=$bitness" "$buf_option" "--session=$session_name" "$map_name") + if [ -n "$buf_option" ] + then + args+=("$buf_option") + fi + + if [ -n "$coalesced" ] + then + args+=("$coalesced") + fi + + "$FULL_LTTNG_BIN" add-map ${args[@]} > /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Map $domain --bitness $bitness creating failed as expected" + else + ok $ret "Map $domain --bitness $bitness created succesfully" + fi +} + +function lttng_add_map_ok() +{ + lttng_add_map 0 "$@" +} -- 2.34.1