From 381140130b556bdccc8c7a45d843dcd34c6d7b8f Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Mon, 30 Mar 2020 21:26:09 -0400 Subject: [PATCH] lttng-ctl: add capture descriptor feature to event rule condition API MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This patch makes it possible to add capture descriptors to an event rule condition. A capture descriptor describes a value to be captured by an LTTng tracer when an event occurs and to be attached to the corresponding condition's evaluation. The lttng_condition_event_rule_append_capture_descriptor() function appends a capture descriptor to an event rule condition. The capture descriptor is an event expression object of which the ownership is moved to the condition object. The verb "append" here indicates that the order of the condition's capture descriptors is significant: indeed, when liblttng-ctl gets an upgrade to the "event rule hit" condition evaluation API in the future to make its user read the captured values, such values will be ordered the same way, therefore sharing the same numeric indexes. Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau Change-Id: I68f163e6eb011e690f648d9ff8ea3eb89af004ff Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- include/lttng/condition/event-rule-internal.h | 11 +- include/lttng/condition/event-rule.h | 66 +++ include/lttng/trigger/trigger.h | 3 + src/common/conditions/event-rule.c | 514 ++++++++++++++++-- 4 files changed, 552 insertions(+), 42 deletions(-) diff --git a/include/lttng/condition/event-rule-internal.h b/include/lttng/condition/event-rule-internal.h index a4e02c509..55b61949a 100644 --- a/include/lttng/condition/event-rule-internal.h +++ b/include/lttng/condition/event-rule-internal.h @@ -12,18 +12,15 @@ #include #include #include +#include struct lttng_condition_event_rule { struct lttng_condition parent; struct lttng_event_rule *rule; -}; -struct lttng_condition_event_rule_comm { - /* length excludes the header's length. */ - uint32_t event_rule_length; - /* Event rule follows. */ - char payload[]; -} LTTNG_PACKED; + /* Array of `struct lttng_event_expr *`. */ + struct lttng_dynamic_pointer_array capture_descriptors; +}; struct lttng_evaluation_event_rule { struct lttng_evaluation parent; diff --git a/include/lttng/condition/event-rule.h b/include/lttng/condition/event-rule.h index 973cbe69b..91fce32d6 100644 --- a/include/lttng/condition/event-rule.h +++ b/include/lttng/condition/event-rule.h @@ -16,6 +16,8 @@ extern "C" { #endif +struct lttng_event_expr; + /** * Event rule conditions allows an action to be taken whenever an event matching * the event rule is hit by the tracers. @@ -72,6 +74,70 @@ lttng_evaluation_event_rule_get_trigger_name( const struct lttng_evaluation *evaluation, const char **name); +/* + * Appends (transfering the ownership) the capture descriptor `expr` to + * the event rule condition `condition`. + * + * Returns: + * + * `LTTNG_CONDITION_STATUS_OK`: + * Success. + * + * `LTTNG_CONDITION_STATUS_ERROR`: + * Memory error. + * + * `LTTNG_CONDITION_STATUS_INVALID`: + * * `condition` is `NULL`. + * * The type of `condition` is not + * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * * `expr` is `NULL`. + * * `expr` is not a locator expression, that is, its type is not + * one of: + * + * * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD` + * * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD` + * * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD` + * * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT` + */ +extern enum lttng_condition_status +lttng_condition_event_rule_append_capture_descriptor( + struct lttng_condition *condition, + struct lttng_event_expr *expr); + +/* + * Sets `*count` to the number of capture descriptors in the event rule + * condition `condition`. + * + * Returns: + * + * `LTTNG_CONDITION_STATUS_OK`: + * Success. + * + * `LTTNG_CONDITION_STATUS_INVALID`: + * * `condition` is `NULL`. + * * The type of `condition` is not + * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * * `count` is `NULL`. + */ +extern enum lttng_condition_status +lttng_condition_event_rule_get_capture_descriptor_count( + const struct lttng_condition *condition, unsigned int *count); + +/* + * Returns the capture descriptor (borrowed) of the event rule condition + * `condition` at the index `index`, or `NULL` if: + * + * * `condition` is `NULL`. + * * The type of `condition` is not + * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * * `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()). + */ +extern const struct lttng_event_expr * +lttng_condition_event_rule_get_capture_descriptor_at_index( + const struct lttng_condition *condition, unsigned int index); + #ifdef __cplusplus } #endif diff --git a/include/lttng/trigger/trigger.h b/include/lttng/trigger/trigger.h index 5fef53fd4..de4e93d70 100644 --- a/include/lttng/trigger/trigger.h +++ b/include/lttng/trigger/trigger.h @@ -52,6 +52,9 @@ enum lttng_trigger_firing_policy { * The caller retains the ownership of both the condition and action * and both must be kept alive for the lifetime of the trigger object. * + * If the action is a notification action with capture descriptors, + * the condition must be an event rule condition. + * * A trigger must be registered in order to become activate and can * be destroyed after its registration. * diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/event-rule.c index 06ba18fa5..e7967e6ab 100644 --- a/src/common/conditions/event-rule.c +++ b/src/common/conditions/event-rule.c @@ -8,11 +8,15 @@ #include #include #include +#include #include #include #include +#include +#include #include #include +#include #define IS_EVENT_RULE_CONDITION(condition) \ (lttng_condition_get_type(condition) == \ @@ -58,15 +62,143 @@ end: return valid; } +/* + * Serializes the C string `str` into `buf`. + * + * Encoding is the length of `str` plus one (for the null character), + * and then the string, including its null terminator. + */ +static +int serialize_cstr(const char *str, struct lttng_dynamic_buffer *buf) +{ + int ret; + const uint32_t len = strlen(str) + 1; + + /* Serialize the length, including the null terminator. */ + DBG("Serializing C string's length (including null terminator): " + "%" PRIu32, len); + ret = lttng_dynamic_buffer_append(buf, &len, sizeof(len)); + if (ret) { + goto end; + } + + /* Serialize the string. */ + DBG("Serializing C string: '%s'", str); + ret = lttng_dynamic_buffer_append(buf, str, len); + if (ret) { + goto end; + } + +end: + return ret; +} + +/* + * Serializes the event expression `expr` into `buf`. + */ +static +int serialize_event_expr(const struct lttng_event_expr *expr, + struct lttng_payload *payload) +{ + const uint8_t type = expr->type; + int ret; + + /* Serialize the expression's type. */ + DBG("Serializing event expression's type: %d", expr->type); + ret = lttng_dynamic_buffer_append(&payload->buffer, &type, sizeof(type)); + if (ret) { + goto end; + } + + /* Serialize the expression */ + switch (expr->type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + const struct lttng_event_expr_field *field_expr = + container_of(expr, + const struct lttng_event_expr_field, + parent); + + /* Serialize the field name. */ + DBG("Serializing field event expression's field name: '%s'", + field_expr->name); + ret = serialize_cstr(field_expr->name, &payload->buffer); + if (ret) { + goto end; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const struct lttng_event_expr_app_specific_context_field *field_expr = + container_of(expr, + const struct lttng_event_expr_app_specific_context_field, + parent); + + /* Serialize the provider name. */ + DBG("Serializing app-specific context field event expression's " + "provider name: '%s'", + field_expr->provider_name); + ret = serialize_cstr(field_expr->provider_name, &payload->buffer); + if (ret) { + goto end; + } + + /* Serialize the type name. */ + DBG("Serializing app-specific context field event expression's " + "type name: '%s'", + field_expr->provider_name); + ret = serialize_cstr(field_expr->type_name, &payload->buffer); + if (ret) { + goto end; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + const struct lttng_event_expr_array_field_element *elem_expr = + container_of(expr, + const struct lttng_event_expr_array_field_element, + parent); + const uint32_t index = elem_expr->index; + + /* Serialize the index. */ + DBG("Serializing array field element event expression's " + "index: %u", elem_expr->index); + ret = lttng_dynamic_buffer_append(&payload->buffer, &index, sizeof(index)); + if (ret) { + goto end; + } + + /* Serialize the parent array field expression. */ + DBG("Serializing array field element event expression's " + "parent array field event expression."); + ret = serialize_event_expr(elem_expr->array_field_expr, payload); + if (ret) { + goto end; + } + + break; + } + default: + break; + } + +end: + return ret; +} + static int lttng_condition_event_rule_serialize( const struct lttng_condition *condition, struct lttng_payload *payload) { int ret; - size_t header_offset, size_before_payload; struct lttng_condition_event_rule *event_rule; - struct lttng_condition_event_rule_comm event_rule_comm = {}; - struct lttng_condition_event_rule_comm *header = NULL; + /* Used for iteration and communication (size matters). */ + uint32_t i, capture_descr_count; if (!condition || !IS_EVENT_RULE_CONDITION(condition)) { ret = -1; @@ -77,28 +209,82 @@ static int lttng_condition_event_rule_serialize( event_rule = container_of( condition, struct lttng_condition_event_rule, parent); - header_offset = payload->buffer.size; - ret = lttng_dynamic_buffer_append(&payload->buffer, &event_rule_comm, - sizeof(event_rule_comm)); + DBG("Serializing event rule condition's event rule"); + ret = lttng_event_rule_serialize(event_rule->rule, payload); if (ret) { goto end; } - size_before_payload = payload->buffer.size; - ret = lttng_event_rule_serialize(event_rule->rule, payload); + capture_descr_count = lttng_dynamic_pointer_array_get_count( + &event_rule->capture_descriptors); + DBG("Serializing event rule condition's capture descriptor count: %" PRIu32, + capture_descr_count); + ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_descr_count, + sizeof(capture_descr_count)); if (ret) { goto end; } - /* Update payload size. */ - header = (struct lttng_condition_event_rule_comm *) - ((char *) payload->buffer.data + header_offset); - header->event_rule_length = payload->buffer.size - size_before_payload; + for (i = 0; i < capture_descr_count; i++) { + const struct lttng_event_expr *expr = + lttng_dynamic_pointer_array_get_pointer( + &event_rule->capture_descriptors, i); + + DBG("Serializing event rule condition's capture descriptor %" PRIu32, + i); + ret = serialize_event_expr(expr, payload); + if (ret) { + goto end; + } + } end: return ret; } +static +bool capture_descriptors_are_equal( + const struct lttng_condition_event_rule *condition_a, + const struct lttng_condition_event_rule *condition_b) +{ + bool is_equal = true; + size_t capture_descr_count_a; + size_t capture_descr_count_b; + size_t i; + + capture_descr_count_a = lttng_dynamic_pointer_array_get_count( + &condition_a->capture_descriptors); + capture_descr_count_b = lttng_dynamic_pointer_array_get_count( + &condition_b->capture_descriptors); + + if (capture_descr_count_a != capture_descr_count_b) { + goto not_equal; + } + + for (i = 0; i < capture_descr_count_a; i++) { + const struct lttng_event_expr *expr_a = + lttng_dynamic_pointer_array_get_pointer( + &condition_a->capture_descriptors, + i); + const struct lttng_event_expr *expr_b = + lttng_dynamic_pointer_array_get_pointer( + &condition_b->capture_descriptors, + i); + + if (!lttng_event_expr_is_equal(expr_a, expr_b)) { + goto not_equal; + } + } + + goto end; + +not_equal: + is_equal = false; + +end: + return is_equal; +} + static bool lttng_condition_event_rule_is_equal( const struct lttng_condition *_a, const struct lttng_condition *_b) @@ -116,6 +302,12 @@ static bool lttng_condition_event_rule_is_equal( } is_equal = lttng_event_rule_is_equal(a->rule, b->rule); + if (!is_equal) { + goto end; + } + + is_equal = capture_descriptors_are_equal(a, b); + end: return is_equal; } @@ -129,9 +321,16 @@ static void lttng_condition_event_rule_destroy( condition, struct lttng_condition_event_rule, parent); lttng_event_rule_put(event_rule->rule); + lttng_dynamic_pointer_array_reset(&event_rule->capture_descriptors); free(event_rule); } +static +void destroy_event_expr(void *ptr) +{ + lttng_event_expr_destroy(ptr); +} + struct lttng_condition *lttng_condition_event_rule_create( struct lttng_event_rule *rule) { @@ -158,38 +357,186 @@ struct lttng_condition *lttng_condition_event_rule_create( condition->rule = rule; rule = NULL; + lttng_dynamic_pointer_array_init(&condition->capture_descriptors, + destroy_event_expr); + parent = &condition->parent; end: return parent; } +static +uint64_t uint_from_buffer(const struct lttng_buffer_view *view, size_t size, + size_t *offset) +{ + uint64_t ret; + const struct lttng_buffer_view uint_view = + lttng_buffer_view_from_view(view, *offset, size); + + if (!lttng_buffer_view_is_valid(&uint_view)) { + ret = UINT64_C(-1); + goto end; + } + + switch (size) { + case 1: + ret = (uint64_t) *uint_view.data; + break; + case sizeof(uint32_t): + { + uint32_t u32; + + memcpy(&u32, uint_view.data, sizeof(u32)); + ret = (uint64_t) u32; + break; + } + case sizeof(ret): + memcpy(&ret, uint_view.data, sizeof(ret)); + break; + default: + abort(); + } + + *offset += size; + +end: + return ret; +} + +static +const char *str_from_buffer(const struct lttng_buffer_view *view, + size_t *offset) +{ + uint64_t len; + const char *ret; + + len = uint_from_buffer(view, sizeof(uint32_t), offset); + if (len == UINT64_C(-1)) { + goto error; + } + + ret = &view->data[*offset]; + + if (!lttng_buffer_view_contains_string(view, ret, len)) { + goto error; + } + + *offset += len; + goto end; + +error: + ret = NULL; + +end: + return ret; +} + +static +struct lttng_event_expr *event_expr_from_payload( + struct lttng_payload_view *view, size_t *offset) +{ + struct lttng_event_expr *expr = NULL; + const char *str; + uint64_t type; + + type = uint_from_buffer(&view->buffer, sizeof(uint8_t), offset); + if (type == UINT64_C(-1)) { + goto error; + } + + switch (type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + str = str_from_buffer(&view->buffer, offset); + if (!str) { + goto error; + } + + expr = lttng_event_expr_event_payload_field_create(str); + break; + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + str = str_from_buffer(&view->buffer, offset); + if (!str) { + goto error; + } + + expr = lttng_event_expr_channel_context_field_create(str); + break; + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const char *provider_name; + const char *type_name; + + provider_name = str_from_buffer(&view->buffer, offset); + if (!provider_name) { + goto error; + } + + type_name = str_from_buffer(&view->buffer, offset); + if (!type_name) { + goto error; + } + + expr = lttng_event_expr_app_specific_context_field_create( + provider_name, type_name); + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + struct lttng_event_expr *array_field_expr; + const uint64_t index = uint_from_buffer( + &view->buffer, sizeof(uint32_t), offset); + + if (index == UINT64_C(-1)) { + goto error; + } + + /* Array field expression is the encoded after this. */ + array_field_expr = event_expr_from_payload(view, offset); + if (!array_field_expr) { + goto error; + } + + /* Move ownership of `array_field_expr` to new expression. */ + expr = lttng_event_expr_array_field_element_create( + array_field_expr, (unsigned int) index); + if (!expr) { + /* `array_field_expr` not moved: destroy it. */ + lttng_event_expr_destroy(array_field_expr); + } + + break; + } + default: + abort(); + } + + goto end; + +error: + lttng_event_expr_destroy(expr); + expr = NULL; + +end: + return expr; +} + LTTNG_HIDDEN ssize_t lttng_condition_event_rule_create_from_payload( struct lttng_payload_view *view, struct lttng_condition **_condition) { - ssize_t offset, event_rule_length; + ssize_t consumed_length; + size_t offset = 0; + ssize_t event_rule_length; + uint32_t i, capture_descr_count; struct lttng_condition *condition = NULL; struct lttng_event_rule *event_rule = NULL; - const struct lttng_condition_event_rule_comm *header; - const struct lttng_payload_view header_view = - lttng_payload_view_from_view( - view, 0, sizeof(*header)); if (!view || !_condition) { goto error; } - if (!lttng_payload_view_is_valid(&header_view)) { - ERR("Failed to initialize from malformed event rule condition: buffer too short to contain header"); - goto error; - } - - header = (const struct lttng_condition_event_rule_comm *) - header_view.buffer.data; - offset = sizeof(*header); - - /* lttng_event_rule payload. */ + /* Struct lttng_event_rule. */ { struct lttng_payload_view event_rule_view = lttng_payload_view_from_view(view, offset, -1); @@ -202,30 +549,53 @@ ssize_t lttng_condition_event_rule_create_from_payload( goto error; } - if ((size_t) header->event_rule_length != event_rule_length) { + /* Create condition (no capture descriptors yet) at this point. */ + condition = lttng_condition_event_rule_create(event_rule); + if (!condition) { goto error; } - /* Move to the end of the payload. */ - offset += header->event_rule_length; - /* Acquires a reference to the event rule. */ - condition = lttng_condition_event_rule_create(event_rule); - if (!condition) { + /* 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; } + /* Capture descriptors. */ + for (i = 0; i < capture_descr_count; i++) { + struct lttng_event_expr *expr = event_expr_from_payload( + view, &offset); + enum lttng_condition_status status; + + if (!expr) { + goto error; + } + + /* Move ownership of `expr` to `condition`. */ + status = lttng_condition_event_rule_append_capture_descriptor( + condition, expr); + if (status != LTTNG_CONDITION_STATUS_OK) { + /* `expr` not moved: destroy it. */ + lttng_event_expr_destroy(expr); + goto error; + } + } + + consumed_length = (ssize_t) offset; *_condition = condition; condition = NULL; goto end; error: - offset = -1; + consumed_length = -1; end: lttng_event_rule_put(event_rule); lttng_condition_put(condition); - return offset; + return consumed_length; } LTTNG_HIDDEN @@ -266,6 +636,80 @@ enum lttng_condition_status lttng_condition_event_rule_get_rule( return status; } +enum lttng_condition_status +lttng_condition_event_rule_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 = + container_of(condition, + struct lttng_condition_event_rule, parent); + + /* Only accept l-values. */ + if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !expr || + !lttng_event_expr_is_lvalue(expr)) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &event_rule_cond->capture_descriptors, expr); + if (ret) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + +end: + return status; +} + +enum lttng_condition_status +lttng_condition_event_rule_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 = + container_of(condition, + const struct lttng_condition_event_rule, + parent); + + if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !count) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count( + &event_rule_cond->capture_descriptors); + +end: + return status; +} + +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; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition) || + index >= lttng_dynamic_pointer_array_get_count( + &event_rule_cond->capture_descriptors)) { + goto end; + } + + expr = lttng_dynamic_pointer_array_get_pointer( + &event_rule_cond->capture_descriptors, index); + +end: + return expr; +} + LTTNG_HIDDEN ssize_t lttng_evaluation_event_rule_create_from_payload( struct lttng_payload_view *view, -- 2.34.1