From b99a0cb3edd93f55e712096a352d64b79d4716bb Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 15 Apr 2021 13:09:35 -0400 Subject: [PATCH] lttng-ctl: Add error query interface MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: Ibb0ba800330976aad7b7c501f43a0d23a0dba027 Signed-off-by: Jérémie Galarneau --- include/Makefile.am | 2 + include/lttng/endpoint-internal.h | 1 + include/lttng/endpoint.h | 27 +- include/lttng/error-query-internal.h | 85 ++ include/lttng/error-query.h | 124 +++ include/lttng/lttng-error.h | 1 + include/lttng/lttng.h | 1 + src/common/Makefile.am | 1 + src/common/endpoint.c | 10 +- src/common/error-query.c | 971 +++++++++++++++++++++++ src/common/sessiond-comm/sessiond-comm.h | 6 + src/lib/lttng-ctl/lttng-ctl.c | 88 +- 12 files changed, 1310 insertions(+), 7 deletions(-) create mode 100644 include/lttng/error-query-internal.h create mode 100644 include/lttng/error-query.h create mode 100644 src/common/error-query.c diff --git a/include/Makefile.am b/include/Makefile.am index 94a421b0a..cb6209d92 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -104,6 +104,7 @@ lttnginclude_HEADERS = \ lttng/destruction-handle.h \ lttng/domain.h \ lttng/endpoint.h \ + lttng/error-query.h \ lttng/event-expr.h \ lttng/event-field-value.h \ lttng/event.h \ @@ -173,6 +174,7 @@ noinst_HEADERS = \ lttng/condition/session-rotation-internal.h \ lttng/domain-internal.h \ lttng/endpoint-internal.h \ + lttng/error-query-internal.h \ lttng/event-expr-internal.h \ lttng/event-field-value-internal.h \ lttng/event-internal.h \ diff --git a/include/lttng/endpoint-internal.h b/include/lttng/endpoint-internal.h index c5cfd8f24..3ec1df9c4 100644 --- a/include/lttng/endpoint-internal.h +++ b/include/lttng/endpoint-internal.h @@ -13,6 +13,7 @@ enum lttng_endpoint_type { LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_NOTIFICATION = 0, + LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_COMMAND = 1, }; struct lttng_endpoint { diff --git a/include/lttng/endpoint.h b/include/lttng/endpoint.h index fcd787018..1db5e5108 100644 --- a/include/lttng/endpoint.h +++ b/include/lttng/endpoint.h @@ -12,9 +12,34 @@ extern "C" { #endif -/* Default LTTng session daemon endpoint singleton. */ +/* + * Default LTTng session daemon notification endpoint singleton. + * + * For use during the creation of a notification channel. This endpoint + * implements the following policy to connect to a session daemon's + * notification delivery channel: + * - If the caller is root or part of the tracing group: + * - Attempt to connect to the "root" (global) session daemon, + * - Fallback to the session daemon running as the caller's user. + * - Otherwise (caller is an unpriviliged user): + * - Attempt to connect to the session daemon running as the caller's user. + */ extern struct lttng_endpoint *lttng_session_daemon_notification_endpoint; +/* + * Default LTTng session daemon command endpoint singleton. + * + * For use as part of the invocation of a command. This endpoint + * implements the following policy to connect to a session daemon's + * command channel: + * - If the caller is root or part of the tracing group: + * - Attempt to connect to the "root" (global) session daemon, + * - Fallback to the session daemon running as the caller's user. + * - Otherwise (caller is an unpriviliged user): + * - Attempt to connect to the session daemon running as the caller's user. + */ +extern struct lttng_endpoint *lttng_session_daemon_command_endpoint; + #ifdef __cplusplus } #endif diff --git a/include/lttng/error-query-internal.h b/include/lttng/error-query-internal.h new file mode 100644 index 000000000..24216f964 --- /dev/null +++ b/include/lttng/error-query-internal.h @@ -0,0 +1,85 @@ +/* + * error-query-internal.h + * + * Copyright (C) 2021 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.1-only + * + */ + +#ifndef LTTNG_ERROR_QUERY_INTERNAL_H +#define LTTNG_ERROR_QUERY_INTERNAL_H + +#include +#include +#include +#include + +enum lttng_error_query_target_type { + LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER, + LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION, +}; + +LTTNG_HIDDEN +enum lttng_error_query_target_type lttng_error_query_get_target_type( + const struct lttng_error_query *query); + +LTTNG_HIDDEN +const struct lttng_trigger *lttng_error_query_trigger_borrow_target( + const struct lttng_error_query *query); + +LTTNG_HIDDEN +const struct lttng_trigger *lttng_error_query_action_borrow_trigger_target( + const struct lttng_error_query *query); + +LTTNG_HIDDEN +const struct lttng_action *lttng_error_query_action_borrow_action_target( + const struct lttng_error_query *query, + const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +int lttng_error_query_serialize(const struct lttng_error_query *query, + struct lttng_payload *payload); + +LTTNG_HIDDEN +ssize_t lttng_error_query_create_from_payload(struct lttng_payload_view *view, + struct lttng_error_query **query); + +LTTNG_HIDDEN +int lttng_error_query_result_serialize( + const struct lttng_error_query_result *result, + struct lttng_payload *payload); + +LTTNG_HIDDEN +ssize_t lttng_error_query_result_create_from_payload( + struct lttng_payload_view *view, + struct lttng_error_query_result **result); + +LTTNG_HIDDEN +int lttng_error_query_results_serialize( + const struct lttng_error_query_results *results, + struct lttng_payload *payload); + +LTTNG_HIDDEN +ssize_t lttng_error_query_results_create_from_payload( + struct lttng_payload_view *view, + struct lttng_error_query_results **results); + +LTTNG_HIDDEN +struct lttng_error_query_result * +lttng_error_query_result_counter_create( + const char *name, const char *description, uint64_t value); + +LTTNG_HIDDEN +void lttng_error_query_result_destroy(struct lttng_error_query_result *result); + +LTTNG_HIDDEN +struct lttng_error_query_results *lttng_error_query_results_create(void); + +/* Ownership of `result` is transferred on success. */ +LTTNG_HIDDEN +int lttng_error_query_results_add_result( + struct lttng_error_query_results *results, + struct lttng_error_query_result *result); + +#endif /* LTTNG_ERROR_QUERY_INTERNAL_H */ diff --git a/include/lttng/error-query.h b/include/lttng/error-query.h new file mode 100644 index 000000000..382f2cad5 --- /dev/null +++ b/include/lttng/error-query.h @@ -0,0 +1,124 @@ +/* + * error-query.h + * + * Copyright (C) 2021 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.1-only + * + */ + +#ifndef LTTNG_ERROR_QUERY_H +#define LTTNG_ERROR_QUERY_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* An error query. */ +struct lttng_error_query; + +/* + * A collection of "lttng_error_query_result" returned after executing an error + * query against an endpoint. + */ +struct lttng_error_query_results; + +/* A 'result' is an opaque error type. */ +struct lttng_error_query_result; + +enum lttng_error_query_status { + LTTNG_ERROR_QUERY_STATUS_OK = 0, + /* An error occurred while querying for errors. */ + LTTNG_ERROR_QUERY_STATUS_ERROR = -1, + /* The target of the query does not make sense for this endpoint. */ + LTTNG_ERROR_QUERY_STATUS_INVALID_QUERY_TARGET = -2, + LTTNG_ERROR_QUERY_STATUS_INVALID_PARAMETER = -3, +}; + +enum lttng_error_query_result_type { + /* A count of errors provided as an unsigned integral value. */ + LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER = 0, + LTTNG_ERROR_QUERY_RESULT_TYPE_UNKNOWN = -1, +}; + +enum lttng_error_query_result_status { + LTTNG_ERROR_QUERY_RESULT_STATUS_OK = 0, + LTTNG_ERROR_QUERY_RESULT_STATUS_ERROR = -1, + LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER = -2, +}; + +enum lttng_error_query_results_status { + LTTNG_ERROR_QUERY_RESULTS_STATUS_OK = 0, + LTTNG_ERROR_QUERY_RESULTS_STATUS_ERROR = -1, + LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER = -2, +}; + +/* Create an error query targetting a trigger object. */ +extern struct lttng_error_query *lttng_error_query_trigger_create( + const struct lttng_trigger *trigger); + +/* Create an error query targetting an action object. */ +extern struct lttng_error_query *lttng_error_query_action_create( + const struct lttng_trigger *trigger, + const struct lttng_action *action); + +/* Destroy an error query. */ +extern void lttng_error_query_destroy(struct lttng_error_query *query); + +/* + * Run an error query against an endpoint. + * + * Currently, only the `lttng_session_daemon_command_endpoint` is supported, + * see `lttng/endpoint.h`. + */ +extern enum lttng_error_code lttng_error_query_execute( + const struct lttng_error_query *query, + const struct lttng_endpoint *endpoint, + struct lttng_error_query_results **results); + +/* Get the number of results in a result set. */ +extern enum lttng_error_query_results_status +lttng_error_query_results_get_count( + const struct lttng_error_query_results *results, + unsigned int *count); + +/* Get a result from a result set by index. */ +extern enum lttng_error_query_results_status +lttng_error_query_results_get_result( + const struct lttng_error_query_results *results, + const struct lttng_error_query_result **result, + unsigned int index); + +/* Destroy an error query result set. */ +extern void lttng_error_query_results_destroy( + struct lttng_error_query_results *results); + +/* Get the type of an error query result. */ +extern enum lttng_error_query_result_type lttng_error_query_result_get_type( + const struct lttng_error_query_result *result); + +/* Get the name of result. */ +extern enum lttng_error_query_result_status lttng_error_query_result_get_name( + const struct lttng_error_query_result *result, + const char **name); + +/* Get the description of a result. */ +extern enum lttng_error_query_result_status +lttng_error_query_result_get_description( + const struct lttng_error_query_result *result, + const char **description); + +/* Get the value of an error counter. */ +extern enum lttng_error_query_result_status +lttng_error_query_result_counter_get_value( + const struct lttng_error_query_result *result, uint64_t *value); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ERROR_QUERY_H */ diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index fa4876be6..d955e143a 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -179,6 +179,7 @@ enum lttng_error_code { 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_ERROR_QUERY_TARGET = 169, /* Invalid error query target. */ /* 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 345d2b707..081a21e87 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/common/Makefile.am b/src/common/Makefile.am index b90b8854c..a6cdfd0b5 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -58,6 +58,7 @@ libcommon_la_SOURCES = \ dynamic-buffer.c dynamic-buffer.h \ endpoint.c \ error.c error.h \ + error-query.c \ evaluation.c \ event.c \ event-expr-to-bytecode.c event-expr-to-bytecode.h \ diff --git a/src/common/endpoint.c b/src/common/endpoint.c index 4b4c50459..3a0bbadcb 100644 --- a/src/common/endpoint.c +++ b/src/common/endpoint.c @@ -9,8 +9,16 @@ static struct lttng_endpoint lttng_session_daemon_notification_endpoint_instance = { - .type = LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_NOTIFICATION + .type = LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_NOTIFICATION, +}; + +static +struct lttng_endpoint lttng_session_daemon_command_endpoint_instance = { + .type = LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_COMMAND, }; struct lttng_endpoint *lttng_session_daemon_notification_endpoint = <tng_session_daemon_notification_endpoint_instance; + +struct lttng_endpoint *lttng_session_daemon_command_endpoint = + <tng_session_daemon_command_endpoint_instance; diff --git a/src/common/error-query.c b/src/common/error-query.c new file mode 100644 index 000000000..17fd5ffae --- /dev/null +++ b/src/common/error-query.c @@ -0,0 +1,971 @@ +/* + * error-query.c + * + * Copyright (C) 2021 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct lttng_error_query { + enum lttng_error_query_target_type target_type; +}; + +struct lttng_error_query_comm { + /* enum lttng_error_query_target_type */ + int8_t target_type; + /* Target-specific payload. */ + char payload[]; +}; + +struct lttng_error_query_trigger { + struct lttng_error_query parent; + /* Mutable only because of the reference count. */ + struct lttng_trigger *trigger; +}; + +struct lttng_error_query_action_comm { + LTTNG_OPTIONAL_COMM(uint32_t) action_index; + /* Trigger payload. */ + char payload[]; +}; + +struct lttng_error_query_action { + struct lttng_error_query parent; + /* Mutable only because of the reference count. */ + struct lttng_trigger *trigger; + /* + * Index of the target action. Since action lists can't be nested, + * the targetted action is the top-level group if the action_index is + * unset. Otherwise, the index refers to the index within the top-level + * group. + */ + LTTNG_OPTIONAL(unsigned int) action_index; +}; + +struct lttng_error_query_result { + enum lttng_error_query_result_type type; + char *name; + char *description; +}; + +struct lttng_error_query_result_comm { + /* enum lttng_error_query_result_type */ + uint8_t type; + /* Length of name (including null-terminator). */ + uint32_t name_len; + /* Length of description (including null-terminator). */ + uint32_t description_len; + /* Name, description, and type-specific payload follow. */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_error_query_result_counter_comm { + uint64_t value; +} LTTNG_PACKED; + +struct lttng_error_query_result_counter { + struct lttng_error_query_result parent; + uint64_t value; +}; + +struct lttng_error_query_results_comm { + uint32_t count; + /* `count` instances of `struct lttng_error_query_result` follow. */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_error_query_results { + struct lttng_dynamic_pointer_array results; +}; + + +struct lttng_error_query *lttng_error_query_trigger_create( + const struct lttng_trigger *trigger) +{ + struct lttng_error_query_trigger *query = NULL; + struct lttng_trigger *trigger_copy; + + trigger_copy = lttng_trigger_copy(trigger); + if (!trigger_copy) { + goto end; + } + + if (!trigger) { + goto end; + } + + query = zmalloc(sizeof(*query)); + if (!query) { + PERROR("Failed to allocate trigger error query"); + goto end; + } + + query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER; + query->trigger = trigger_copy; +end: + return query ? &query->parent : NULL; +} + +extern struct lttng_error_query *lttng_error_query_action_create( + const struct lttng_trigger *trigger, + const struct lttng_action *action) +{ + struct lttng_error_query_action *query = NULL; + typeof(query->action_index) action_index; + struct lttng_trigger *trigger_copy; + + if (!trigger || !action) { + goto end; + } + + trigger_copy = lttng_trigger_copy(trigger); + if (!trigger_copy) { + goto end; + } + + /* + * If an action is not the top-level action of the trigger, our only + * hope of finding its position is if the top-level action is an + * action list. + * + * Note that action comparisons are performed by pointer since multiple + * otherwise identical actions can be found in an action group (two + * notify actions, for example). + */ + if (action != trigger->action && + lttng_action_get_type(trigger->action) == + LTTNG_ACTION_TYPE_GROUP) { + unsigned int i, action_group_count; + enum lttng_action_status action_status; + + action_status = lttng_action_group_get_count( + trigger->action, &action_group_count); + if (action_status != LTTNG_ACTION_STATUS_OK) { + goto end; + } + + for (i = 0; i < action_group_count; i++) { + const struct lttng_action *candidate_action = + lttng_action_group_get_at_index( + trigger->action, i); + + assert(candidate_action); + if (candidate_action == action) { + LTTNG_OPTIONAL_SET(&action_index, i); + break; + } + } + + if (!action_index.is_set) { + /* Not found; invalid action. */ + goto end; + } + } else { + /* + * Trigger action is not a group and not equal to the target + * action; invalid action provided. + */ + goto end; + } + + query = zmalloc(sizeof(*query)); + if (!query) { + PERROR("Failed to allocate action error query"); + goto end; + } + + query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION; + query->trigger = trigger_copy; + query->action_index = action_index; +end: + return query ? &query->parent : NULL; +} + +void lttng_error_query_destroy(struct lttng_error_query *query) +{ + struct lttng_error_query_trigger *trigger_query; + + if (!query) { + return; + } + + trigger_query = container_of(query, typeof(*trigger_query), parent); + lttng_trigger_put(trigger_query->trigger); + free(trigger_query); +} + +static +int lttng_error_query_result_counter_serialize( + const struct lttng_error_query_result *result, + struct lttng_payload *payload) +{ + const struct lttng_error_query_result_counter *counter_result; + + assert(result->type == LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER); + counter_result = container_of(result, typeof(*counter_result), parent); + + return lttng_dynamic_buffer_append(&payload->buffer, + &(struct lttng_error_query_result_counter_comm) { + .value = counter_result->value + }, + sizeof(struct lttng_error_query_result_counter_comm)); +} + +LTTNG_HIDDEN +int lttng_error_query_result_serialize( + const struct lttng_error_query_result *result, + struct lttng_payload *payload) +{ + int ret; + struct lttng_error_query_result_comm header = { + .type = (uint8_t) result->type, + .name_len = (typeof(header.name_len)) strlen(result->name) + 1, + .description_len = (typeof(header.name_len)) strlen(result->description) + 1, + }; + + /* Header. */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, &header, sizeof(header)); + if (ret) { + ERR("Failed to append error query result communication header to payload"); + goto end; + } + + /* Name. */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, result->name, header.name_len); + if (ret) { + ERR("Failed to append error query result name to payload"); + goto end; + } + + /* Description. */ + ret = lttng_dynamic_buffer_append(&payload->buffer, result->description, + header.description_len); + if (ret) { + ERR("Failed to append error query result description to payload"); + goto end; + } + + /* Type-specific payload. */ + switch (result->type) { + case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: + ret = lttng_error_query_result_counter_serialize( + result, payload); + if (ret) { + ERR("Failed to serialize counter error query result"); + goto end; + } + break; + default: + abort(); + } + +end: + return ret; +} + +static +int lttng_error_query_result_init( + struct lttng_error_query_result *result, + enum lttng_error_query_result_type result_type, + const char *name, + const char *description) +{ + int ret; + + assert(name); + assert(description); + + result->type = result_type; + + result->name = strdup(name); + if (!result->name) { + PERROR("Failed to copy error query result name"); + ret = -1; + goto end; + } + + result->description = strdup(description); + if (!result->description) { + PERROR("Failed to copy error query result description"); + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +LTTNG_HIDDEN +void lttng_error_query_result_destroy(struct lttng_error_query_result *counter) +{ + if (!counter) { + return; + } + + switch (counter->type) { + case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: + /* Nothing to tear down. */ + break; + default: + abort(); + } + + free(counter->name); + free(counter->description); + free(counter); +} + +LTTNG_HIDDEN +struct lttng_error_query_result * +lttng_error_query_result_counter_create( + const char *name, const char *description, uint64_t value) +{ + int init_ret; + struct lttng_error_query_result_counter *counter; + + counter = zmalloc(sizeof(*counter)); + if (!counter) { + PERROR("Failed to allocate error query counter result"); + goto end; + } + + init_ret = lttng_error_query_result_init(&counter->parent, + LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER, name, + description); + if (init_ret) { + goto error; + } + + goto end; +error: + lttng_error_query_result_destroy(&counter->parent); +end: + return counter ? &counter->parent : NULL; +} + +static +void destroy_result(void *ptr) +{ + struct lttng_error_query_result *result = (typeof(result)) ptr; + + lttng_error_query_result_destroy(result); +} + +LTTNG_HIDDEN +struct lttng_error_query_results *lttng_error_query_results_create(void) +{ + struct lttng_error_query_results *set = zmalloc(sizeof(*set)); + + if (!set) { + PERROR("Failed to allocate an error query result set"); + goto end; + } + + lttng_dynamic_pointer_array_init(&set->results, destroy_result); +end: + return set; +} + +LTTNG_HIDDEN +int lttng_error_query_results_add_result( + struct lttng_error_query_results *results, + struct lttng_error_query_result *result) +{ + return lttng_dynamic_pointer_array_add_pointer( + &results->results, result); +} + +LTTNG_HIDDEN +ssize_t lttng_error_query_result_create_from_payload( + struct lttng_payload_view *view, + struct lttng_error_query_result **result) +{ + ssize_t used_size = 0; + struct lttng_error_query_result_comm *header; + struct lttng_payload_view header_view = + lttng_payload_view_from_view(view, 0, sizeof(*header)); + const char *name; + const char *description; + + if (!lttng_payload_view_is_valid(&header_view)) { + used_size = -1; + goto end; + } + + header = (typeof(header)) header_view.buffer.data; + used_size += sizeof(*header); + + { + struct lttng_payload_view name_view = + lttng_payload_view_from_view(view, used_size, + header->name_len); + + if (!lttng_payload_view_is_valid(&name_view) || + !lttng_buffer_view_contains_string( + &name_view.buffer, + name_view.buffer.data, + header->name_len)) { + used_size = -1; + goto end; + } + + name = name_view.buffer.data; + used_size += header->name_len; + } + + { + struct lttng_payload_view description_view = + lttng_payload_view_from_view(view, used_size, + header->description_len); + + if (!lttng_payload_view_is_valid(&description_view) || + !lttng_buffer_view_contains_string( + &description_view.buffer, + description_view.buffer.data, + header->description_len)) { + used_size = -1; + goto end; + } + + description = description_view.buffer.data; + used_size += header->description_len; + } + + switch (header->type) { + case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: + { + struct lttng_error_query_result_counter_comm *counter; + struct lttng_payload_view counter_payload_view = + lttng_payload_view_from_view(view, used_size, + sizeof(*counter)); + + if (!lttng_payload_view_is_valid(&counter_payload_view)) { + used_size = -1; + goto end; + } + + counter = (typeof(counter)) counter_payload_view.buffer.data; + *result = lttng_error_query_result_counter_create( + name, description, counter->value); + if (!*result) { + used_size = -1; + goto end; + } + + used_size += sizeof(*counter); + break; + } + default: + used_size = -1; + goto end; + } + +end: + return used_size; +} + +LTTNG_HIDDEN +int lttng_error_query_results_serialize( + const struct lttng_error_query_results *results, + struct lttng_payload *payload) +{ + int ret; + size_t result_index; + const size_t result_count = lttng_dynamic_pointer_array_get_count( + &results->results); + const struct lttng_error_query_results_comm header = { + .count = (typeof(header.count)) result_count, + }; + + /* Header. */ + ret = lttng_dynamic_buffer_append(&payload->buffer, &header, sizeof(header)); + if (ret) { + ERR("Failed to append error query result set header to payload"); + goto end; + } + + /* Results. */ + for (result_index = 0; result_index < result_count; result_index++) { + const struct lttng_error_query_result *result = (typeof(result)) + lttng_dynamic_pointer_array_get_pointer( + &results->results, + result_index); + + ret = lttng_error_query_result_serialize(result, payload); + if (ret) { + ERR("Failed to append error query result to payload"); + goto end; + } + } +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_error_query_results_create_from_payload( + struct lttng_payload_view *view, + struct lttng_error_query_results **_results) +{ + size_t result_index; + ssize_t total_used_size = 0; + struct lttng_error_query_results_comm *header; + struct lttng_payload_view header_view = + lttng_payload_view_from_view(view, 0, sizeof(*header)); + struct lttng_error_query_results *results = NULL; + + if (!lttng_payload_view_is_valid(&header_view)) { + ERR("Failed to map view to error query result set header"); + total_used_size = -1; + goto end; + } + + header = (typeof(header)) header_view.buffer.data; + total_used_size += sizeof(*header); + results = lttng_error_query_results_create(); + if (!results) { + total_used_size = -1; + goto end; + } + + for (result_index = 0; result_index < header->count; result_index++) { + ssize_t used_size; + struct lttng_error_query_result *result; + struct lttng_payload_view result_view = + lttng_payload_view_from_view( + view, total_used_size, -1); + + if (!lttng_payload_view_is_valid(&result_view)) { + total_used_size = -1; + goto end; + } + + used_size = lttng_error_query_result_create_from_payload( + &result_view, &result); + if (used_size < 0) { + total_used_size = -1; + goto end; + } + + total_used_size += used_size; + + if (lttng_dynamic_pointer_array_add_pointer( + &results->results, result)) { + lttng_error_query_result_destroy(result); + total_used_size = -1; + goto end; + } + } + + *_results = results; + results = NULL; +end: + lttng_error_query_results_destroy(results); + return total_used_size; +} + +static +int lttng_error_query_trigger_serialize(const struct lttng_error_query *query, + struct lttng_payload *payload) +{ + int ret; + const struct lttng_error_query_trigger *query_trigger = + container_of(query, typeof(*query_trigger), parent); + + if (!lttng_trigger_validate(query_trigger->trigger)) { + ret = -1; + goto end; + } + + ret = lttng_trigger_serialize(query_trigger->trigger, payload); + if (ret) { + goto end; + } + +end: + return ret; +} + +static +int lttng_error_query_action_serialize(const struct lttng_error_query *query, + struct lttng_payload *payload) +{ + int ret; + const struct lttng_error_query_action *query_action = + container_of(query, typeof(*query_action), parent); + struct lttng_error_query_action_comm header = { + .action_index.is_set = query_action->action_index.is_set, + .action_index.value = query_action->action_index.value, + }; + + if (!lttng_trigger_validate(query_action->trigger)) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &header, sizeof(header)); + if (ret) { + goto end; + } + + ret = lttng_trigger_serialize(query_action->trigger, payload); + if (ret) { + goto end; + } +end: + return ret; +} + +LTTNG_HIDDEN +enum lttng_error_query_target_type lttng_error_query_get_target_type( + const struct lttng_error_query *query) +{ + return query->target_type; +} + +LTTNG_HIDDEN +const struct lttng_trigger *lttng_error_query_trigger_borrow_target( + const struct lttng_error_query *query) +{ + const struct lttng_error_query_trigger *query_trigger = + container_of(query, typeof(*query_trigger), parent); + + return query_trigger->trigger; +} + +LTTNG_HIDDEN +const struct lttng_trigger *lttng_error_query_action_borrow_trigger_target( + const struct lttng_error_query *query) +{ + const struct lttng_error_query_action *query_action = + container_of(query, typeof(*query_action), parent); + + return query_action->trigger; +} + +LTTNG_HIDDEN +const struct lttng_action *lttng_error_query_action_borrow_action_target( + const struct lttng_error_query *query, + const struct lttng_trigger *trigger) +{ + const struct lttng_action *target_action = NULL; + const struct lttng_error_query_action *query_action = + container_of(query, typeof(*query_action), parent); + const struct lttng_action *trigger_action = + lttng_trigger_get_const_action(trigger); + + if (!query_action->action_index.is_set) { + target_action = trigger_action; + } else { + if (lttng_action_get_type(trigger_action) != + LTTNG_ACTION_TYPE_GROUP) { + ERR("Invalid action error query target index: trigger action is not a group"); + goto end; + } + + target_action = lttng_action_group_get_at_index(trigger_action, + LTTNG_OPTIONAL_GET(query_action->action_index)); + } + +end: + return target_action; +} + +LTTNG_HIDDEN +int lttng_error_query_serialize(const struct lttng_error_query *query, + struct lttng_payload *payload) +{ + int ret; + struct lttng_error_query_comm header = { + .target_type = (typeof(header.target_type)) query->target_type, + }; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &header, sizeof(header)); + if (ret) { + ERR("Failed to append error query header to payload"); + goto end; + } + + switch (query->target_type) { + case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER: + ret = lttng_error_query_trigger_serialize(query, payload); + if (ret) { + goto end; + } + + break; + case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION: + ret = lttng_error_query_action_serialize(query, payload); + if (ret) { + goto end; + } + + break; + default: + abort(); + } +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_error_query_create_from_payload(struct lttng_payload_view *view, + struct lttng_error_query **query) +{ + ssize_t used_size = 0; + struct lttng_error_query_comm *header; + struct lttng_payload_view header_view = + lttng_payload_view_from_view(view, 0, sizeof(*header)); + + if (!lttng_payload_view_is_valid(&header_view)) { + ERR("Failed to map error query header"); + used_size = -1; + goto end; + } + + used_size = sizeof(*header); + + header = (typeof(header)) header_view.buffer.data; + switch ((enum lttng_error_query_target_type) header->target_type) { + case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER: + { + struct lttng_trigger *trigger; + ssize_t trigger_used_size; + struct lttng_payload_view trigger_view = + lttng_payload_view_from_view( + view, used_size, -1); + + if (!lttng_payload_view_is_valid(&trigger_view)) { + used_size = -1; + goto end; + } + + trigger_used_size = lttng_trigger_create_from_payload( + &trigger_view, &trigger); + if (trigger_used_size < 0) { + used_size = -1; + goto end; + } + + used_size += trigger_used_size; + + *query = lttng_error_query_trigger_create(trigger); + lttng_trigger_put(trigger); + if (!*query) { + used_size = -1; + goto end; + } + + break; + } + case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION: + { + struct lttng_trigger *trigger; + const struct lttng_action *target_action; + ssize_t trigger_used_size; + struct lttng_error_query_action_comm *action_header; + + { + struct lttng_payload_view action_header_view = + lttng_payload_view_from_view(view, + used_size, + sizeof(*action_header)); + + if (!lttng_payload_view_is_valid(&action_header_view)) { + used_size = -1; + goto end; + } + + action_header = (typeof(action_header)) action_header_view.buffer.data; + used_size += sizeof(*action_header); + } + + { + struct lttng_payload_view trigger_view = + lttng_payload_view_from_view( + view, used_size, -1); + + if (!lttng_payload_view_is_valid(&trigger_view)) { + used_size = -1; + goto end; + } + + trigger_used_size = lttng_trigger_create_from_payload( + &trigger_view, &trigger); + if (trigger_used_size < 0) { + used_size = -1; + goto end; + } + + used_size += trigger_used_size; + } + + if (!action_header->action_index.is_set) { + target_action = trigger->action; + } else { + if (lttng_action_get_type(trigger->action) != + LTTNG_ACTION_TYPE_GROUP) { + used_size = -1; + goto end; + } + + target_action = lttng_action_group_get_at_index( + trigger->action, + action_header->action_index.value); + } + + *query = lttng_error_query_action_create( + trigger, target_action); + lttng_trigger_put(trigger); + if (!*query) { + used_size = -1; + goto end; + } + + break; + } + default: + used_size = -1; + goto end; + } + +end: + return used_size; +} + +enum lttng_error_query_results_status lttng_error_query_results_get_count( + const struct lttng_error_query_results *results, + unsigned int *count) +{ + enum lttng_error_query_results_status status; + + if (!results || !count) { + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&results->results); + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK; +end: + return status; +} + +enum lttng_error_query_results_status +lttng_error_query_results_get_result( + const struct lttng_error_query_results *results, + const struct lttng_error_query_result **result, + unsigned int index) +{ + unsigned int result_count; + enum lttng_error_query_results_status status; + + if (!results || !result) { + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER; + goto end; + } + + status = lttng_error_query_results_get_count(results, &result_count); + if (status != LTTNG_ERROR_QUERY_RESULTS_STATUS_OK) { + goto end; + } + + if (index >= result_count) { + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER; + goto end; + } + + *result = (typeof(*result)) lttng_dynamic_pointer_array_get_pointer( + &results->results, index); + assert(*result); + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK; +end: + return status; +} + +void lttng_error_query_results_destroy( + struct lttng_error_query_results *results) +{ + if (!results) { + return; + } + + lttng_dynamic_pointer_array_reset(&results->results); + free(results); +} + +enum lttng_error_query_result_type +lttng_error_query_result_get_type(const struct lttng_error_query_result *result) +{ + return result ? result->type : LTTNG_ERROR_QUERY_RESULT_TYPE_UNKNOWN; +} + +enum lttng_error_query_result_status lttng_error_query_result_get_name( + const struct lttng_error_query_result *result, + const char **name) +{ + enum lttng_error_query_result_status status; + + if (!result || !name) { + status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER; + goto end; + } + + *name = result->name; + status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK; +end: + return status; +} + +enum lttng_error_query_result_status lttng_error_query_result_get_description( + const struct lttng_error_query_result *result, + const char **description) +{ + enum lttng_error_query_result_status status; + + if (!result || !description) { + status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER; + goto end; + } + + *description = result->description; + status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK; +end: + return status; +} + +enum lttng_error_query_result_status lttng_error_query_result_counter_get_value( + const struct lttng_error_query_result *result, + uint64_t *value) +{ + enum lttng_error_query_result_status status; + const struct lttng_error_query_result_counter *counter_result; + + if (!result || !value || + result->type != LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER) { + status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER; + goto end; + } + + counter_result = container_of(result, typeof(*counter_result), parent); + + *value = counter_result->value; + status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK; +end: + return status; +} diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index 8891557c1..d24d2f580 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -107,6 +107,7 @@ enum lttcomm_sessiond_command { LTTNG_CREATE_SESSION_EXT = 49, LTTNG_CLEAR_SESSION = 50, LTTNG_LIST_TRIGGERS = 51, + LTTNG_EXECUTE_ERROR_QUERY = 52, }; static inline @@ -197,6 +198,8 @@ 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_EXECUTE_ERROR_QUERY: + return "LTTNG_EXECUTE_ERROR_QUERY"; default: abort(); } @@ -484,6 +487,9 @@ struct lttcomm_session_msg { struct { uint32_t length; } LTTNG_PACKED trigger; + struct { + uint32_t length; + } LTTNG_PACKED error_query; struct { uint64_t rotation_id; } LTTNG_PACKED get_rotation_info; diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 52b5263d8..7debaed6b 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -23,10 +23,10 @@ #include #include #include -#include #include -#include +#include #include +#include #include #include #include @@ -35,19 +35,20 @@ #include #include #include +#include #include #include +#include #include #include #include #include #include -#include +#include "lttng-ctl-helper.h" #include #include #include -#include "lttng-ctl-helper.h" #define COPY_DOMAIN_PACKED(dst, src) \ do { \ @@ -3097,7 +3098,6 @@ int lttng_register_trigger(struct lttng_trigger *trigger) .gid = LTTNG_OPTIONAL_INIT_UNSET, }; - lttng_payload_init(&message); lttng_payload_init(&reply); @@ -3201,6 +3201,84 @@ end: return ret; } +enum lttng_error_code lttng_error_query_execute( + const struct lttng_error_query *query, + const struct lttng_endpoint *endpoint, + struct lttng_error_query_results **results) +{ + int ret; + enum lttng_error_code ret_code; + struct lttcomm_session_msg lsm = { + .cmd_type = LTTNG_EXECUTE_ERROR_QUERY, + }; + struct lttng_payload message; + struct lttng_payload reply; + struct lttcomm_session_msg *message_lsm; + + lttng_payload_init(&message); + lttng_payload_init(&reply); + + if (!query || !results) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + if (endpoint != lttng_session_daemon_command_endpoint) { + ret_code = LTTNG_ERR_INVALID_ERROR_QUERY_TARGET; + goto end; + } + + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_error_query_serialize(query, &message); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + + message_lsm = (struct lttcomm_session_msg *) message.buffer.data; + message_lsm->u.error_query.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; + } + } + + { + ssize_t reply_create_ret; + struct lttng_payload_view reply_view = + lttng_payload_view_from_payload( + &reply, 0, reply.buffer.size); + + reply_create_ret = lttng_error_query_results_create_from_payload( + &reply_view, results); + if (reply_create_ret < 0) { + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto end; + } + } + + ret_code = LTTNG_OK; +end: + lttng_payload_reset(&message); + lttng_payload_reset(&reply); + return ret_code; +} + int lttng_unregister_trigger(const struct lttng_trigger *trigger) { int ret; -- 2.34.1