From: Philippe Proulx Date: Tue, 31 Mar 2020 01:23:25 +0000 (-0400) Subject: lttng-ctl: add event expression API X-Git-Tag: v2.13.0-rc1~270 X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=commitdiff_plain;h=48c475643635d97d56fc251ca0b54e67ff25a51f lttng-ctl: add event expression API This new API having the prefix `lttng_event_expr` makes it possible to create event expressions. An event expression is an expression which can be evaluated by an LTTng when an event occurs. An LTTng event filter expression, for example, is an event expression, although the current interface to specify an event filter expression is to use the string version, while this new API offers functions to build an expression tree. Currently, the event expressions API offers functions to create the following types of expressions: Event payload field expression: The named payload field of an event. Equivalent CLI string example: next_prio Channel context field: The named per-channel context field of an event. Equivalent CLI string example: $ctx.vpid Application-specific context field: The named application-specific context field of an event. Equivalent CLI string example: $app.iga:active-clients Array field element: The element of an array field. Equivalent CLI string examples: my_field[4] $ctx.some_context[5] The attentive reader will have discovered that you cannot create all the expressions needed to create any supported filter expression. This is because the goal of this patch is to pave the way for the capture descriptor feature of notify trigger actions, which, for the moment, only need the expressions above. However, the event expression API can be extended to support all the filtering subexpressions in the future. Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau Change-Id: I34770fa8900f0bfb90bb2cbf4a7de59a1645b738 Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- diff --git a/include/Makefile.am b/include/Makefile.am index 957a01d25..54fec49a2 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -103,6 +103,7 @@ lttnginclude_HEADERS = \ lttng/channel.h \ lttng/domain.h \ lttng/event.h \ + lttng/event-expr.h \ lttng/handle.h \ lttng/session.h \ lttng/lttng-error.h \ @@ -176,6 +177,7 @@ noinst_HEADERS = \ lttng/channel-internal.h \ lttng/domain-internal.h \ lttng/event-internal.h \ + lttng/event-expr-internal.h \ lttng/rotate-internal.h \ lttng/ref-internal.h \ lttng/location-internal.h \ diff --git a/include/lttng/event-expr-internal.h b/include/lttng/event-expr-internal.h new file mode 100644 index 000000000..88fa695de --- /dev/null +++ b/include/lttng/event-expr-internal.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_EXPR_INTERNAL_H +#define LTTNG_EVENT_EXPR_INTERNAL_H + +#include +#include + +struct lttng_event_expr { + enum lttng_event_expr_type type; +}; + +/* + * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD` and + * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`. + */ +struct lttng_event_expr_field { + struct lttng_event_expr parent; + char *name; +}; + +/* `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD` */ +struct lttng_event_expr_app_specific_context_field { + struct lttng_event_expr parent; + char *provider_name; + char *type_name; +}; + +/* `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT` */ +struct lttng_event_expr_array_field_element { + struct lttng_event_expr parent; + + /* Owned by this */ + struct lttng_event_expr *array_field_expr; + + unsigned int index; +}; + +/* + * Returns whether or not `expr` is an l-value (locator value). + */ +static inline +bool lttng_event_expr_is_lvalue(const struct lttng_event_expr *expr) +{ + assert(expr); + return expr->type == LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD || + expr->type == LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD || + expr->type == LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD || + expr->type == LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT; +} + +#endif /* LTTNG_EVENT_EXPR_INTERNAL_H */ diff --git a/include/lttng/event-expr.h b/include/lttng/event-expr.h new file mode 100644 index 000000000..911648779 --- /dev/null +++ b/include/lttng/event-expr.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_EXPR_H +#define LTTNG_EVENT_EXPR_H + +#include + +struct lttng_event_expr; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Types of an event expression. + */ +enum lttng_event_expr_type { + /* + * Returned by lttng_event_expr_get_type() with an invalid + * parameter. + */ + LTTNG_EVENT_EXPR_TYPE_INVALID = -1, + + /* + * The named payload field of an event. + * + * Command-line expression example: + * + * next_prio + */ + LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD = 0, + + /* + * The named per-channel context field of an event. + * + * Command-line expression example: + * + * $ctx.vpid + */ + LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD = 1, + + /* + * The named application-specific context field of an event. + * + * Command-line expression example: + * + * $app.iga:active-clients + */ + LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD = 2, + + /* + * The element of an array field. + * + * Command-line expression example: + * + * my_field[4] + * $ctx.some_context[5][1] + */ + LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT = 3, +}; + +/* + * Event expression API status codes. + */ +enum lttng_event_expr_status { + /* + * Invalid parameter. + */ + LTTNG_EVENT_EXPR_STATUS_INVALID = -1, + + /* + * Success. + */ + LTTNG_EVENT_EXPR_STATUS_OK = 0, +}; + +/* + * Returns the type of the event expression `expr`, or + * `LTTNG_EVENT_EXPR_TYPE_INVALID` if `expr` is `NULL`. + */ +extern enum lttng_event_expr_type lttng_event_expr_get_type( + const struct lttng_event_expr *expr); + +/* + * Creates an event payload field expression for the payload field named + * `field_name`. + * + * Returns `NULL` if: + * + * * There's a memory error. + * * `field_name` is `NULL`. + */ +extern struct lttng_event_expr *lttng_event_expr_event_payload_field_create( + const char *field_name); + +/* + * Returns the field name of the event payload field expression `expr`, + * or `NULL` if: + * + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD`. + */ +extern const char *lttng_event_expr_event_payload_field_get_name( + const struct lttng_event_expr *expr); + +/* + * Creates a per-channel context field expression for the per-channel + * context field named `field_name`. + * + * Returns `NULL` if: + * + * * There's a memory error. + * * `field_name` is `NULL`. + */ +extern struct lttng_event_expr * +lttng_event_expr_channel_context_field_create(const char *field_name); + +/* + * Returns the field name of the per-channel context field + * expression `expr`, or `NULL` if: + * + * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`. + */ +extern const char *lttng_event_expr_channel_context_field_get_name( + const struct lttng_event_expr *expr); + +/* + * Creates an application-specific context field expression for the + * application-specific context field provided by the provider named + * `provider_name` and having the type named `type_name`. + * + * Returns `NULL` if: + * + * * There's a memory error. + * * `provider_name` is `NULL`. + * * `type_name` is `NULL`. + */ +extern struct lttng_event_expr * +lttng_event_expr_app_specific_context_field_create( + const char *provider_name, const char *type_name); + +/* + * Returns the provider name of the application-specific context field + * expression `expr`, or `NULL` if: + * + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`. + */ +extern const char * +lttng_event_expr_app_specific_context_field_get_provider_name( + const struct lttng_event_expr *expr); + +/* + * Returns the type name of the application-specific context field + * expression `expr`, or `NULL` if: + * + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`. + */ +extern const char * +lttng_event_expr_app_specific_context_field_get_type_name( + const struct lttng_event_expr *expr); + +/* + * Creates an array field element expression for the parent array field + * `array_field_expr` (transfering the ownership) and the index `index`. + * + * Returns `NULL` if: + * + * * There's a memory error. + * * `array_field_expr` is `NULL`. + * * `array_field_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 struct lttng_event_expr *lttng_event_expr_array_field_element_create( + struct lttng_event_expr *array_field_expr, + unsigned int index); + +/* + * Returns the parent array field expression of the array field element + * expression `expr`, or `NULL` if: + * + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`. + */ +extern const struct lttng_event_expr * +lttng_event_expr_array_field_element_get_parent_expr( + const struct lttng_event_expr *expr); + +/* + * Sets `*index` to the index of the array field element expression + * `expr`. + * + * Returns: + * + * `LTTNG_EVENT_EXPR_STATUS_OK`: + * Success. + * + * `LTTNG_EVENT_EXPR_STATUS_INVALID`: + * * `expr` is `NULL`. + * * The type of `expr` is not + * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`. + * * `index` is `NULL`. + */ +extern enum lttng_event_expr_status +lttng_event_expr_array_field_element_get_index( + const struct lttng_event_expr *expr, unsigned int *index); + +/* + * Returns whether or not the event expressions `expr_a` and `expr_b` + * are equal. + * + * `expr_a` and `expr_b` can be `NULL`. + */ +extern bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a, + const struct lttng_event_expr *expr_b); + +/* + * Destroys the event expression `expr` if not `NULL`. + */ +extern void lttng_event_expr_destroy(struct lttng_event_expr *expr); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_EXPR_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index d1e569ac2..2b8a56e37 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/src/lib/lttng-ctl/Makefile.am b/src/lib/lttng-ctl/Makefile.am index 8c271f5ef..28aead7ad 100644 --- a/src/lib/lttng-ctl/Makefile.am +++ b/src/lib/lttng-ctl/Makefile.am @@ -9,6 +9,7 @@ lib_LTLIBRARIES = liblttng-ctl.la liblttng_ctl_la_SOURCES = lttng-ctl.c snapshot.c lttng-ctl-helper.h \ lttng-ctl-health.c save.c load.c deprecated-symbols.c \ channel.c rotate.c event.c destruction-handle.c clear.c \ + event-expr.c \ tracker.c liblttng_ctl_la_LDFLAGS = \ diff --git a/src/lib/lttng-ctl/event-expr.c b/src/lib/lttng-ctl/event-expr.c new file mode 100644 index 000000000..273866ca3 --- /dev/null +++ b/src/lib/lttng-ctl/event-expr.c @@ -0,0 +1,431 @@ +/* + * event-expr.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 + +enum lttng_event_expr_type lttng_event_expr_get_type( + const struct lttng_event_expr *expr) +{ + enum lttng_event_expr_type type; + + if (!expr) { + type = LTTNG_EVENT_EXPR_TYPE_INVALID; + goto end; + } + + type = expr->type; + +end: + return type; +} + +static +struct lttng_event_expr *create_empty_expr(enum lttng_event_expr_type type, + size_t size) +{ + struct lttng_event_expr *expr; + + expr = zmalloc(size); + if (!expr) { + goto end; + } + + expr->type = type; + +end: + return expr; +} + +static +struct lttng_event_expr_field *create_field_event_expr( + enum lttng_event_expr_type type, + const char *name) +{ + struct lttng_event_expr_field *expr = + container_of( + create_empty_expr(type, sizeof(*expr)), + struct lttng_event_expr_field, parent); + + if (!expr) { + goto error; + } + + assert(name); + expr->name = strdup(name); + if (!expr->name) { + goto error; + } + + goto end; + +error: + lttng_event_expr_destroy(&expr->parent); + +end: + return expr; +} + +struct lttng_event_expr *lttng_event_expr_event_payload_field_create( + const char *field_name) +{ + struct lttng_event_expr *expr = NULL; + + if (!field_name) { + goto end; + } + + expr = &create_field_event_expr( + LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD, + field_name)->parent; + +end: + return expr; +} + +struct lttng_event_expr *lttng_event_expr_channel_context_field_create( + const char *field_name) +{ + struct lttng_event_expr *expr = NULL; + + if (!field_name) { + goto end; + } + + expr = &create_field_event_expr( + LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD, + field_name)->parent; + +end: + return expr; +} + +struct lttng_event_expr *lttng_event_expr_app_specific_context_field_create( + const char *provider_name, const char *type_name) +{ + struct lttng_event_expr_app_specific_context_field *expr = NULL; + + if (!type_name || !provider_name) { + goto error; + } + + expr = container_of(create_empty_expr( + LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD, + sizeof(*expr)), + struct lttng_event_expr_app_specific_context_field, + parent); + if (!expr) { + goto error; + } + + expr->provider_name = strdup(provider_name); + if (!expr->provider_name) { + goto error; + } + + expr->type_name = strdup(type_name); + if (!expr->type_name) { + goto error; + } + + goto end; + +error: + lttng_event_expr_destroy(&expr->parent); + +end: + return &expr->parent; +} + +struct lttng_event_expr *lttng_event_expr_array_field_element_create( + struct lttng_event_expr *array_field_expr, + unsigned int index) +{ + struct lttng_event_expr_array_field_element *expr = NULL; + + /* The parent array field expression must be an l-value */ + if (!array_field_expr || + !lttng_event_expr_is_lvalue(array_field_expr)) { + goto error; + } + + expr = container_of(create_empty_expr( + LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT, + sizeof(*expr)), + struct lttng_event_expr_array_field_element, + parent); + if (!expr) { + goto error; + } + + expr->array_field_expr = array_field_expr; + expr->index = index; + goto end; + +error: + lttng_event_expr_destroy(&expr->parent); + +end: + return &expr->parent; +} + +const char *lttng_event_expr_event_payload_field_get_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_field, parent)->name; + +end: + return ret; +} + +const char *lttng_event_expr_channel_context_field_get_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_field, parent)->name; + +end: + return ret; +} + +const char *lttng_event_expr_app_specific_context_field_get_provider_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_app_specific_context_field, + parent)->provider_name; + +end: + return ret; +} + +const char *lttng_event_expr_app_specific_context_field_get_type_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_app_specific_context_field, + parent)->type_name; + +end: + return ret; +} + +const struct lttng_event_expr * +lttng_event_expr_array_field_element_get_parent_expr( + const struct lttng_event_expr *expr) +{ + const struct lttng_event_expr *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_array_field_element, + parent)->array_field_expr; + +end: + return ret; +} + +enum lttng_event_expr_status lttng_event_expr_array_field_element_get_index( + const struct lttng_event_expr *expr, unsigned int *index) +{ + enum lttng_event_expr_status ret = LTTNG_EVENT_EXPR_STATUS_OK; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT || + !index) { + ret = LTTNG_EVENT_EXPR_STATUS_INVALID; + goto end; + } + + *index = container_of(expr, + const struct lttng_event_expr_array_field_element, + parent)->index; + +end: + return ret; +} + +bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a, + const struct lttng_event_expr *expr_b) +{ + bool is_equal = true; + + if (!expr_a && !expr_b) { + /* Both `NULL`: equal */ + goto end; + } + + if (!expr_a || !expr_b) { + /* Only one `NULL`: not equal */ + goto not_equal; + } + + if (expr_a->type != expr_b->type) { + /* Different types: not equal */ + goto not_equal; + } + + switch (expr_a->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_a = + container_of(expr_a, + const struct lttng_event_expr_field, + parent); + const struct lttng_event_expr_field *field_expr_b = + container_of(expr_b, + const struct lttng_event_expr_field, + parent); + + if (strcmp(field_expr_a->name, field_expr_b->name) != 0) { + goto not_equal; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const struct lttng_event_expr_app_specific_context_field *field_expr_a = + container_of(expr_a, + const struct lttng_event_expr_app_specific_context_field, + parent); + const struct lttng_event_expr_app_specific_context_field *field_expr_b = + container_of(expr_b, + const struct lttng_event_expr_app_specific_context_field, + parent); + + if (strcmp(field_expr_a->provider_name, + field_expr_b->provider_name) != 0) { + goto not_equal; + } + + if (strcmp(field_expr_a->type_name, + field_expr_b->type_name) != 0) { + goto not_equal; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + const struct lttng_event_expr_array_field_element *elem_expr_a = + container_of(expr_a, + const struct lttng_event_expr_array_field_element, + parent); + const struct lttng_event_expr_array_field_element *elem_expr_b = + container_of(expr_b, + const struct lttng_event_expr_array_field_element, + parent); + + if (!lttng_event_expr_is_equal(elem_expr_a->array_field_expr, + elem_expr_b->array_field_expr)) { + goto not_equal; + } + + if (elem_expr_a->index != elem_expr_b->index) { + goto not_equal; + } + + break; + } + default: + break; + } + + goto end; + +not_equal: + is_equal = false; + +end: + return is_equal; +} + +void lttng_event_expr_destroy(struct lttng_event_expr *expr) +{ + if (!expr) { + goto end; + } + + switch (expr->type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + struct lttng_event_expr_field *field_expr = + container_of(expr, + struct lttng_event_expr_field, parent); + + free(field_expr->name); + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + struct lttng_event_expr_app_specific_context_field *field_expr = + container_of(expr, + struct lttng_event_expr_app_specific_context_field, + parent); + + free(field_expr->provider_name); + free(field_expr->type_name); + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + struct lttng_event_expr_array_field_element *elem_expr = + container_of(expr, + struct lttng_event_expr_array_field_element, + parent); + + lttng_event_expr_destroy(elem_expr->array_field_expr); + break; + } + default: + break; + } + + free(expr); + +end: + return; +}