lttng-ctl: add capture descriptor feature to event rule condition API
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Tue, 31 Mar 2020 01:26:09 +0000 (21:26 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 5 Mar 2021 19:50:55 +0000 (14:50 -0500)
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 <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Change-Id: I68f163e6eb011e690f648d9ff8ea3eb89af004ff
Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479

include/lttng/condition/event-rule-internal.h
include/lttng/condition/event-rule.h
include/lttng/trigger/trigger.h
src/common/conditions/event-rule.c

index a4e02c50919aabe76843a43474fde019db58f32e..55b61949a0406688f48ec9ddf4e6e4d3115a689e 100644 (file)
 #include <common/buffer-view.h>
 #include <common/macros.h>
 #include <lttng/condition/evaluation-internal.h>
+#include <common/dynamic-array.h>
 
 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;
index 973cbe69bfcae834efc72b1d9ae92db03fc363de..91fce32d63a22a91858866d8342bb0c599f051b9 100644 (file)
@@ -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
index 5fef53fd415f15e69b859f48e9e1ce947f291fb9..de4e93d70705bbb9787eb4e2a2e3faa54d152f18 100644 (file)
@@ -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.
  *
index 06ba18fa51abbc8c2dbdb6acc167dfbd04d9df93..e7967e6ab2abfb3a924f73706c156bea543fe9f7 100644 (file)
@@ -8,11 +8,15 @@
 #include <assert.h>
 #include <common/error.h>
 #include <common/macros.h>
+#include <inttypes.h>
 #include <lttng/condition/condition-internal.h>
 #include <lttng/condition/event-rule-internal.h>
 #include <lttng/condition/event-rule.h>
+#include <lttng/event-expr-internal.h>
+#include <lttng/event-expr.h>
 #include <lttng/event-rule/event-rule-internal.h>
 #include <stdbool.h>
+#include <stdint.h>
 
 #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,
This page took 0.034334 seconds and 5 git commands to generate.