From: Simon Marchi Date: Mon, 6 Sep 2021 13:45:35 +0000 (-0400) Subject: tests: compile some tools/tests as C++ X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=729c1fec5976af17fded2f026725a08ef1924290;p=lttng-tools.git tests: compile some tools/tests as C++ These tests use things from the common libs, or at least include header files from src/common. These files are going to contain C++-specific things in a following commit, so it's easier if we compile them tools/tests as C++. Change-Id: Ib99f2373beb414c50eaa10b35e0d895bc37e4e64 Signed-off-by: Simon Marchi Signed-off-by: Jérémie Galarneau --- diff --git a/doc/examples/trigger-condition-event-matches/Makefile.am b/doc/examples/trigger-condition-event-matches/Makefile.am index bace545b9..cb5512ed0 100644 --- a/doc/examples/trigger-condition-event-matches/Makefile.am +++ b/doc/examples/trigger-condition-event-matches/Makefile.am @@ -15,7 +15,7 @@ libtracepoint_trigger_example_a_SOURCES = tracepoint-trigger-example.c tracepoin instrumented_app_SOURCES = instrumented-app.c instrumented_app_LDADD = libtracepoint-trigger-example.a $(UST_LIBS) $(DL_LIBS) -notification_client_SOURCES = notification-client.c +notification_client_SOURCES = notification-client.cpp notification_client_LDADD = $(LIBLTTNG_CTL) endif diff --git a/doc/examples/trigger-condition-event-matches/notification-client.c b/doc/examples/trigger-condition-event-matches/notification-client.c deleted file mode 100644 index 4a2515569..000000000 --- a/doc/examples/trigger-condition-event-matches/notification-client.c +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: MIT - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -static int print_capture(const struct lttng_condition *condition, - const struct lttng_event_field_value *capture, - unsigned int indent_level); -static int print_array(const struct lttng_condition *condition, - const struct lttng_event_field_value *array, - unsigned int indent_level); - -static void indent(unsigned int indentation_level) -{ - unsigned int i; - for (i = 0; i < indentation_level; i++) { - printf(" "); - } -} - -static void print_one_event_expr(const struct lttng_event_expr *event_expr) -{ - enum lttng_event_expr_type type; - - type = lttng_event_expr_get_type(event_expr); - - switch (type) { - case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: - { - const char *name; - - name = lttng_event_expr_event_payload_field_get_name( - event_expr); - printf("%s", name); - - break; - } - - case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: - { - const char *name; - - name = lttng_event_expr_channel_context_field_get_name( - event_expr); - printf("$ctx.%s", name); - - break; - } - - case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: - { - const char *provider_name; - const char *type_name; - - provider_name = lttng_event_expr_app_specific_context_field_get_provider_name( - event_expr); - type_name = lttng_event_expr_app_specific_context_field_get_type_name( - event_expr); - - printf("$app.%s:%s", provider_name, type_name); - - break; - } - - case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: - { - unsigned int index; - const struct lttng_event_expr *parent_expr; - enum lttng_event_expr_status status; - - parent_expr = lttng_event_expr_array_field_element_get_parent_expr( - event_expr); - LTTNG_ASSERT(parent_expr != NULL); - - print_one_event_expr(parent_expr); - - status = lttng_event_expr_array_field_element_get_index( - event_expr, &index); - LTTNG_ASSERT(status == LTTNG_EVENT_EXPR_STATUS_OK); - - printf("[%u]", index); - - break; - } - - default: - abort(); - } -} - -static bool action_group_contains_notify( - const struct lttng_action *action_group) -{ - unsigned int i, count; - enum lttng_action_status status = - lttng_action_list_get_count(action_group, &count); - - if (status != LTTNG_ACTION_STATUS_OK) { - printf("Failed to get action count from action group\n"); - exit(1); - } - - for (i = 0; i < count; i++) { - const struct lttng_action *action = - lttng_action_list_get_at_index(action_group, i); - const enum lttng_action_type action_type = - lttng_action_get_type(action); - - if (action_type == LTTNG_ACTION_TYPE_NOTIFY) { - return true; - } - } - return false; -} - -static int print_capture(const struct lttng_condition *condition, - const struct lttng_event_field_value *capture, - unsigned int indent_level) -{ - int ret = 0; - enum lttng_event_field_value_status event_field_status; - uint64_t u_val; - int64_t s_val; - double d_val; - const char *string_val = NULL; - - switch (lttng_event_field_value_get_type(capture)) { - case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: - { - event_field_status = - lttng_event_field_value_unsigned_int_get_value( - capture, &u_val); - if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - ret = 1; - goto end; - } - - printf("[Unsigned int] %" PRIu64, u_val); - break; - } - case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: - { - event_field_status = - lttng_event_field_value_signed_int_get_value( - capture, &s_val); - if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - ret = 1; - goto end; - } - - printf("[Signed int] %" PRId64, s_val); - break; - } - case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: - { - event_field_status = - lttng_event_field_value_unsigned_int_get_value( - capture, &u_val); - if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - ret = 1; - goto end; - } - - printf("[Unsigned enum] %" PRIu64, u_val); - break; - } - case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: - { - event_field_status = - lttng_event_field_value_signed_int_get_value( - capture, &s_val); - if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - ret = 1; - goto end; - } - - printf("[Signed enum] %" PRId64, s_val); - break; - } - case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL: - { - event_field_status = lttng_event_field_value_real_get_value( - capture, &d_val); - if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - ret = 1; - goto end; - } - - printf("[Real] %lf", d_val); - break; - } - case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: - { - event_field_status = lttng_event_field_value_string_get_value( - capture, &string_val); - if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - ret = 1; - goto end; - } - - printf("[String] %s", string_val); - break; - } - case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: - printf("[Array] [\n"); - print_array(condition, capture, indent_level); - indent(indent_level); - printf("]\n"); - break; - case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN: - case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID: - default: - ret = 1; - break; - } - -end: - return ret; -} - -static void print_unavailabe(void) -{ - printf("Capture unavailable"); -} - -static int print_array(const struct lttng_condition *condition, - const struct lttng_event_field_value *array, - unsigned int indent_level) -{ - int ret = 0; - enum lttng_event_field_value_status event_field_status; - unsigned int captured_field_count; - - event_field_status = lttng_event_field_value_array_get_length( - array, &captured_field_count); - if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - ret = 1; - goto end; - } - - for (unsigned int i = 0; i < captured_field_count; i++) { - const struct lttng_event_field_value *captured_field = NULL; - const struct lttng_event_expr *expr = - lttng_condition_event_rule_matches_get_capture_descriptor_at_index( - condition, i); - LTTNG_ASSERT(expr); - - indent(indent_level + 1); - - printf("Field: "); - print_one_event_expr(expr); - printf(" Value: "); - - event_field_status = - lttng_event_field_value_array_get_element_at_index( - array, i, &captured_field); - if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - if (event_field_status == - LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) { - print_unavailabe(); - } else { - ret = 1; - goto end; - } - } else { - print_capture(condition, captured_field, - indent_level + 1); - } - - if (i + 1 < captured_field_count) { - printf(","); - } else { - printf("."); - } - printf("\n"); - } - -end: - return ret; -} - -static int print_captures(struct lttng_notification *notification) -{ - int ret = 0; - const struct lttng_evaluation *evaluation = - lttng_notification_get_evaluation(notification); - const struct lttng_condition *condition = - lttng_notification_get_condition(notification); - - /* Status */ - enum lttng_condition_status condition_status; - enum lttng_evaluation_event_rule_matches_status evaluation_status; - - const struct lttng_event_field_value *captured_field_array = NULL; - unsigned int expected_capture_field_count; - - LTTNG_ASSERT(lttng_evaluation_get_type(evaluation) == - LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES); - - condition_status = - lttng_condition_event_rule_matches_get_capture_descriptor_count( - condition, - &expected_capture_field_count); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - ret = 1; - goto end; - } - - if (expected_capture_field_count == 0) { - ret = 0; - goto end; - } - - evaluation_status = - lttng_evaluation_event_rule_matches_get_captured_values( - evaluation, &captured_field_array); - if (evaluation_status != LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_OK) { - ret = 1; - goto end; - } - - printf("Captured field values:\n"); - print_array(condition, captured_field_array, 1); -end: - return ret; -} - -static int print_notification(struct lttng_notification *notification) -{ - int ret = 0; - const struct lttng_evaluation *evaluation = - lttng_notification_get_evaluation(notification); - const enum lttng_condition_type type = - lttng_evaluation_get_type(evaluation); - - switch (type) { - case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: - printf("Received consumed size notification\n"); - break; - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - printf("Received buffer usage notification\n"); - break; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - printf("Received session rotation ongoing notification\n"); - break; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - printf("Received session rotation completed notification\n"); - break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: - { - const char *trigger_name; - enum lttng_trigger_status trigger_status; - char time_str[64]; - struct timeval tv; - time_t the_time; - const struct lttng_trigger *trigger = NULL; - - gettimeofday(&tv, NULL); - the_time = tv.tv_sec; - - strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T", - localtime(&the_time)); - printf("%s.%ld - ", time_str, tv.tv_usec); - - trigger = lttng_notification_get_trigger(notification); - if (!trigger) { - fprintf(stderr, "Failed to retrieve notification's trigger"); - goto end; - } - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - fprintf(stderr, "Failed to retrieve trigger's name"); - goto end; - } - - printf("Received notification of event rule matches trigger \"%s\"\n", - trigger_name); - ret = print_captures(notification); - break; - } - default: - fprintf(stderr, "Unknown notification type (%d)\n", type); - } - -end: - return ret; -} - -int main(int argc, char **argv) -{ - int ret; - struct lttng_triggers *triggers = NULL; - unsigned int count, i, j, subcription_count = 0, trigger_count; - enum lttng_trigger_status trigger_status; - struct lttng_notification_channel *notification_channel = NULL; - - if (argc < 2) { - fprintf(stderr, "Missing trigger name(s)\n"); - fprintf(stderr, "Usage: notification-client TRIGGER_NAME ..."); - ret = -1; - goto end; - } - - trigger_count = argc - 1; - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - if (!notification_channel) { - fprintf(stderr, "Failed to create notification channel\n"); - ret = -1; - goto end; - } - - ret = lttng_list_triggers(&triggers); - if (ret != LTTNG_OK) { - fprintf(stderr, "Failed to list triggers\n"); - goto end; - } - - trigger_status = lttng_triggers_get_count(triggers, &count); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - fprintf(stderr, "Failed to get trigger count\n"); - ret = -1; - goto end; - } - - for (i = 0; i < count; i++) { - const struct lttng_trigger *trigger = - lttng_triggers_get_at_index(triggers, i); - const struct lttng_condition *condition = - lttng_trigger_get_const_condition(trigger); - const struct lttng_action *action = - lttng_trigger_get_const_action(trigger); - const enum lttng_action_type action_type = - lttng_action_get_type(action); - enum lttng_notification_channel_status channel_status; - const char *trigger_name = NULL; - bool subscribe = false; - - lttng_trigger_get_name(trigger, &trigger_name); - for (j = 0; j < trigger_count; j++) { - if (!strcmp(trigger_name, argv[j + 1])) { - subscribe = true; - break; - } - } - - if (!subscribe) { - continue; - } - - if (!((action_type == LTTNG_ACTION_TYPE_LIST && - action_group_contains_notify(action)) || - action_type == LTTNG_ACTION_TYPE_NOTIFY)) { - printf("The action of trigger \"%s\" is not \"notify\", skipping.\n", - trigger_name); - continue; - } - - channel_status = lttng_notification_channel_subscribe( - notification_channel, condition); - if (channel_status == - LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED) { - continue; - } - if (channel_status) { - fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n", - trigger_name); - ret = -1; - goto end; - } - - printf("Subscribed to notifications of trigger \"%s\"\n", - trigger_name); - subcription_count++; - } - - if (subcription_count == 0) { - printf("No matching trigger with a notify action found.\n"); - ret = 0; - goto end; - } - - for (;;) { - struct lttng_notification *notification; - enum lttng_notification_channel_status channel_status; - - channel_status = - lttng_notification_channel_get_next_notification( - notification_channel, - ¬ification); - switch (channel_status) { - case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: - printf("Dropped notification\n"); - break; - case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED: - ret = 0; - goto end; - case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: - break; - case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: - printf("Notification channel was closed by peer.\n"); - break; - default: - fprintf(stderr, "A communication error occurred on the notification channel.\n"); - ret = -1; - goto end; - } - - ret = print_notification(notification); - lttng_notification_destroy(notification); - if (ret) { - goto end; - } - } -end: - lttng_triggers_destroy(triggers); - lttng_notification_channel_destroy(notification_channel); - return !!ret; -} diff --git a/doc/examples/trigger-condition-event-matches/notification-client.cpp b/doc/examples/trigger-condition-event-matches/notification-client.cpp new file mode 100644 index 000000000..4a2515569 --- /dev/null +++ b/doc/examples/trigger-condition-event-matches/notification-client.cpp @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int print_capture(const struct lttng_condition *condition, + const struct lttng_event_field_value *capture, + unsigned int indent_level); +static int print_array(const struct lttng_condition *condition, + const struct lttng_event_field_value *array, + unsigned int indent_level); + +static void indent(unsigned int indentation_level) +{ + unsigned int i; + for (i = 0; i < indentation_level; i++) { + printf(" "); + } +} + +static void print_one_event_expr(const struct lttng_event_expr *event_expr) +{ + enum lttng_event_expr_type type; + + type = lttng_event_expr_get_type(event_expr); + + switch (type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + { + const char *name; + + name = lttng_event_expr_event_payload_field_get_name( + event_expr); + printf("%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + const char *name; + + name = lttng_event_expr_channel_context_field_get_name( + event_expr); + printf("$ctx.%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const char *provider_name; + const char *type_name; + + provider_name = lttng_event_expr_app_specific_context_field_get_provider_name( + event_expr); + type_name = lttng_event_expr_app_specific_context_field_get_type_name( + event_expr); + + printf("$app.%s:%s", provider_name, type_name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + unsigned int index; + const struct lttng_event_expr *parent_expr; + enum lttng_event_expr_status status; + + parent_expr = lttng_event_expr_array_field_element_get_parent_expr( + event_expr); + LTTNG_ASSERT(parent_expr != NULL); + + print_one_event_expr(parent_expr); + + status = lttng_event_expr_array_field_element_get_index( + event_expr, &index); + LTTNG_ASSERT(status == LTTNG_EVENT_EXPR_STATUS_OK); + + printf("[%u]", index); + + break; + } + + default: + abort(); + } +} + +static bool action_group_contains_notify( + const struct lttng_action *action_group) +{ + unsigned int i, count; + enum lttng_action_status status = + lttng_action_list_get_count(action_group, &count); + + if (status != LTTNG_ACTION_STATUS_OK) { + printf("Failed to get action count from action group\n"); + exit(1); + } + + for (i = 0; i < count; i++) { + const struct lttng_action *action = + lttng_action_list_get_at_index(action_group, i); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + + if (action_type == LTTNG_ACTION_TYPE_NOTIFY) { + return true; + } + } + return false; +} + +static int print_capture(const struct lttng_condition *condition, + const struct lttng_event_field_value *capture, + unsigned int indent_level) +{ + int ret = 0; + enum lttng_event_field_value_status event_field_status; + uint64_t u_val; + int64_t s_val; + double d_val; + const char *string_val = NULL; + + switch (lttng_event_field_value_get_type(capture)) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: + { + event_field_status = + lttng_event_field_value_unsigned_int_get_value( + capture, &u_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Unsigned int] %" PRIu64, u_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: + { + event_field_status = + lttng_event_field_value_signed_int_get_value( + capture, &s_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Signed int] %" PRId64, s_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + { + event_field_status = + lttng_event_field_value_unsigned_int_get_value( + capture, &u_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Unsigned enum] %" PRIu64, u_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + { + event_field_status = + lttng_event_field_value_signed_int_get_value( + capture, &s_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Signed enum] %" PRId64, s_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL: + { + event_field_status = lttng_event_field_value_real_get_value( + capture, &d_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[Real] %lf", d_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: + { + event_field_status = lttng_event_field_value_string_get_value( + capture, &string_val); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + printf("[String] %s", string_val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: + printf("[Array] [\n"); + print_array(condition, capture, indent_level); + indent(indent_level); + printf("]\n"); + break; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN: + case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID: + default: + ret = 1; + break; + } + +end: + return ret; +} + +static void print_unavailabe(void) +{ + printf("Capture unavailable"); +} + +static int print_array(const struct lttng_condition *condition, + const struct lttng_event_field_value *array, + unsigned int indent_level) +{ + int ret = 0; + enum lttng_event_field_value_status event_field_status; + unsigned int captured_field_count; + + event_field_status = lttng_event_field_value_array_get_length( + array, &captured_field_count); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + ret = 1; + goto end; + } + + for (unsigned int i = 0; i < captured_field_count; i++) { + const struct lttng_event_field_value *captured_field = NULL; + const struct lttng_event_expr *expr = + lttng_condition_event_rule_matches_get_capture_descriptor_at_index( + condition, i); + LTTNG_ASSERT(expr); + + indent(indent_level + 1); + + printf("Field: "); + print_one_event_expr(expr); + printf(" Value: "); + + event_field_status = + lttng_event_field_value_array_get_element_at_index( + array, i, &captured_field); + if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + if (event_field_status == + LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) { + print_unavailabe(); + } else { + ret = 1; + goto end; + } + } else { + print_capture(condition, captured_field, + indent_level + 1); + } + + if (i + 1 < captured_field_count) { + printf(","); + } else { + printf("."); + } + printf("\n"); + } + +end: + return ret; +} + +static int print_captures(struct lttng_notification *notification) +{ + int ret = 0; + const struct lttng_evaluation *evaluation = + lttng_notification_get_evaluation(notification); + const struct lttng_condition *condition = + lttng_notification_get_condition(notification); + + /* Status */ + enum lttng_condition_status condition_status; + enum lttng_evaluation_event_rule_matches_status evaluation_status; + + const struct lttng_event_field_value *captured_field_array = NULL; + unsigned int expected_capture_field_count; + + LTTNG_ASSERT(lttng_evaluation_get_type(evaluation) == + LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES); + + condition_status = + lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition, + &expected_capture_field_count); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + ret = 1; + goto end; + } + + if (expected_capture_field_count == 0) { + ret = 0; + goto end; + } + + evaluation_status = + lttng_evaluation_event_rule_matches_get_captured_values( + evaluation, &captured_field_array); + if (evaluation_status != LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_OK) { + ret = 1; + goto end; + } + + printf("Captured field values:\n"); + print_array(condition, captured_field_array, 1); +end: + return ret; +} + +static int print_notification(struct lttng_notification *notification) +{ + int ret = 0; + const struct lttng_evaluation *evaluation = + lttng_notification_get_evaluation(notification); + const enum lttng_condition_type type = + lttng_evaluation_get_type(evaluation); + + switch (type) { + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + printf("Received consumed size notification\n"); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + printf("Received buffer usage notification\n"); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + printf("Received session rotation ongoing notification\n"); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + printf("Received session rotation completed notification\n"); + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: + { + const char *trigger_name; + enum lttng_trigger_status trigger_status; + char time_str[64]; + struct timeval tv; + time_t the_time; + const struct lttng_trigger *trigger = NULL; + + gettimeofday(&tv, NULL); + the_time = tv.tv_sec; + + strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T", + localtime(&the_time)); + printf("%s.%ld - ", time_str, tv.tv_usec); + + trigger = lttng_notification_get_trigger(notification); + if (!trigger) { + fprintf(stderr, "Failed to retrieve notification's trigger"); + goto end; + } + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to retrieve trigger's name"); + goto end; + } + + printf("Received notification of event rule matches trigger \"%s\"\n", + trigger_name); + ret = print_captures(notification); + break; + } + default: + fprintf(stderr, "Unknown notification type (%d)\n", type); + } + +end: + return ret; +} + +int main(int argc, char **argv) +{ + int ret; + struct lttng_triggers *triggers = NULL; + unsigned int count, i, j, subcription_count = 0, trigger_count; + enum lttng_trigger_status trigger_status; + struct lttng_notification_channel *notification_channel = NULL; + + if (argc < 2) { + fprintf(stderr, "Missing trigger name(s)\n"); + fprintf(stderr, "Usage: notification-client TRIGGER_NAME ..."); + ret = -1; + goto end; + } + + trigger_count = argc - 1; + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + if (!notification_channel) { + fprintf(stderr, "Failed to create notification channel\n"); + ret = -1; + goto end; + } + + ret = lttng_list_triggers(&triggers); + if (ret != LTTNG_OK) { + fprintf(stderr, "Failed to list triggers\n"); + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to get trigger count\n"); + ret = -1; + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_trigger *trigger = + lttng_triggers_get_at_index(triggers, i); + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const struct lttng_action *action = + lttng_trigger_get_const_action(trigger); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + enum lttng_notification_channel_status channel_status; + const char *trigger_name = NULL; + bool subscribe = false; + + lttng_trigger_get_name(trigger, &trigger_name); + for (j = 0; j < trigger_count; j++) { + if (!strcmp(trigger_name, argv[j + 1])) { + subscribe = true; + break; + } + } + + if (!subscribe) { + continue; + } + + if (!((action_type == LTTNG_ACTION_TYPE_LIST && + action_group_contains_notify(action)) || + action_type == LTTNG_ACTION_TYPE_NOTIFY)) { + printf("The action of trigger \"%s\" is not \"notify\", skipping.\n", + trigger_name); + continue; + } + + channel_status = lttng_notification_channel_subscribe( + notification_channel, condition); + if (channel_status == + LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED) { + continue; + } + if (channel_status) { + fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n", + trigger_name); + ret = -1; + goto end; + } + + printf("Subscribed to notifications of trigger \"%s\"\n", + trigger_name); + subcription_count++; + } + + if (subcription_count == 0) { + printf("No matching trigger with a notify action found.\n"); + ret = 0; + goto end; + } + + for (;;) { + struct lttng_notification *notification; + enum lttng_notification_channel_status channel_status; + + channel_status = + lttng_notification_channel_get_next_notification( + notification_channel, + ¬ification); + switch (channel_status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + printf("Dropped notification\n"); + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED: + ret = 0; + goto end; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: + printf("Notification channel was closed by peer.\n"); + break; + default: + fprintf(stderr, "A communication error occurred on the notification channel.\n"); + ret = -1; + goto end; + } + + ret = print_notification(notification); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } +end: + lttng_triggers_destroy(triggers); + lttng_notification_channel_destroy(notification_channel); + return !!ret; +} diff --git a/tests/regression/tools/live/Makefile.am b/tests/regression/tools/live/Makefile.am index 1587deb12..c67cde86d 100644 --- a/tests/regression/tools/live/Makefile.am +++ b/tests/regression/tools/live/Makefile.am @@ -19,7 +19,7 @@ if HAVE_LIBLTTNG_UST_CTL EXTRA_DIST += test_ust test_ust_tracefile_count test_lttng_ust endif -live_test_SOURCES = live_test.c +live_test_SOURCES = live_test.cpp live_test_LDADD = $(LIBTAP) $(LIBLTTNG_SESSIOND_COMMON) $(DL_LIBS) all-local: diff --git a/tests/regression/tools/live/live_test.c b/tests/regression/tools/live/live_test.c deleted file mode 100644 index 40d0f6270..000000000 --- a/tests/regression/tools/live/live_test.c +++ /dev/null @@ -1,729 +0,0 @@ -/* - * Copyright (C) 2013 Julien Desfossez - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#define SESSION1 "test1" -#define RELAYD_URL "net://localhost" -#define LIVE_TIMER 2000000 - -/* Number of TAP tests in this file */ -#define NUM_TESTS 11 -#define mmap_size 524288 - -#ifdef HAVE_LIBLTTNG_UST_CTL -#include -#include -LTTNG_EXPORT DEFINE_LTTNG_UST_SIGBUS_STATE(); -#endif - -static int control_sock; -struct live_session *session; - -static int first_packet_offset; -static int first_packet_len; -static int first_packet_stream_id = -1; - -struct viewer_stream { - uint64_t id; - uint64_t ctf_trace_id; - void *mmap_base; - int fd; - int metadata_flag; - int first_read; - char path[PATH_MAX]; -}; - -struct live_session { - struct viewer_stream *streams; - uint64_t live_timer_interval; - uint64_t stream_count; -}; - -static -ssize_t lttng_live_recv(int fd, void *buf, size_t len) -{ - ssize_t ret; - size_t copied = 0, to_copy = len; - - do { - ret = recv(fd, buf + copied, to_copy, 0); - if (ret > 0) { - LTTNG_ASSERT(ret <= to_copy); - copied += ret; - to_copy -= ret; - } - } while ((ret > 0 && to_copy > 0) - || (ret < 0 && errno == EINTR)); - if (ret > 0) - ret = copied; - /* ret = 0 means orderly shutdown, ret < 0 is error. */ - return ret; -} - -static -ssize_t lttng_live_send(int fd, const void *buf, size_t len) -{ - ssize_t ret; - - do { - ret = send(fd, buf, len, MSG_NOSIGNAL); - } while (ret < 0 && errno == EINTR); - return ret; -} - -static -int connect_viewer(const char *hostname) -{ - struct hostent *host; - struct sockaddr_in server_addr; - int ret; - - host = gethostbyname(hostname); - if (!host) { - ret = -1; - goto end; - } - - if ((control_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - PERROR("Socket"); - ret = -1; - goto end; - } - - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(5344); - server_addr.sin_addr = *((struct in_addr *) host->h_addr); - bzero(&(server_addr.sin_zero), 8); - - if (connect(control_sock, (struct sockaddr *) &server_addr, - sizeof(struct sockaddr)) == -1) { - PERROR("Connect"); - ret = -1; - goto end; - } - - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(5345); - server_addr.sin_addr = *((struct in_addr *) host->h_addr); - bzero(&(server_addr.sin_zero), 8); - - ret = 0; - -end: - return ret; -} - -static -int establish_connection(void) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_connect connect; - ssize_t ret_len; - - cmd.cmd = htobe32(LTTNG_VIEWER_CONNECT); - cmd.data_size = htobe64(sizeof(connect)); - cmd.cmd_version = htobe32(0); - - memset(&connect, 0, sizeof(connect)); - connect.major = htobe32(VERSION_MAJOR); - connect.minor = htobe32(VERSION_MINOR); - connect.type = htobe32(LTTNG_VIEWER_CLIENT_COMMAND); - - ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - diag("Error sending cmd"); - goto error; - } - ret_len = lttng_live_send(control_sock, &connect, sizeof(connect)); - if (ret_len < 0) { - diag("Error sending version"); - goto error; - } - - ret_len = lttng_live_recv(control_sock, &connect, sizeof(connect)); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error; - } - if (ret_len < 0) { - diag("Error receiving version"); - goto error; - } - return 0; - -error: - return -1; -} - -/* - * Returns the number of sessions, should be 1 during the unit test. - */ -static -int list_sessions(uint64_t *session_id) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_list_sessions list; - struct lttng_viewer_session lsession; - int i; - ssize_t ret_len; - int first_session = 0; - - cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS); - cmd.data_size = htobe64(0); - cmd.cmd_version = htobe32(0); - - ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - diag("Error sending cmd"); - goto error; - } - - ret_len = lttng_live_recv(control_sock, &list, sizeof(list)); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error; - } - if (ret_len < 0) { - diag("Error receiving session list"); - goto error; - } - - for (i = 0; i < be32toh(list.sessions_count); i++) { - ret_len = lttng_live_recv(control_sock, &lsession, sizeof(lsession)); - if (ret_len < 0) { - diag("Error receiving session"); - goto error; - } - if (lsession.streams > 0 && first_session <= 0) { - first_session = be64toh(lsession.id); - *session_id = first_session; - } - } - - return be32toh(list.sessions_count); - -error: - return -1; -} - -static -int create_viewer_session(void) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_create_session_response resp; - ssize_t ret_len; - - cmd.cmd = htobe32(LTTNG_VIEWER_CREATE_SESSION); - cmd.data_size = htobe64(0); - cmd.cmd_version = htobe32(0); - - ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - diag("[error] Error sending cmd"); - goto error; - } - LTTNG_ASSERT(ret_len == sizeof(cmd)); - - ret_len = lttng_live_recv(control_sock, &resp, sizeof(resp)); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error; - } - if (ret_len < 0) { - diag("[error] Error receiving create session reply"); - goto error; - } - LTTNG_ASSERT(ret_len == sizeof(resp)); - - if (be32toh(resp.status) != LTTNG_VIEWER_CREATE_SESSION_OK) { - diag("[error] Error creating viewer session"); - goto error; - } - return 0; - -error: - return -1; -} - -static -int attach_session(uint64_t id) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_attach_session_request rq; - struct lttng_viewer_attach_session_response rp; - struct lttng_viewer_stream stream; - int i; - ssize_t ret_len; - - session = zmalloc(sizeof(struct live_session)); - if (!session) { - goto error; - } - - cmd.cmd = htobe32(LTTNG_VIEWER_ATTACH_SESSION); - cmd.data_size = htobe64(sizeof(rq)); - cmd.cmd_version = htobe32(0); - - memset(&rq, 0, sizeof(rq)); - rq.session_id = htobe64(id); - rq.seek = htobe32(LTTNG_VIEWER_SEEK_BEGINNING); - - ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - diag("Error sending cmd LTTNG_VIEWER_ATTACH_SESSION"); - goto error; - } - ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); - if (ret_len < 0) { - diag("Error sending attach request"); - goto error; - } - - ret_len = lttng_live_recv(control_sock, &rp, sizeof(rp)); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error; - } - if (ret_len < 0) { - diag("Error receiving attach response"); - goto error; - } - if (be32toh(rp.status) != LTTNG_VIEWER_ATTACH_OK) { - goto error; - } - - session->stream_count = be32toh(rp.streams_count); - if (session->stream_count == 0) { - diag("Got session stream count == 0"); - goto error; - } - session->streams = zmalloc(session->stream_count * - sizeof(struct viewer_stream)); - if (!session->streams) { - goto error; - } - - for (i = 0; i < be32toh(rp.streams_count); i++) { - ret_len = lttng_live_recv(control_sock, &stream, sizeof(stream)); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error; - } - if (ret_len < 0) { - diag("Error receiving stream"); - goto error; - } - session->streams[i].id = be64toh(stream.id); - - session->streams[i].ctf_trace_id = be64toh(stream.ctf_trace_id); - session->streams[i].first_read = 1; - session->streams[i].mmap_base = mmap(NULL, mmap_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (session->streams[i].mmap_base == MAP_FAILED) { - diag("mmap error"); - goto error; - } - - if (be32toh(stream.metadata_flag)) { - session->streams[i].metadata_flag = 1; - } - } - return session->stream_count; - -error: - return -1; -} - -static -int get_metadata(void) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_get_metadata rq; - struct lttng_viewer_metadata_packet rp; - ssize_t ret_len; - int ret; - uint64_t i; - char *data = NULL; - uint64_t len = 0; - int metadata_stream_id = -1; - - cmd.cmd = htobe32(LTTNG_VIEWER_GET_METADATA); - cmd.data_size = htobe64(sizeof(rq)); - cmd.cmd_version = htobe32(0); - - for (i = 0; i < session->stream_count; i++) { - if (session->streams[i].metadata_flag) { - metadata_stream_id = i; - break; - } - } - - if (metadata_stream_id < 0) { - diag("No metadata stream found"); - goto error; - } - - rq.stream_id = htobe64(session->streams[metadata_stream_id].id); - -retry: - ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - diag("Error sending cmd"); - goto error; - } - ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); - if (ret_len < 0) { - diag("Error sending get_metadata request"); - goto error; - } - ret_len = lttng_live_recv(control_sock, &rp, sizeof(rp)); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error; - } - if (ret_len < 0) { - diag("Error receiving metadata response"); - goto error; - } - switch (be32toh(rp.status)) { - case LTTNG_VIEWER_METADATA_OK: - break; - case LTTNG_VIEWER_NO_NEW_METADATA: - diag("Got LTTNG_VIEWER_NO_NEW_METADATA:"); - usleep(50); - goto retry; - case LTTNG_VIEWER_METADATA_ERR: - diag("Got LTTNG_VIEWER_METADATA_ERR:"); - goto error; - default: - diag("Got unknown status during LTTNG_VIEWER_GET_METADATA"); - goto error; - } - - len = be64toh(rp.len); - if (len <= 0) { - goto error; - } - - data = zmalloc(len); - if (!data) { - PERROR("relay data zmalloc"); - goto error; - } - ret_len = lttng_live_recv(control_sock, data, len); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error_free_data; - } - if (ret_len < 0) { - diag("Error receiving trace packet"); - goto error_free_data; - } - free(data); - ret = len; - - return ret; - -error_free_data: - free(data); -error: - return -1; -} - -static -int get_next_index(void) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_get_next_index rq; - struct lttng_viewer_index rp; - ssize_t ret_len; - int id; - - cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEXT_INDEX); - cmd.data_size = htobe64(sizeof(rq)); - cmd.cmd_version = htobe32(0); - - for (id = 0; id < session->stream_count; id++) { - if (session->streams[id].metadata_flag) { - continue; - } - memset(&rq, 0, sizeof(rq)); - rq.stream_id = htobe64(session->streams[id].id); - -retry: - ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - diag("Error sending cmd"); - goto error; - } - ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); - if (ret_len < 0) { - diag("Error sending get_next_index request"); - goto error; - } - ret_len = lttng_live_recv(control_sock, &rp, sizeof(rp)); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error; - } - if (ret_len < 0) { - diag("Error receiving index response"); - goto error; - } - - rp.flags = be32toh(rp.flags); - - switch (be32toh(rp.status)) { - case LTTNG_VIEWER_INDEX_INACTIVE: - /* Skip this stream. */ - diag("Got LTTNG_VIEWER_INDEX_INACTIVE"); - continue; - case LTTNG_VIEWER_INDEX_OK: - break; - case LTTNG_VIEWER_INDEX_RETRY: - sleep(1); - goto retry; - case LTTNG_VIEWER_INDEX_HUP: - diag("Got LTTNG_VIEWER_INDEX_HUP"); - session->streams[id].id = -1ULL; - session->streams[id].fd = -1; - goto error; - case LTTNG_VIEWER_INDEX_ERR: - diag("Got LTTNG_VIEWER_INDEX_ERR"); - goto error; - default: - diag("Unknown reply status during LTTNG_VIEWER_GET_NEXT_INDEX (%d)", be32toh(rp.status)); - goto error; - } - if (first_packet_stream_id < 0) { - /* - * Initialize the first packet stream id. That is, - * the first active stream encoutered. - */ - first_packet_offset = be64toh(rp.offset); - first_packet_len = be64toh(rp.packet_size) / CHAR_BIT; - first_packet_stream_id = id; - diag("Got first packet index with offset %d and len %d", - first_packet_offset, first_packet_len); - } - } - return 0; - -error: - return -1; -} - -static -int get_data_packet(int id, uint64_t offset, - uint64_t len) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_get_packet rq; - struct lttng_viewer_trace_packet rp; - ssize_t ret_len; - - cmd.cmd = htobe32(LTTNG_VIEWER_GET_PACKET); - cmd.data_size = htobe64(sizeof(rq)); - cmd.cmd_version = htobe32(0); - - memset(&rq, 0, sizeof(rq)); - rq.stream_id = htobe64(session->streams[id].id); - /* Already in big endian. */ - rq.offset = offset; - rq.len = htobe32(len); - - ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - diag("Error sending cmd"); - goto error; - } - ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); - if (ret_len < 0) { - diag("Error sending get_data_packet request"); - goto error; - } - ret_len = lttng_live_recv(control_sock, &rp, sizeof(rp)); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error; - } - if (ret_len < 0) { - diag("Error receiving data response"); - goto error; - } - rp.flags = be32toh(rp.flags); - - switch (be32toh(rp.status)) { - case LTTNG_VIEWER_GET_PACKET_OK: - len = be32toh(rp.len); - if (len == 0) { - diag("Got LTTNG_VIEWER_GET_PACKET_OK, but len == 0"); - goto error; - } - break; - case LTTNG_VIEWER_GET_PACKET_RETRY: - diag("Got LTTNG_VIEWER_GET_PACKET_RETRY:"); - goto error; - case LTTNG_VIEWER_GET_PACKET_ERR: - if (rp.flags & LTTNG_VIEWER_FLAG_NEW_METADATA) { - diag("Got LTTNG_VIEWER_GET_PACKET_ERR with NEW_METADATA flag"); - goto end; - } - diag("Got LTTNG_VIEWER_GET_PACKET_ERR:"); - goto error; - default: - diag("Got unknown status code during LTTNG_VIEWER_GET_PACKET"); - goto error; - } - - if (len > mmap_size) { - diag("mmap_size not big enough"); - goto error; - } - - ret_len = lttng_live_recv(control_sock, session->streams[id].mmap_base, len); - if (ret_len == 0) { - diag("[error] Remote side has closed connection"); - goto error; - } - if (ret_len < 0) { - diag("Error receiving trace packet"); - goto error; - } -end: - return 0; -error: - return -1; -} - -static -int detach_viewer_session(uint64_t id) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_detach_session_response resp; - struct lttng_viewer_detach_session_request rq; - int ret; - ssize_t ret_len; - - cmd.cmd = htobe32(LTTNG_VIEWER_DETACH_SESSION); - cmd.data_size = htobe64(sizeof(rq)); - cmd.cmd_version = htobe32(0); - - memset(&rq, 0, sizeof(rq)); - rq.session_id = htobe64(id); - - ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - fprintf(stderr, "[error] Error sending cmd\n"); - ret = ret_len; - goto error; - } - - ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); - if (ret_len < 0) { - fprintf(stderr, "Error sending attach request\n"); - ret = ret_len; - goto error; - } - - ret_len = lttng_live_recv(control_sock, &resp, sizeof(resp)); - if (ret_len < 0) { - fprintf(stderr, "[error] Error receiving detach session reply\n"); - ret = ret_len; - goto error; - } - - if (be32toh(resp.status) != LTTNG_VIEWER_DETACH_SESSION_OK) { - fprintf(stderr, "[error] Error detaching viewer session\n"); - ret = -1; - goto error; - } - ret = 0; - -error: - return ret; -} - -int main(int argc, char **argv) -{ - int ret; - uint64_t session_id; - - plan_tests(NUM_TESTS); - - diag("Live unit tests"); - - ret = connect_viewer("localhost"); - ok(ret == 0, "Connect viewer to relayd"); - - ret = establish_connection(); - ok(ret == 0, "Established connection and version check with %d.%d", - VERSION_MAJOR, VERSION_MINOR); - - ret = list_sessions(&session_id); - ok(ret > 0, "List sessions : %d session(s)", ret); - if (ret < 0) { - goto end; - } - - ret = create_viewer_session(); - ok(ret == 0, "Create viewer session"); - - ret = attach_session(session_id); - ok(ret > 0, "Attach to session, %d stream(s) received", ret); - - ret = get_metadata(); - ok(ret > 0, "Get metadata, received %d bytes", ret); - - ret = get_next_index(); - ok(ret == 0, "Get one index per stream"); - - ret = get_data_packet(first_packet_stream_id, first_packet_offset, - first_packet_len); - ok(ret == 0, - "Get one data packet for stream %d, offset %d, len %d", - first_packet_stream_id, first_packet_offset, - first_packet_len); - - ret = detach_viewer_session(session_id); - ok(ret == 0, "Detach viewer session"); - - ret = list_sessions(&session_id); - ok(ret > 0, "List sessions : %d session(s)", ret); - - ret = attach_session(session_id); - ok(ret > 0, "Attach to session, %d streams received", ret); -end: - return exit_status(); -} diff --git a/tests/regression/tools/live/live_test.cpp b/tests/regression/tools/live/live_test.cpp new file mode 100644 index 000000000..afaf23b65 --- /dev/null +++ b/tests/regression/tools/live/live_test.cpp @@ -0,0 +1,729 @@ +/* + * Copyright (C) 2013 Julien Desfossez + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#define SESSION1 "test1" +#define RELAYD_URL "net://localhost" +#define LIVE_TIMER 2000000 + +/* Number of TAP tests in this file */ +#define NUM_TESTS 11 +#define mmap_size 524288 + +#ifdef HAVE_LIBLTTNG_UST_CTL +#include +#include +LTTNG_EXPORT DEFINE_LTTNG_UST_SIGBUS_STATE(); +#endif + +static int control_sock; +struct live_session *session; + +static int first_packet_offset; +static int first_packet_len; +static int first_packet_stream_id = -1; + +struct viewer_stream { + uint64_t id; + uint64_t ctf_trace_id; + void *mmap_base; + int fd; + int metadata_flag; + int first_read; + char path[PATH_MAX]; +}; + +struct live_session { + struct viewer_stream *streams; + uint64_t live_timer_interval; + uint64_t stream_count; +}; + +static +ssize_t lttng_live_recv(int fd, void *buf, size_t len) +{ + ssize_t ret; + size_t copied = 0, to_copy = len; + + do { + ret = recv(fd, (char *) buf + copied, to_copy, 0); + if (ret > 0) { + LTTNG_ASSERT(ret <= to_copy); + copied += ret; + to_copy -= ret; + } + } while ((ret > 0 && to_copy > 0) + || (ret < 0 && errno == EINTR)); + if (ret > 0) + ret = copied; + /* ret = 0 means orderly shutdown, ret < 0 is error. */ + return ret; +} + +static +ssize_t lttng_live_send(int fd, const void *buf, size_t len) +{ + ssize_t ret; + + do { + ret = send(fd, buf, len, MSG_NOSIGNAL); + } while (ret < 0 && errno == EINTR); + return ret; +} + +static +int connect_viewer(const char *hostname) +{ + struct hostent *host; + struct sockaddr_in server_addr; + int ret; + + host = gethostbyname(hostname); + if (!host) { + ret = -1; + goto end; + } + + if ((control_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + PERROR("Socket"); + ret = -1; + goto end; + } + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(5344); + server_addr.sin_addr = *((struct in_addr *) host->h_addr); + bzero(&(server_addr.sin_zero), 8); + + if (connect(control_sock, (struct sockaddr *) &server_addr, + sizeof(struct sockaddr)) == -1) { + PERROR("Connect"); + ret = -1; + goto end; + } + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(5345); + server_addr.sin_addr = *((struct in_addr *) host->h_addr); + bzero(&(server_addr.sin_zero), 8); + + ret = 0; + +end: + return ret; +} + +static +int establish_connection(void) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_connect connect; + ssize_t ret_len; + + cmd.cmd = htobe32(LTTNG_VIEWER_CONNECT); + cmd.data_size = htobe64(sizeof(connect)); + cmd.cmd_version = htobe32(0); + + memset(&connect, 0, sizeof(connect)); + connect.major = htobe32(VERSION_MAJOR); + connect.minor = htobe32(VERSION_MINOR); + connect.type = htobe32(LTTNG_VIEWER_CLIENT_COMMAND); + + ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); + if (ret_len < 0) { + diag("Error sending cmd"); + goto error; + } + ret_len = lttng_live_send(control_sock, &connect, sizeof(connect)); + if (ret_len < 0) { + diag("Error sending version"); + goto error; + } + + ret_len = lttng_live_recv(control_sock, &connect, sizeof(connect)); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error; + } + if (ret_len < 0) { + diag("Error receiving version"); + goto error; + } + return 0; + +error: + return -1; +} + +/* + * Returns the number of sessions, should be 1 during the unit test. + */ +static +int list_sessions(uint64_t *session_id) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_list_sessions list; + struct lttng_viewer_session lsession; + int i; + ssize_t ret_len; + int first_session = 0; + + cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS); + cmd.data_size = htobe64(0); + cmd.cmd_version = htobe32(0); + + ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); + if (ret_len < 0) { + diag("Error sending cmd"); + goto error; + } + + ret_len = lttng_live_recv(control_sock, &list, sizeof(list)); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error; + } + if (ret_len < 0) { + diag("Error receiving session list"); + goto error; + } + + for (i = 0; i < be32toh(list.sessions_count); i++) { + ret_len = lttng_live_recv(control_sock, &lsession, sizeof(lsession)); + if (ret_len < 0) { + diag("Error receiving session"); + goto error; + } + if (lsession.streams > 0 && first_session <= 0) { + first_session = be64toh(lsession.id); + *session_id = first_session; + } + } + + return be32toh(list.sessions_count); + +error: + return -1; +} + +static +int create_viewer_session(void) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_create_session_response resp; + ssize_t ret_len; + + cmd.cmd = htobe32(LTTNG_VIEWER_CREATE_SESSION); + cmd.data_size = htobe64(0); + cmd.cmd_version = htobe32(0); + + ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); + if (ret_len < 0) { + diag("[error] Error sending cmd"); + goto error; + } + LTTNG_ASSERT(ret_len == sizeof(cmd)); + + ret_len = lttng_live_recv(control_sock, &resp, sizeof(resp)); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error; + } + if (ret_len < 0) { + diag("[error] Error receiving create session reply"); + goto error; + } + LTTNG_ASSERT(ret_len == sizeof(resp)); + + if (be32toh(resp.status) != LTTNG_VIEWER_CREATE_SESSION_OK) { + diag("[error] Error creating viewer session"); + goto error; + } + return 0; + +error: + return -1; +} + +static +int attach_session(uint64_t id) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_attach_session_request rq; + struct lttng_viewer_attach_session_response rp; + struct lttng_viewer_stream stream; + int i; + ssize_t ret_len; + + session = (live_session *) zmalloc(sizeof(struct live_session)); + if (!session) { + goto error; + } + + cmd.cmd = htobe32(LTTNG_VIEWER_ATTACH_SESSION); + cmd.data_size = htobe64(sizeof(rq)); + cmd.cmd_version = htobe32(0); + + memset(&rq, 0, sizeof(rq)); + rq.session_id = htobe64(id); + rq.seek = htobe32(LTTNG_VIEWER_SEEK_BEGINNING); + + ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); + if (ret_len < 0) { + diag("Error sending cmd LTTNG_VIEWER_ATTACH_SESSION"); + goto error; + } + ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); + if (ret_len < 0) { + diag("Error sending attach request"); + goto error; + } + + ret_len = lttng_live_recv(control_sock, &rp, sizeof(rp)); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error; + } + if (ret_len < 0) { + diag("Error receiving attach response"); + goto error; + } + if (be32toh(rp.status) != LTTNG_VIEWER_ATTACH_OK) { + goto error; + } + + session->stream_count = be32toh(rp.streams_count); + if (session->stream_count == 0) { + diag("Got session stream count == 0"); + goto error; + } + session->streams = (viewer_stream *) zmalloc(session->stream_count * + sizeof(struct viewer_stream)); + if (!session->streams) { + goto error; + } + + for (i = 0; i < be32toh(rp.streams_count); i++) { + ret_len = lttng_live_recv(control_sock, &stream, sizeof(stream)); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error; + } + if (ret_len < 0) { + diag("Error receiving stream"); + goto error; + } + session->streams[i].id = be64toh(stream.id); + + session->streams[i].ctf_trace_id = be64toh(stream.ctf_trace_id); + session->streams[i].first_read = 1; + session->streams[i].mmap_base = mmap(NULL, mmap_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (session->streams[i].mmap_base == MAP_FAILED) { + diag("mmap error"); + goto error; + } + + if (be32toh(stream.metadata_flag)) { + session->streams[i].metadata_flag = 1; + } + } + return session->stream_count; + +error: + return -1; +} + +static +int get_metadata(void) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_get_metadata rq; + struct lttng_viewer_metadata_packet rp; + ssize_t ret_len; + int ret; + uint64_t i; + char *data = NULL; + uint64_t len = 0; + int metadata_stream_id = -1; + + cmd.cmd = htobe32(LTTNG_VIEWER_GET_METADATA); + cmd.data_size = htobe64(sizeof(rq)); + cmd.cmd_version = htobe32(0); + + for (i = 0; i < session->stream_count; i++) { + if (session->streams[i].metadata_flag) { + metadata_stream_id = i; + break; + } + } + + if (metadata_stream_id < 0) { + diag("No metadata stream found"); + goto error; + } + + rq.stream_id = htobe64(session->streams[metadata_stream_id].id); + +retry: + ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); + if (ret_len < 0) { + diag("Error sending cmd"); + goto error; + } + ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); + if (ret_len < 0) { + diag("Error sending get_metadata request"); + goto error; + } + ret_len = lttng_live_recv(control_sock, &rp, sizeof(rp)); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error; + } + if (ret_len < 0) { + diag("Error receiving metadata response"); + goto error; + } + switch (be32toh(rp.status)) { + case LTTNG_VIEWER_METADATA_OK: + break; + case LTTNG_VIEWER_NO_NEW_METADATA: + diag("Got LTTNG_VIEWER_NO_NEW_METADATA:"); + usleep(50); + goto retry; + case LTTNG_VIEWER_METADATA_ERR: + diag("Got LTTNG_VIEWER_METADATA_ERR:"); + goto error; + default: + diag("Got unknown status during LTTNG_VIEWER_GET_METADATA"); + goto error; + } + + len = be64toh(rp.len); + if (len <= 0) { + goto error; + } + + data = (char *) zmalloc(len); + if (!data) { + PERROR("relay data zmalloc"); + goto error; + } + ret_len = lttng_live_recv(control_sock, data, len); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error_free_data; + } + if (ret_len < 0) { + diag("Error receiving trace packet"); + goto error_free_data; + } + free(data); + ret = len; + + return ret; + +error_free_data: + free(data); +error: + return -1; +} + +static +int get_next_index(void) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_get_next_index rq; + struct lttng_viewer_index rp; + ssize_t ret_len; + int id; + + cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEXT_INDEX); + cmd.data_size = htobe64(sizeof(rq)); + cmd.cmd_version = htobe32(0); + + for (id = 0; id < session->stream_count; id++) { + if (session->streams[id].metadata_flag) { + continue; + } + memset(&rq, 0, sizeof(rq)); + rq.stream_id = htobe64(session->streams[id].id); + +retry: + ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); + if (ret_len < 0) { + diag("Error sending cmd"); + goto error; + } + ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); + if (ret_len < 0) { + diag("Error sending get_next_index request"); + goto error; + } + ret_len = lttng_live_recv(control_sock, &rp, sizeof(rp)); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error; + } + if (ret_len < 0) { + diag("Error receiving index response"); + goto error; + } + + rp.flags = be32toh(rp.flags); + + switch (be32toh(rp.status)) { + case LTTNG_VIEWER_INDEX_INACTIVE: + /* Skip this stream. */ + diag("Got LTTNG_VIEWER_INDEX_INACTIVE"); + continue; + case LTTNG_VIEWER_INDEX_OK: + break; + case LTTNG_VIEWER_INDEX_RETRY: + sleep(1); + goto retry; + case LTTNG_VIEWER_INDEX_HUP: + diag("Got LTTNG_VIEWER_INDEX_HUP"); + session->streams[id].id = -1ULL; + session->streams[id].fd = -1; + goto error; + case LTTNG_VIEWER_INDEX_ERR: + diag("Got LTTNG_VIEWER_INDEX_ERR"); + goto error; + default: + diag("Unknown reply status during LTTNG_VIEWER_GET_NEXT_INDEX (%d)", be32toh(rp.status)); + goto error; + } + if (first_packet_stream_id < 0) { + /* + * Initialize the first packet stream id. That is, + * the first active stream encoutered. + */ + first_packet_offset = be64toh(rp.offset); + first_packet_len = be64toh(rp.packet_size) / CHAR_BIT; + first_packet_stream_id = id; + diag("Got first packet index with offset %d and len %d", + first_packet_offset, first_packet_len); + } + } + return 0; + +error: + return -1; +} + +static +int get_data_packet(int id, uint64_t offset, + uint64_t len) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_get_packet rq; + struct lttng_viewer_trace_packet rp; + ssize_t ret_len; + + cmd.cmd = htobe32(LTTNG_VIEWER_GET_PACKET); + cmd.data_size = htobe64(sizeof(rq)); + cmd.cmd_version = htobe32(0); + + memset(&rq, 0, sizeof(rq)); + rq.stream_id = htobe64(session->streams[id].id); + /* Already in big endian. */ + rq.offset = offset; + rq.len = htobe32(len); + + ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); + if (ret_len < 0) { + diag("Error sending cmd"); + goto error; + } + ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); + if (ret_len < 0) { + diag("Error sending get_data_packet request"); + goto error; + } + ret_len = lttng_live_recv(control_sock, &rp, sizeof(rp)); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error; + } + if (ret_len < 0) { + diag("Error receiving data response"); + goto error; + } + rp.flags = be32toh(rp.flags); + + switch (be32toh(rp.status)) { + case LTTNG_VIEWER_GET_PACKET_OK: + len = be32toh(rp.len); + if (len == 0) { + diag("Got LTTNG_VIEWER_GET_PACKET_OK, but len == 0"); + goto error; + } + break; + case LTTNG_VIEWER_GET_PACKET_RETRY: + diag("Got LTTNG_VIEWER_GET_PACKET_RETRY:"); + goto error; + case LTTNG_VIEWER_GET_PACKET_ERR: + if (rp.flags & LTTNG_VIEWER_FLAG_NEW_METADATA) { + diag("Got LTTNG_VIEWER_GET_PACKET_ERR with NEW_METADATA flag"); + goto end; + } + diag("Got LTTNG_VIEWER_GET_PACKET_ERR:"); + goto error; + default: + diag("Got unknown status code during LTTNG_VIEWER_GET_PACKET"); + goto error; + } + + if (len > mmap_size) { + diag("mmap_size not big enough"); + goto error; + } + + ret_len = lttng_live_recv(control_sock, session->streams[id].mmap_base, len); + if (ret_len == 0) { + diag("[error] Remote side has closed connection"); + goto error; + } + if (ret_len < 0) { + diag("Error receiving trace packet"); + goto error; + } +end: + return 0; +error: + return -1; +} + +static +int detach_viewer_session(uint64_t id) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_detach_session_response resp; + struct lttng_viewer_detach_session_request rq; + int ret; + ssize_t ret_len; + + cmd.cmd = htobe32(LTTNG_VIEWER_DETACH_SESSION); + cmd.data_size = htobe64(sizeof(rq)); + cmd.cmd_version = htobe32(0); + + memset(&rq, 0, sizeof(rq)); + rq.session_id = htobe64(id); + + ret_len = lttng_live_send(control_sock, &cmd, sizeof(cmd)); + if (ret_len < 0) { + fprintf(stderr, "[error] Error sending cmd\n"); + ret = ret_len; + goto error; + } + + ret_len = lttng_live_send(control_sock, &rq, sizeof(rq)); + if (ret_len < 0) { + fprintf(stderr, "Error sending attach request\n"); + ret = ret_len; + goto error; + } + + ret_len = lttng_live_recv(control_sock, &resp, sizeof(resp)); + if (ret_len < 0) { + fprintf(stderr, "[error] Error receiving detach session reply\n"); + ret = ret_len; + goto error; + } + + if (be32toh(resp.status) != LTTNG_VIEWER_DETACH_SESSION_OK) { + fprintf(stderr, "[error] Error detaching viewer session\n"); + ret = -1; + goto error; + } + ret = 0; + +error: + return ret; +} + +int main(int argc, char **argv) +{ + int ret; + uint64_t session_id; + + plan_tests(NUM_TESTS); + + diag("Live unit tests"); + + ret = connect_viewer("localhost"); + ok(ret == 0, "Connect viewer to relayd"); + + ret = establish_connection(); + ok(ret == 0, "Established connection and version check with %d.%d", + VERSION_MAJOR, VERSION_MINOR); + + ret = list_sessions(&session_id); + ok(ret > 0, "List sessions : %d session(s)", ret); + if (ret < 0) { + goto end; + } + + ret = create_viewer_session(); + ok(ret == 0, "Create viewer session"); + + ret = attach_session(session_id); + ok(ret > 0, "Attach to session, %d stream(s) received", ret); + + ret = get_metadata(); + ok(ret > 0, "Get metadata, received %d bytes", ret); + + ret = get_next_index(); + ok(ret == 0, "Get one index per stream"); + + ret = get_data_packet(first_packet_stream_id, first_packet_offset, + first_packet_len); + ok(ret == 0, + "Get one data packet for stream %d, offset %d, len %d", + first_packet_stream_id, first_packet_offset, + first_packet_len); + + ret = detach_viewer_session(session_id); + ok(ret == 0, "Detach viewer session"); + + ret = list_sessions(&session_id); + ok(ret > 0, "List sessions : %d session(s)", ret); + + ret = attach_session(session_id); + ok(ret > 0, "Attach to session, %d streams received", ret); +end: + return exit_status(); +} diff --git a/tests/regression/tools/notification/Makefile.am b/tests/regression/tools/notification/Makefile.am index 2f1a9174e..4029a2126 100644 --- a/tests/regression/tools/notification/Makefile.am +++ b/tests/regression/tools/notification/Makefile.am @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -AM_CFLAGS += -I$(top_srcdir)/tests/utils +AM_CPPFLAGS += -I$(top_srcdir)/tests/utils LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la LIB_LTTNG_CTL = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la @@ -14,7 +14,7 @@ EXTRA_DIST = \ base_client.c \ consumer_testpoints.cpp \ sessiond_testpoints.cpp \ - notification.c \ + notification.cpp \ test_notification_kernel_buffer_usage \ test_notification_kernel_capture \ test_notification_kernel_error \ @@ -54,9 +54,9 @@ noinst_LTLIBRARIES = libpause_sessiond.la libpause_consumer.la base_client_SOURCES = base_client.c base_client_LDADD = $(LIB_LTTNG_CTL) -notification_SOURCES = notification.c +notification_SOURCES = notification.cpp # Tests the deprecated lttng_register_trigger() interface -notification_CFLAGS = -Wno-deprecated-declarations $(AM_CFLAGS) +notification_CXXFLAGS = -Wno-deprecated-declarations $(AM_CXXFLAGS) notification_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm rotation_SOURCES = rotation.c diff --git a/tests/regression/tools/notification/consumer_testpoints.cpp b/tests/regression/tools/notification/consumer_testpoints.cpp index 07c855c9c..05db3421a 100644 --- a/tests/regression/tools/notification/consumer_testpoints.cpp +++ b/tests/regression/tools/notification/consumer_testpoints.cpp @@ -20,7 +20,8 @@ static char *pause_pipe_path; static struct lttng_pipe *pause_pipe; static int *data_consumption_state; -static enum lttng_consumer_type (*lttng_consumer_get_type)(void); +using lttng_consumer_get_type_func = enum lttng_consumer_type (*)(); +static lttng_consumer_get_type_func lttng_consumer_get_type; int lttng_opt_verbose; int lttng_opt_mi; diff --git a/tests/regression/tools/notification/notification.c b/tests/regression/tools/notification/notification.c deleted file mode 100644 index a56b7945d..000000000 --- a/tests/regression/tools/notification/notification.c +++ /dev/null @@ -1,2689 +0,0 @@ -/* - * notification.c - * - * Tests suite for LTTng notification API - * - * Copyright (C) 2017 Jonathan Rajotte - * - * SPDX-License-Identifier: MIT - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#define FIELD_NAME_MAX_LEN 256 - -/* A callback to populate the condition capture descriptor. */ -typedef int (*condition_capture_desc_cb)(struct lttng_condition *condition); - -/* A callback for captured field validation. */ -typedef int (*validate_cb)(const struct lttng_event_field_value *event_field, unsigned iteration); - -int nb_args = 0; -int named_pipe_args_start = 0; -pid_t app_pid = 0; -const char *app_state_file = NULL; - -enum field_type { - FIELD_TYPE_PAYLOAD, - FIELD_TYPE_CONTEXT, - FIELD_TYPE_APP_CONTEXT, - FIELD_TYPE_ARRAY_FIELD, -}; - -struct capture_base_field_tuple { - char* field_name; - enum field_type field_type; - /* Do we expect a userspace capture? */ - bool expected_ust; - /* Do we expect a kernel capture? */ - bool expected_kernel; - validate_cb validate_ust; - validate_cb validate_kernel; -}; - -static -const char *field_value_type_to_str(enum lttng_event_field_value_type type) -{ - switch (type) { - case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN: - return "UNKNOWN"; - case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID: - return "INVALID"; - case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: - return "UNSIGNED INT"; - case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: - return "SIGNED INT"; - case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: - return "UNSIGNED ENUM"; - case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: - return "SIGNED ENUM"; - case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL: - return "REAL"; - case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: - return "STRING"; - case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: - return "ARRAY"; - default: - abort(); - } -} - -static int validate_type(const struct lttng_event_field_value *event_field, - enum lttng_event_field_value_type expect) -{ - int ret; - enum lttng_event_field_value_type value; - - value = lttng_event_field_value_get_type(event_field); - if (value == LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID) { - ret = 1; - goto end; - } - - ok(expect == value, "Expected field type %s, got %s", - field_value_type_to_str(expect), - field_value_type_to_str(value)); - - ret = expect != value; - -end: - return ret; -} - -/* - * Validate unsigned captured field against the iteration number. - */ -static int validate_unsigned_int_field( - const struct lttng_event_field_value *event_field, - unsigned int expected_value) -{ - int ret; - uint64_t value; - enum lttng_event_field_value_status status; - - ret = validate_type( - event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); - if (ret) { - goto end; - } - - status = lttng_event_field_value_unsigned_int_get_value( - event_field, &value); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_unsigned_int_get_value returned an error: status = %d", - (int) status); - ret = 1; - goto end; - } - - ok(value == (uint64_t) expected_value, - "Expected unsigned integer value %u, got %" PRIu64, - expected_value, value); - - ret = value != (uint64_t) expected_value; - -end: - return ret; -} - -/* - * Validate signed captured field. - */ -static int validate_signed_int_field( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - int ret; - const int64_t expected = -1; - int64_t value; - enum lttng_event_field_value_status status; - - /* Unused. */ - (void) iteration; - - ret = validate_type( - event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT); - if (ret) { - goto end; - } - - status = lttng_event_field_value_signed_int_get_value( - event_field, &value); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_signed_int_get_value returned an error: status = %d", - (int) status); - ret = 1; - goto end; - } - - ok(value == expected, - "Expected signed integer value %" PRId64 - ", got %" PRId64, - expected, value); - - ret = value != expected; - -end: - - return ret; -} - -/* - * Validate array of unsigned int. - */ -static int validate_array_unsigned_int_field( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - int ret; - enum lttng_event_field_value_status status; - const unsigned int expected = 3; - unsigned int i, count; - - /* Unused. */ - (void) iteration; - - ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); - if (ret) { - goto end; - } - - status = lttng_event_field_value_array_get_length(event_field, &count); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_array_get_length"); - ret = 1; - goto end; - } - - ok(count == expected, "Expected %d subelements, got %d", expected, - count); - if (count != expected) { - ret = 1; - goto end; - } - - for (i = 1; i < count + 1; i++) { - const struct lttng_event_field_value *value; - - status = lttng_event_field_value_array_get_element_at_index( - event_field, i - 1, &value); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_array_get_element_at_index returned an error: status = %d", - (int) status); - ret = 1; - goto end; - } - - ret = validate_unsigned_int_field(value, i); - if (ret) { - goto end; - } - } - - ret = 0; -end: - - return ret; -} - -static int validate_array_unsigned_int_field_at_index( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - int ret; - const uint64_t expected_value = 2; - enum lttng_event_field_value_status status; - uint64_t value; - - /* Unused. */ - (void) iteration; - - ret = validate_type( - event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); - if (ret) { - goto end; - } - - status = lttng_event_field_value_unsigned_int_get_value( - event_field, &value); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_unsigned_int_get_value returned an error: status = %d", - (int) status); - ret = 1; - goto end; - } - - ok(value == expected_value, - "Expected unsigned integer value %u, got %" PRIu64, - expected_value, value); - - ret = 0; -end: - return ret; -} - -/* - * Validate sequence for a string (seqfield1): - * - * Value: "test" encoded in UTF-8: [116, 101, 115, 116] - */ -static int validate_seqfield1(const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - int ret; - enum lttng_event_field_value_status status; - unsigned int i, count; - const unsigned int expect[] = {116, 101, 115, 116}; - const size_t array_count = sizeof(expect) / sizeof(*expect); - - /* Unused. */ - (void) iteration; - - ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); - if (ret) { - goto end; - } - - status = lttng_event_field_value_array_get_length(event_field, &count); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_array_get_length returned an error: status = %d", - (int) status); - ret = 1; - goto end; - } - - ok(count == array_count, "Expected %zu array sub-elements, got %d", - array_count, count); - if (count != array_count) { - ret = 1; - goto end; - } - - for (i = 0; i < count; i++) { - const struct lttng_event_field_value *value; - - status = lttng_event_field_value_array_get_element_at_index( - event_field, i, &value); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_array_get_element_at_index returned an error: status = %d", - (int) status); - ret = 1; - goto end; - } - - ret = validate_unsigned_int_field(value, expect[i]); - if (ret) { - goto end; - } - } - - ret = 0; -end: - return ret; -} - -static int validate_string( - const struct lttng_event_field_value *event_field, - const char *expect) -{ - int ret; - const char *value = NULL; - enum lttng_event_field_value_status status; - - ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_STRING); - if (ret) { - goto end; - } - - status = lttng_event_field_value_string_get_value(event_field, &value); - if (!value) { - fail("lttng_event_field_value_array_get_length returned an error: status = %d", - (int) status); - ret = 1; - goto end; - } - - ok(!strcmp(value, expect), "Expected string value \"%s\", got \"%s\"", - expect, value); - - ret = 0; -end: - - return ret; -} - -/* - * Validate string. Expected value is "test". - */ -static int validate_string_test( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - const char * const expect = "test"; - - /* Unused. */ - (void) iteration; - - return validate_string(event_field, expect); -} - -/* - * Validate escaped string. Expected value is "\*". - */ -static int validate_string_escaped( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - const char * const expect = "\\*"; - - /* Unused. */ - (void) iteration; - - return validate_string(event_field, expect); -} - -/* - * Validate real field. - */ -static int validate_real( - const struct lttng_event_field_value *event_field, - double expect) -{ - int ret; - double value; - enum lttng_event_field_value_status status; - - ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_REAL); - if (ret) { - goto end; - } - - status = lttng_event_field_value_real_get_value(event_field, &value); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_real_get_value returned an error: status = %d", - (int) status); - ret = 1; - goto end; - } - - ok(value == expect, "Expected real value %f, got %f", expect, value); - ret = value != expect; -end: - return ret; -} - -/* - * Validate floatfield. - */ -static int validate_floatfield( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - const double expect = 2222.0; - - /* Unused. */ - (void) iteration; - - return validate_real(event_field, expect); -} - -/* - * Validate doublefield. - */ -static int validate_doublefield( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - const double expect = 2.0; - - /* Unused. */ - (void) iteration; - - return validate_real(event_field, expect); -} - -/* - * Validate enum0: enum0 = ( "AUTO: EXPECT 0" : container = 0 ) - */ -static int validate_enum0(const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - int ret; - enum lttng_event_field_value_status status; - uint64_t value; - const uint64_t expected_value = 0; - - /* Unused. */ - (void) iteration; - - ret = validate_type(event_field, - LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM); - if (ret) { - goto end; - } - - status = lttng_event_field_value_unsigned_int_get_value( - event_field, &value); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_unsigned_int_get_value returned an error: status = %d", - (int) status); - ret = 1; - goto end; - } - - ok(value == expected_value, - "Expected enum value %" PRIu64 ", got %" PRIu64, - expected_value, value); - -end: - return ret; -} - -/* - * Validate enumnegative: enumnegative = ( "AUTO: EXPECT 0" : container = 0 ) - * - * We expect 2 labels here. - */ -static int validate_enumnegative( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - int ret; - enum lttng_event_field_value_status status; - int64_t value; - const int64_t expected_value = -1; - - /* Unused. */ - (void) iteration; - - ret = validate_type(event_field, - LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM); - if (ret) { - goto end; - } - - status = lttng_event_field_value_signed_int_get_value( - event_field, &value); - if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("lttng_event_field_value_unsigned_int_get_value"); - ret = 1; - goto end; - } - - ok(value == expected_value, - "Expected enum value %" PRId64 ", got %" PRId64, - expected_value, value); - -end: - return ret; -} - -static int validate_context_procname_ust( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - /* Unused. */ - (void) iteration; - return validate_string(event_field, "gen-ust-events"); -} - -static int validate_context_procname_kernel( - const struct lttng_event_field_value *event_field, - unsigned int iteration) -{ - /* Unused. */ - (void) iteration; - return validate_string(event_field, "echo"); -} - -struct capture_base_field_tuple test_capture_base_fields[] = { - { "DOESNOTEXIST", FIELD_TYPE_PAYLOAD, false, false, NULL, NULL }, - { "intfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field }, - { "longfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field }, - { "signedfield", FIELD_TYPE_PAYLOAD, true, true, validate_signed_int_field, validate_signed_int_field }, - { "arrfield1", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field }, - { "arrfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test }, - { "arrfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field }, - { "seqfield1", FIELD_TYPE_PAYLOAD, true, true, validate_seqfield1, validate_seqfield1 }, - { "seqfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test }, - { "seqfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field }, - { "seqfield4", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field }, - { "arrfield1[1]", FIELD_TYPE_ARRAY_FIELD, true, true, validate_array_unsigned_int_field_at_index, validate_array_unsigned_int_field_at_index }, - { "stringfield", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test }, - { "stringfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_escaped, validate_string_escaped }, - { "floatfield", FIELD_TYPE_PAYLOAD, true, false, validate_floatfield, validate_floatfield }, - { "doublefield", FIELD_TYPE_PAYLOAD, true, false, validate_doublefield, validate_doublefield }, - { "enum0", FIELD_TYPE_PAYLOAD, true, true, validate_enum0, validate_enum0 }, - { "enumnegative", FIELD_TYPE_PAYLOAD, true, true, validate_enumnegative, validate_enumnegative }, - { "$ctx.procname", FIELD_TYPE_CONTEXT, true, true, validate_context_procname_ust, validate_context_procname_kernel }, -}; - -static const char *get_notification_trigger_name( - struct lttng_notification *notification) -{ - const char *trigger_name = NULL; - enum lttng_trigger_status trigger_status; - const struct lttng_trigger *trigger; - - trigger = lttng_notification_get_trigger(notification); - if (!trigger) { - fail("Failed to get trigger from notification"); - goto end; - } - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - switch (trigger_status) { - case LTTNG_TRIGGER_STATUS_OK: - break; - case LTTNG_TRIGGER_STATUS_UNSET: - trigger_name = "(anonymous)"; - break; - default: - fail("Failed to get name from notification's trigger"); - goto end; - } - -end: - return trigger_name; -} - -static int validator_notification_trigger_name( - struct lttng_notification *notification, - const char *trigger_name) -{ - int ret; - bool name_is_equal; - const char *name; - - LTTNG_ASSERT(notification); - LTTNG_ASSERT(trigger_name); - - name = get_notification_trigger_name(notification); - if (name == NULL) { - ret = 1; - goto end; - } - - name_is_equal = (strcmp(trigger_name, name) == 0); - ok(name_is_equal, "Expected trigger name: %s got %s", trigger_name, - name); - - ret = !name_is_equal; - -end: - return ret; -} - -static -void wait_on_file(const char *path, bool file_exist) -{ - if (!path) { - return; - } - for (;;) { - int ret; - struct stat buf; - - ret = stat(path, &buf); - if (ret == -1 && errno == ENOENT) { - if (file_exist) { - /* - * The file does not exist. wait a bit and - * continue looping until it does. - */ - (void) poll(NULL, 0, 10); - continue; - } - - /* - * File does not exist and the exit condition we want. - * Break from the loop and return. - */ - break; - } - if (ret) { - perror("stat"); - exit(EXIT_FAILURE); - } - /* - * stat() returned 0, so the file exists. break now only if - * that's the exit condition we want. - */ - if (file_exist) { - break; - } - } -} - -static -int write_pipe(const char *path, uint8_t data) -{ - int ret = 0; - int fd = 0; - - fd = open(path, O_WRONLY | O_NONBLOCK); - if (fd < 0) { - perror("Could not open consumer control named pipe"); - goto end; - } - - ret = write(fd, &data , sizeof(data)); - if (ret < 1) { - perror("Named pipe write failed"); - if (close(fd)) { - perror("Named pipe close failed"); - } - ret = -1; - goto end; - } - - ret = close(fd); - if (ret < 0) { - perror("Name pipe closing failed"); - ret = -1; - goto end; - } -end: - return ret; -} - -static -int stop_consumer(const char **argv) -{ - int ret = 0, i; - - for (i = named_pipe_args_start; i < nb_args; i++) { - ret = write_pipe(argv[i], 49); - } - return ret; -} - -static -int resume_consumer(const char **argv) -{ - int ret = 0, i; - - for (i = named_pipe_args_start; i < nb_args; i++) { - ret = write_pipe(argv[i], 0); - } - return ret; -} - -static -int suspend_application(void) -{ - int ret; - struct stat buf; - - if (!stat(app_state_file, &buf)) { - fail("App is already in a suspended state."); - ret = -1; - goto error; - } - - /* - * Send SIGUSR1 to application instructing it to bypass tracepoint. - */ - LTTNG_ASSERT(app_pid > 1); - - ret = kill(app_pid, SIGUSR1); - if (ret) { - fail("SIGUSR1 failed. errno %d", errno); - ret = -1; - goto error; - } - - wait_on_file(app_state_file, true); - -error: - return ret; - -} - -static -int resume_application(void) -{ - int ret; - struct stat buf; - - ret = stat(app_state_file, &buf); - if (ret == -1 && errno == ENOENT) { - fail("State file does not exist"); - goto error; - } - if (ret) { - perror("stat"); - goto error; - } - - LTTNG_ASSERT(app_pid > 1); - - ret = kill(app_pid, SIGUSR1); - if (ret) { - fail("SIGUSR1 failed. errno %d", errno); - ret = -1; - goto error; - } - - wait_on_file(app_state_file, false); - -error: - return ret; - -} - - -static -void test_triggers_buffer_usage_condition(const char *session_name, - const char *channel_name, - enum lttng_domain_type domain_type, - enum lttng_condition_type condition_type) -{ - unsigned int test_vector_size = 5, i; - enum lttng_condition_status condition_status; - struct lttng_action *action; - - /* Set-up */ - action = lttng_action_notify_create(); - if (!action) { - fail("Setup error on action creation"); - goto end; - } - - /* Test lttng_register_trigger with null value */ - ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected"); - - /* Test: register a trigger */ - - for (i = 0; i < pow(2,test_vector_size); i++) { - int loop_ret = 0; - char *test_tuple_string = NULL; - unsigned int mask_position = 0; - bool session_name_set = false; - bool channel_name_set = false; - bool threshold_ratio_set = false; - bool threshold_byte_set = false; - bool domain_type_set = false; - - struct lttng_trigger *trigger = NULL; - struct lttng_condition *condition = NULL; - - /* Create base condition */ - switch (condition_type) { - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - condition = lttng_condition_buffer_usage_low_create(); - break; - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - condition = lttng_condition_buffer_usage_high_create(); - break; - default: - loop_ret = 1; - goto loop_end; - } - - if (!condition) { - loop_ret = 1; - goto loop_end; - - } - - /* Prepare the condition for trigger registration test */ - - /* Set session name */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_session_name( - condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; - } - session_name_set = true; - } - mask_position++; - - /* Set channel name */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_channel_name( - condition, channel_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; - } - channel_name_set = true; - } - mask_position++; - - /* Set threshold ratio */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - condition, 0.0); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; - } - threshold_ratio_set = true; - } - mask_position++; - - /* Set threshold byte */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_threshold( - condition, 0); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; - } - threshold_byte_set = true; - } - mask_position++; - - /* Set domain type */ - if ((1 << mask_position) & i) { - condition_status = lttng_condition_buffer_usage_set_domain_type( - condition, LTTNG_DOMAIN_UST); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - loop_ret = 1; - goto loop_end; - } - domain_type_set = true; - } - - /* Safety check */ - if (mask_position != test_vector_size -1) { - LTTNG_ASSERT("Logic error for test vector generation"); - } - - loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s", - session_name_set ? "set" : "unset", - channel_name_set ? "set" : "unset", - threshold_ratio_set ? "set" : "unset", - threshold_byte_set ? "set" : "unset", - domain_type_set? "set" : "unset"); - if (!test_tuple_string || loop_ret < 0) { - loop_ret = 1; - goto loop_end; - } - - /* Create trigger */ - trigger = lttng_trigger_create(condition, action); - if (!trigger) { - loop_ret = 1; - goto loop_end; - } - - loop_ret = lttng_register_trigger(trigger); - -loop_end: - if (loop_ret == 1) { - fail("Setup error occurred for tuple: %s", test_tuple_string); - goto loop_cleanup; - } - - /* This combination happens three times */ - if (session_name_set && channel_name_set - && (threshold_ratio_set || threshold_byte_set) - && domain_type_set) { - ok(loop_ret == 0, "Trigger is registered: %s", test_tuple_string); - - /* - * Test that a trigger cannot be registered - * multiple time. - */ - loop_ret = lttng_register_trigger(trigger); - ok(loop_ret == -LTTNG_ERR_TRIGGER_EXISTS, "Re-register trigger fails as expected: %s", test_tuple_string); - - /* Test that a trigger can be unregistered */ - loop_ret = lttng_unregister_trigger(trigger); - ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string); - - /* - * Test that unregistration of a non-previously - * registered trigger fail. - */ - loop_ret = lttng_unregister_trigger(trigger); - ok(loop_ret == -LTTNG_ERR_TRIGGER_NOT_FOUND, "Unregister of a non-registered trigger fails as expected: %s", test_tuple_string); - } else { - ok(loop_ret == -LTTNG_ERR_INVALID_TRIGGER, "Trigger is invalid as expected and cannot be registered: %s", test_tuple_string); - } - -loop_cleanup: - free(test_tuple_string); - lttng_trigger_destroy(trigger); - lttng_condition_destroy(condition); - } - -end: - lttng_action_destroy(action); -} - -static -void wait_data_pending(const char *session_name) -{ - int ret; - - do { - ret = lttng_data_pending(session_name); - LTTNG_ASSERT(ret >= 0); - } while (ret != 0); -} - -static -int setup_buffer_usage_condition(struct lttng_condition *condition, - const char *condition_name, - const char *session_name, - const char *channel_name, - const enum lttng_domain_type domain_type) -{ - enum lttng_condition_status condition_status; - int ret = 0; - - condition_status = lttng_condition_buffer_usage_set_session_name( - condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Failed to set session name on creation of condition `%s`", - condition_name); - ret = -1; - goto end; - } - - condition_status = lttng_condition_buffer_usage_set_channel_name( - condition, channel_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Failed to set channel name on creation of condition `%s`", - condition_name); - ret = -1; - goto end; - } - - condition_status = lttng_condition_buffer_usage_set_domain_type( - condition, domain_type); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Failed to set domain type on creation of condition `%s`", - condition_name); - ret = -1; - goto end; - } - -end: - return ret; -} - -static -void test_invalid_channel_subscription( - const enum lttng_domain_type domain_type) -{ - enum lttng_condition_status condition_status; - enum lttng_notification_channel_status nc_status; - struct lttng_condition *dummy_condition = NULL; - struct lttng_condition *dummy_invalid_condition = NULL; - struct lttng_notification_channel *notification_channel = NULL; - int ret = 0; - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - if (!notification_channel) { - goto end; - } - - /* - * Create a dummy, empty (thus invalid) condition to test error paths. - */ - dummy_invalid_condition = lttng_condition_buffer_usage_low_create(); - if (!dummy_invalid_condition) { - fail("Setup error on condition creation"); - goto end; - } - - /* - * Test subscription and unsubscription of an invalid condition to/from - * a channel. - */ - nc_status = lttng_notification_channel_subscribe( - notification_channel, dummy_invalid_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, - "Subscribing to an invalid condition"); - - nc_status = lttng_notification_channel_unsubscribe( - notification_channel, dummy_invalid_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, - "Unsubscribing from an invalid condition"); - - /* Create a valid dummy condition with a ratio of 0.5 */ - dummy_condition = lttng_condition_buffer_usage_low_create(); - if (!dummy_condition) { - fail("Setup error on dummy_condition creation"); - goto end; - } - - condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - dummy_condition, 0.5); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on condition creation"); - goto end; - } - - ret = setup_buffer_usage_condition(dummy_condition, "dummy_condition", - "dummy_session", "dummy_channel", domain_type); - if (ret) { - fail("Setup error on dummy condition creation"); - goto end; - } - - /* - * Test subscription and unsubscription to/from a channel with invalid - * parameters. - */ - nc_status = lttng_notification_channel_subscribe(NULL, NULL); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, - "Notification channel subscription is invalid: NULL, NULL"); - - nc_status = lttng_notification_channel_subscribe( - notification_channel, NULL); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, - "Notification channel subscription is invalid: NON-NULL, NULL"); - - nc_status = lttng_notification_channel_subscribe(NULL, dummy_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, - "Notification channel subscription is invalid: NULL, NON-NULL"); - - nc_status = lttng_notification_channel_unsubscribe( - notification_channel, dummy_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, - "Unsubscribing from a valid unknown condition"); - -end: - lttng_notification_channel_destroy(notification_channel); - lttng_condition_destroy(dummy_invalid_condition); - lttng_condition_destroy(dummy_condition); - return; -} - -enum buffer_usage_type { - BUFFER_USAGE_TYPE_LOW, - BUFFER_USAGE_TYPE_HIGH, -}; - -static int register_buffer_usage_notify_trigger(const char *session_name, - const char *channel_name, - const enum lttng_domain_type domain_type, - enum buffer_usage_type buffer_usage_type, - double ratio, - struct lttng_condition **condition, - struct lttng_action **action, - struct lttng_trigger **trigger) -{ - enum lttng_condition_status condition_status; - struct lttng_action *tmp_action = NULL; - struct lttng_condition *tmp_condition = NULL; - struct lttng_trigger *tmp_trigger = NULL; - int ret = 0; - - /* Set-up */ - tmp_action = lttng_action_notify_create(); - if (!action) { - fail("Setup error on action creation"); - ret = -1; - goto error; - } - - if (buffer_usage_type == BUFFER_USAGE_TYPE_LOW) { - tmp_condition = lttng_condition_buffer_usage_low_create(); - } else { - tmp_condition = lttng_condition_buffer_usage_high_create(); - } - - if (!tmp_condition) { - fail("Setup error on condition creation"); - ret = -1; - goto error; - } - - /* Set the buffer usage threashold */ - condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - tmp_condition, ratio); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on condition creation"); - ret = -1; - goto error; - } - - ret = setup_buffer_usage_condition(tmp_condition, "condition_name", - session_name, channel_name, domain_type); - if (ret) { - fail("Setup error on condition creation"); - ret = -1; - goto error; - } - - /* Register the trigger for condition. */ - tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action); - if (!tmp_trigger) { - fail("Setup error on trigger creation"); - ret = -1; - goto error; - } - - ret = lttng_register_trigger(tmp_trigger); - if (ret) { - fail("Setup error on trigger registration"); - ret = -1; - goto error; - } - - *condition = tmp_condition; - *trigger = tmp_trigger; - *action = tmp_action; - goto end; - -error: - lttng_action_destroy(tmp_action); - lttng_condition_destroy(tmp_condition); - lttng_trigger_destroy(tmp_trigger); - -end: - return ret; -} - -static void test_subscription_twice(const char *session_name, - const char *channel_name, - const enum lttng_domain_type domain_type) -{ - int ret = 0; - enum lttng_notification_channel_status nc_status; - - struct lttng_action *action = NULL; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_trigger *trigger = NULL; - - struct lttng_condition *condition = NULL; - - ret = register_buffer_usage_notify_trigger(session_name, channel_name, - domain_type, BUFFER_USAGE_TYPE_LOW, 0.99, &condition, - &action, &trigger); - if (ret) { - fail("Setup error on trigger registration in %s()", - __FUNCTION__); - goto end; - } - - /* Begin testing. */ - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - if (!notification_channel) { - goto end; - } - - /* Subscribe a valid condition. */ - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to condition"); - - /* Subscribing again should fail. */ - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, - "Subscribe to a condition for which subscription was already done"); - -end: - ret = lttng_unregister_trigger(trigger); - if (ret) { - fail("Failed to unregister trigger in %s()", __FUNCTION__); - } - - lttng_trigger_destroy(trigger); - lttng_notification_channel_destroy(notification_channel); - lttng_action_destroy(action); - lttng_condition_destroy(condition); -} - -static void test_buffer_usage_notification_channel(const char *session_name, - const char *channel_name, - const enum lttng_domain_type domain_type, - const char **argv) -{ - int ret = 0; - enum lttng_notification_channel_status nc_status; - - struct lttng_action *low_action = NULL; - struct lttng_action *high_action = NULL; - struct lttng_notification *notification = NULL; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_trigger *low_trigger = NULL; - struct lttng_trigger *high_trigger = NULL; - - struct lttng_condition *low_condition = NULL; - struct lttng_condition *high_condition = NULL; - - const double low_ratio = 0.0; - const double high_ratio = 0.90; - - ret = register_buffer_usage_notify_trigger(session_name, channel_name, - domain_type, BUFFER_USAGE_TYPE_LOW, low_ratio, - &low_condition, &low_action, &low_trigger); - if (ret) { - fail("Setup error on low trigger registration"); - goto end; - } - - ret = register_buffer_usage_notify_trigger(session_name, channel_name, - domain_type, BUFFER_USAGE_TYPE_HIGH, high_ratio, - &high_condition, &high_action, &high_trigger); - if (ret) { - fail("Setup error on high trigger registration"); - goto end; - } - - /* Begin testing */ - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - if (!notification_channel) { - goto end; - } - - /* Subscribe a valid low condition */ - nc_status = lttng_notification_channel_subscribe( - notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to low condition"); - - /* Subscribe a valid high condition */ - nc_status = lttng_notification_channel_subscribe( - notification_channel, high_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to high condition"); - - resume_application(); - - /* Wait for notification to happen */ - stop_consumer(argv); - lttng_start_tracing(session_name); - - /* Wait for high notification */ - do { - nc_status = lttng_notification_channel_get_next_notification( - notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition( - notification)) == - LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, - "High notification received after intermediary communication"); - lttng_notification_destroy(notification); - notification = NULL; - - suspend_application(); - lttng_stop_tracing_no_wait(session_name); - resume_consumer(argv); - wait_data_pending(session_name); - - /* - * Test that communication still work even if there is notification - * waiting for consumption. - */ - - nc_status = lttng_notification_channel_unsubscribe( - notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Unsubscribe with pending notification"); - - nc_status = lttng_notification_channel_subscribe( - notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe with pending notification"); - - do { - nc_status = lttng_notification_channel_get_next_notification( - notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition( - notification)) == - LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, - "Low notification received after intermediary communication"); - lttng_notification_destroy(notification); - notification = NULL; - - /* Stop consumer to force a high notification */ - stop_consumer(argv); - resume_application(); - lttng_start_tracing(session_name); - - do { - nc_status = lttng_notification_channel_get_next_notification( - notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition( - notification)) == - LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, - "High notification received after intermediary communication"); - lttng_notification_destroy(notification); - notification = NULL; - - suspend_application(); - lttng_stop_tracing_no_wait(session_name); - resume_consumer(argv); - wait_data_pending(session_name); - - do { - nc_status = lttng_notification_channel_get_next_notification( - notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition( - notification)) == - LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, - "Low notification received after re-subscription"); - lttng_notification_destroy(notification); - notification = NULL; - - stop_consumer(argv); - resume_application(); - /* Stop consumer to force a high notification */ - lttng_start_tracing(session_name); - - do { - nc_status = lttng_notification_channel_get_next_notification( - notification_channel, ¬ification); - } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition( - notification)) == - LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, - "High notification"); - lttng_notification_destroy(notification); - notification = NULL; - - suspend_application(); - - /* Resume consumer to allow event consumption */ - lttng_stop_tracing_no_wait(session_name); - resume_consumer(argv); - wait_data_pending(session_name); - - nc_status = lttng_notification_channel_unsubscribe( - notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Unsubscribe low condition with pending notification"); - - nc_status = lttng_notification_channel_unsubscribe( - notification_channel, high_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Unsubscribe high condition with pending notification"); - -end: - lttng_notification_channel_destroy(notification_channel); - lttng_trigger_destroy(low_trigger); - lttng_trigger_destroy(high_trigger); - lttng_action_destroy(low_action); - lttng_action_destroy(high_action); - lttng_condition_destroy(low_condition); - lttng_condition_destroy(high_condition); -} - -static void create_tracepoint_event_rule_trigger(const char *event_pattern, - const char *trigger_name, - const char *filter, - unsigned int exclusion_count, - const char * const *exclusions, - enum lttng_domain_type domain_type, - condition_capture_desc_cb capture_desc_cb, - struct lttng_condition **condition, - struct lttng_trigger **trigger) -{ - typedef struct lttng_event_rule *(*event_rule_create)(void); - typedef enum lttng_event_rule_status ( - *event_rule_set_name_pattern)( - struct lttng_event_rule *rule, - const char *pattern); - typedef enum lttng_event_rule_status (*event_rule_set_filter)( - struct lttng_event_rule *rule, - const char *expression); - typedef enum lttng_event_rule_status ( - *event_rule_add_name_pattern_exclusion)( - struct lttng_event_rule * rule, const char *exclusion); - - enum lttng_event_rule_status event_rule_status; - struct lttng_action *tmp_action = NULL; - struct lttng_event_rule *event_rule = NULL; - struct lttng_condition *tmp_condition = NULL; - struct lttng_trigger *tmp_trigger = NULL; - int ret; - enum lttng_error_code ret_code; - event_rule_create create; - event_rule_set_name_pattern set_name_pattern; - event_rule_set_filter set_filter; - event_rule_add_name_pattern_exclusion add_name_pattern_exclusion; - - LTTNG_ASSERT(event_pattern); - LTTNG_ASSERT(trigger_name); - LTTNG_ASSERT(condition); - LTTNG_ASSERT(trigger); - - /* Set the function pointers based on the domain type. */ - switch (domain_type) { - case LTTNG_DOMAIN_UST: - create = lttng_event_rule_user_tracepoint_create; - set_name_pattern = lttng_event_rule_user_tracepoint_set_name_pattern; - set_filter = lttng_event_rule_user_tracepoint_set_filter; - add_name_pattern_exclusion = lttng_event_rule_user_tracepoint_add_name_pattern_exclusion; - break; - case LTTNG_DOMAIN_KERNEL: - create = lttng_event_rule_kernel_tracepoint_create; - set_name_pattern = lttng_event_rule_kernel_tracepoint_set_name_pattern; - set_filter = lttng_event_rule_kernel_tracepoint_set_filter; - add_name_pattern_exclusion = NULL; - break; - default: - abort(); - break; - } - - event_rule = create(); - ok(event_rule, "Tracepoint event rule object creation"); - - event_rule_status = set_name_pattern(event_rule, event_pattern); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting tracepoint event rule pattern: '%s'", - event_pattern); - - if (filter) { - event_rule_status = set_filter(event_rule, filter); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting tracepoint event rule filter: '%s'", - filter); - } - - if (exclusions) { - int i; - bool success = true; - - LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_UST); - LTTNG_ASSERT(add_name_pattern_exclusion != NULL); - LTTNG_ASSERT(exclusion_count > 0); - - for (i = 0; i < exclusion_count; i++) { - event_rule_status = add_name_pattern_exclusion( - event_rule, exclusions[i]); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - fail("Setting tracepoint event rule exclusion '%s'.", - exclusions[i]); - success = false; - } - } - - ok(success, "Setting tracepoint event rule exclusions"); - } - - tmp_condition = lttng_condition_event_rule_matches_create(event_rule); - ok(tmp_condition, "Condition event rule object creation"); - - if (capture_desc_cb) { - ret = capture_desc_cb(tmp_condition); - if (ret) { - fail("Failed to generate the condition capture descriptor"); - abort(); - } - } - - tmp_action = lttng_action_notify_create(); - ok(tmp_action, "Action event rule object creation"); - - tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action); - ok(tmp_trigger, "Trigger object creation %s", trigger_name); - - ret_code = lttng_register_trigger_with_name(tmp_trigger, trigger_name); - ok(ret_code == LTTNG_OK, "Trigger registration %s", trigger_name); - - lttng_event_rule_destroy(event_rule); - - *condition = tmp_condition; - *trigger = tmp_trigger; - - return; -} - -static struct lttng_notification *get_next_notification( - struct lttng_notification_channel *notification_channel) -{ - struct lttng_notification *local_notification = NULL; - enum lttng_notification_channel_status status; - - /* Receive the next notification. */ - status = lttng_notification_channel_get_next_notification( - notification_channel, &local_notification); - - switch (status) { - case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: - break; - case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: - fail("Notifications have been dropped"); - local_notification = NULL; - break; - default: - /* Unhandled conditions / errors. */ - fail("Failed to get next notification (unknown notification channel status): status = %d", - (int) status); - local_notification = NULL; - break; - } - - return local_notification; -} - -static void test_tracepoint_event_rule_notification( - enum lttng_domain_type domain_type) -{ - int i; - int ret; - const int notification_count = 3; - enum lttng_notification_channel_status nc_status; - struct lttng_action *action = NULL; - struct lttng_condition *condition = NULL; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_trigger *trigger = NULL; - const char * const trigger_name = "my_precious"; - const char *pattern; - - if (domain_type == LTTNG_DOMAIN_UST) { - pattern = "tp:tptest"; - } else { - pattern = "lttng_test_filter_event"; - } - - create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, - NULL, domain_type, NULL, &condition, &trigger); - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - resume_application(); - - /* Get notifications. */ - for (i = 0; i < notification_count; i++) { - struct lttng_notification *notification = get_next_notification( - notification_channel); - - ok(notification, "Received notification (%d/%d)", i + 1, - notification_count); - - /* Error. */ - if (notification == NULL) { - goto end; - } - - ret = validator_notification_trigger_name(notification, trigger_name); - lttng_notification_destroy(notification); - if (ret) { - goto end; - } - } - -end: - suspend_application(); - lttng_notification_channel_destroy(notification_channel); - lttng_unregister_trigger(trigger); - lttng_trigger_destroy(trigger); - lttng_action_destroy(action); - lttng_condition_destroy(condition); - return; -} - -static void test_tracepoint_event_rule_notification_filter( - enum lttng_domain_type domain_type) -{ - int i; - const int notification_count = 3; - enum lttng_notification_channel_status nc_status; - struct lttng_condition *ctrl_condition = NULL, *condition = NULL; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL; - const char * const ctrl_trigger_name = "control_trigger"; - const char * const trigger_name = "trigger"; - const char *pattern; - int ctrl_count = 0, count = 0; - - if (domain_type == LTTNG_DOMAIN_UST) { - pattern = "tp:tptest"; - } else { - pattern = "lttng_test_filter_event"; - } - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - - create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, - 0, NULL, domain_type, NULL, &ctrl_condition, &ctrl_trigger); - - nc_status = lttng_notification_channel_subscribe( - notification_channel, ctrl_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - /* - * Attach a filter expression to get notification only if the - * `intfield` is even. - */ - create_tracepoint_event_rule_trigger(pattern, trigger_name, - "(intfield & 1) == 0", 0, NULL, domain_type, NULL, &condition, - &trigger); - - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - /* - * We registered 2 notifications triggers, one with a filter and one - * without (control). The one with a filter will only fired when the - * `intfield` is a multiple of 2. We should get two times as many - * control notifications as filter notifications. - */ - resume_application(); - - /* - * Get 3 notifications. We should get 1 for the regular trigger (with - * the filter) and 2 from the control trigger. This works whatever - * the order we receive the notifications. - */ - for (i = 0; i < notification_count; i++) { - const char *name; - struct lttng_notification *notification = get_next_notification( - notification_channel); - - ok(notification, "Received notification (%d/%d)", i + 1, - notification_count); - - /* Error. */ - if (notification == NULL) { - goto end; - } - - name = get_notification_trigger_name(notification); - if (name == NULL) { - lttng_notification_destroy(notification); - goto end; - } - - if (strcmp(ctrl_trigger_name, name) == 0) { - ctrl_count++; - } else if (strcmp(trigger_name, name) == 0) { - count++; - } - - lttng_notification_destroy(notification); - } - - ok(ctrl_count / 2 == count, - "Get twice as many control notif as of regular notif"); - -end: - suspend_application(); - - lttng_unregister_trigger(trigger); - lttng_unregister_trigger(ctrl_trigger); - lttng_notification_channel_destroy(notification_channel); - lttng_trigger_destroy(trigger); - lttng_trigger_destroy(ctrl_trigger); - lttng_condition_destroy(condition); - lttng_condition_destroy(ctrl_condition); -} - -static void test_tracepoint_event_rule_notification_exclusion( - enum lttng_domain_type domain_type) -{ - enum lttng_notification_channel_status nc_status; - struct lttng_condition *ctrl_condition = NULL, *condition = NULL; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL; - int ctrl_count = 0, count = 0, i; - const int notification_count = 6; - const char * const ctrl_trigger_name = "control_exclusion_trigger"; - const char * const trigger_name = "exclusion_trigger"; - const char * const pattern = "tp:tptest*"; - const char * const exclusions[] = { - "tp:tptest2", - "tp:tptest3", - "tp:tptest4", - "tp:tptest5" - }; - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - - create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, - 0, NULL, domain_type, NULL, &ctrl_condition, - &ctrl_trigger); - - nc_status = lttng_notification_channel_subscribe( - notification_channel, ctrl_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 4, - exclusions, domain_type, NULL, &condition, - &trigger); - - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - /* - * We registered 2 notifications triggers, one with an exclusion and - * one without (control). - * - The trigger with an exclusion will fire once every iteration. - * - The trigger without an exclusion will fire 5 times every - * iteration. - * - * We should get 5 times as many notifications from the control - * trigger. - */ - resume_application(); - - /* - * Get 6 notifications. We should get 1 for the regular trigger (with - * the exclusion) and 5 from the control trigger. This works whatever - * the order we receive the notifications. - */ - for (i = 0; i < notification_count; i++) { - const char *name; - struct lttng_notification *notification = get_next_notification( - notification_channel); - - ok(notification, "Received notification (%d/%d)", i + 1, - notification_count); - - /* Error. */ - if (notification == NULL) { - goto end; - } - - name = get_notification_trigger_name(notification); - if (name == NULL) { - lttng_notification_destroy(notification); - goto end; - } - - if (strcmp(ctrl_trigger_name, name) == 0) { - ctrl_count++; - } else if (strcmp(trigger_name, name) == 0) { - count++; - } - - lttng_notification_destroy(notification); - } - - ok(ctrl_count / 5 == count, - "Got 5 times as many control notif as of regular notif"); - -end: - suspend_application(); - - lttng_unregister_trigger(trigger); - lttng_unregister_trigger(ctrl_trigger); - lttng_notification_channel_destroy(notification_channel); - lttng_trigger_destroy(trigger); - lttng_trigger_destroy(ctrl_trigger); - lttng_condition_destroy(condition); - lttng_condition_destroy(ctrl_condition); - return; -} - -static void test_kprobe_event_rule_notification( - enum lttng_domain_type domain_type) -{ - int i, ret; - enum lttng_error_code ret_code; - const int notification_count = 3; - enum lttng_notification_channel_status nc_status; - enum lttng_event_rule_status event_rule_status; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_condition *condition = NULL; - struct lttng_kernel_probe_location *location = NULL; - struct lttng_event_rule *event_rule = NULL; - struct lttng_action *action = NULL; - struct lttng_trigger *trigger = NULL; - const char * const trigger_name = "kprobe_trigger"; - const char * const symbol_name = "lttng_test_filter_event_write"; - - action = lttng_action_notify_create(); - if (!action) { - fail("Failed to create notify action"); - goto end; - } - - location = lttng_kernel_probe_location_symbol_create(symbol_name, 0); - if (!location) { - fail("Failed to create kernel probe location"); - goto end; - } - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - - event_rule = lttng_event_rule_kernel_kprobe_create(location); - ok(event_rule, "kprobe event rule object creation"); - - event_rule_status = lttng_event_rule_kernel_kprobe_set_event_name( - event_rule, trigger_name); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting kprobe event rule name: '%s'", trigger_name); - - condition = lttng_condition_event_rule_matches_create(event_rule); - ok(condition, "Condition event rule object creation"); - - /* Register the trigger for condition. */ - trigger = lttng_trigger_create(condition, action); - if (!trigger) { - fail("Failed to create trigger with kernel probe event rule condition and notify action"); - goto end; - } - - ret_code = lttng_register_trigger_with_name(trigger, trigger_name); - if (ret_code != LTTNG_OK) { - fail("Failed to register trigger with kernel probe event rule condition and notify action"); - goto end; - } - - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - resume_application(); - - for (i = 0; i < notification_count; i++) { - struct lttng_notification *notification = get_next_notification( - notification_channel); - - ok(notification, "Received notification (%d/%d)", i + 1, - notification_count); - - /* Error. */ - if (notification == NULL) { - goto end; - } - - ret = validator_notification_trigger_name(notification, trigger_name); - lttng_notification_destroy(notification); - if (ret) { - goto end; - } - } - -end: - suspend_application(); - lttng_notification_channel_destroy(notification_channel); - lttng_unregister_trigger(trigger); - lttng_trigger_destroy(trigger); - lttng_action_destroy(action); - lttng_event_rule_destroy(event_rule); - lttng_condition_destroy(condition); - lttng_kernel_probe_location_destroy(location); - return; -} - -static void test_uprobe_event_rule_notification( - enum lttng_domain_type domain_type, - const char *testapp_path, - const char *test_symbol_name) -{ - int i, ret; - enum lttng_error_code ret_code; - const int notification_count = 3; - enum lttng_notification_channel_status nc_status; - enum lttng_event_rule_status event_rule_status; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_userspace_probe_location *probe_location = NULL; - struct lttng_userspace_probe_location_lookup_method *lookup_method = - NULL; - struct lttng_condition *condition = NULL; - struct lttng_event_rule *event_rule = NULL; - struct lttng_action *action = NULL; - struct lttng_trigger *trigger = NULL; - const char * const trigger_name = "uprobe_trigger"; - - action = lttng_action_notify_create(); - if (!action) { - fail("Failed to create notify action"); - goto end; - } - - lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create(); - if (!lookup_method) { - fail("Setup error on userspace probe lookup method creation"); - goto end; - } - - probe_location = lttng_userspace_probe_location_function_create( - testapp_path, test_symbol_name, lookup_method); - if (!probe_location) { - fail("Failed to create userspace probe location"); - goto end; - } - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - - event_rule = lttng_event_rule_kernel_uprobe_create(probe_location); - ok(event_rule, "kprobe event rule object creation"); - - event_rule_status = lttng_event_rule_kernel_uprobe_set_event_name( - event_rule, trigger_name); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting uprobe event rule name: '%s'", trigger_name); - - condition = lttng_condition_event_rule_matches_create(event_rule); - ok(condition, "Condition event rule object creation"); - - /* Register the trigger for condition. */ - trigger = lttng_trigger_create(condition, action); - if (!trigger) { - fail("Failed to create trigger with userspace probe event rule condition and notify action"); - goto end; - } - - ret_code = lttng_register_trigger_with_name(trigger, trigger_name); - if (ret_code != LTTNG_OK) { - fail("Failed to register trigger with userspace probe event rule condition and notify action"); - goto end; - } - - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - resume_application(); - - for (i = 0; i < 3; i++) { - struct lttng_notification *notification = get_next_notification( - notification_channel); - - ok(notification, "Received notification (%d/%d)", i + 1, - notification_count); - - /* Error. */ - if (notification == NULL) { - goto end; - } - - ret = validator_notification_trigger_name(notification, trigger_name); - lttng_notification_destroy(notification); - if (ret) { - goto end; - } - } -end: - suspend_application(); - - lttng_notification_channel_destroy(notification_channel); - lttng_unregister_trigger(trigger); - lttng_trigger_destroy(trigger); - lttng_action_destroy(action); - lttng_userspace_probe_location_destroy(probe_location); - lttng_event_rule_destroy(event_rule); - lttng_condition_destroy(condition); - return; -} - -static void test_syscall_event_rule_notification( - enum lttng_domain_type domain_type) -{ - int i, ret; - enum lttng_error_code ret_code; - const int notification_count = 3; - enum lttng_notification_channel_status nc_status; - enum lttng_event_rule_status event_rule_status; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_condition *condition = NULL; - struct lttng_event_rule *event_rule = NULL; - struct lttng_action *action = NULL; - struct lttng_trigger *trigger = NULL; - const char * const trigger_name = "syscall_trigger"; - const char * const syscall_name = "openat"; - - action = lttng_action_notify_create(); - if (!action) { - fail("Failed to create notify action"); - goto end; - } - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - - event_rule = lttng_event_rule_kernel_syscall_create(LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY); - ok(event_rule, "syscall event rule object creation"); - - event_rule_status = lttng_event_rule_kernel_syscall_set_name_pattern( - event_rule, syscall_name); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting syscall event rule pattern: '%s'", syscall_name); - - condition = lttng_condition_event_rule_matches_create(event_rule); - ok(condition, "Condition syscall event rule object creation"); - - /* Register the trigger for condition. */ - trigger = lttng_trigger_create(condition, action); - if (!trigger) { - fail("Failed to create trigger with syscall event rule condition and notify action"); - goto end; - } - - ret_code = lttng_register_trigger_with_name(trigger, trigger_name); - if (ret_code != LTTNG_OK) { - fail("Failed to register trigger with syscall event rule condition and notify action"); - goto end; - } - - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - resume_application(); - - for (i = 0; i < notification_count; i++) { - struct lttng_notification *notification = get_next_notification( - notification_channel); - - ok(notification, "Received notification (%d/%d)", i + 1, - notification_count); - - /* Error. */ - if (notification == NULL) { - goto end; - } - - ret = validator_notification_trigger_name(notification, trigger_name); - lttng_notification_destroy(notification); - if (ret) { - goto end; - } - } -end: - suspend_application(); - lttng_notification_channel_destroy(notification_channel); - lttng_unregister_trigger(trigger); - lttng_trigger_destroy(trigger); - lttng_action_destroy(action); - lttng_condition_destroy(condition); - return; -} - -static void test_syscall_event_rule_notification_filter( - enum lttng_domain_type domain_type) -{ - int i, ret; - enum lttng_error_code ret_code; - const int notification_count = 3; - enum lttng_notification_channel_status nc_status; - enum lttng_event_rule_status event_rule_status; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_condition *condition = NULL; - struct lttng_event_rule *event_rule = NULL; - struct lttng_action *action = NULL; - struct lttng_trigger *trigger = NULL; - const char * const trigger_name = "syscall_trigger"; - const char * const syscall_name = "openat"; - const char * const filter_pattern = "filename == \"/proc/cpuinfo\""; - - action = lttng_action_notify_create(); - if (!action) { - fail("Failed to create notify action"); - goto end; - } - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - - event_rule = lttng_event_rule_kernel_syscall_create(LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY); - ok(event_rule, "syscall event rule object creation"); - - event_rule_status = lttng_event_rule_kernel_syscall_set_name_pattern( - event_rule, syscall_name); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting syscall event rule pattern: '%s'", syscall_name); - - event_rule_status = lttng_event_rule_kernel_syscall_set_filter( - event_rule, filter_pattern); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting filter: '%s'", filter_pattern); - - condition = lttng_condition_event_rule_matches_create(event_rule); - ok(condition, "Condition event rule object creation"); - - /* Register the triggers for condition */ - trigger = lttng_trigger_create(condition, action); - if (!trigger) { - fail("Failed to create trigger with syscall filtering event rule condition and notify action"); - goto end; - } - - ret_code = lttng_register_trigger_with_name(trigger, trigger_name); - if (ret_code != LTTNG_OK) { - fail("Failed to register trigger with syscall filtering event rule condition and notify action"); - goto end; - } - - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - resume_application(); - - for (i = 0; i < notification_count; i++) { - struct lttng_notification *notification = get_next_notification( - notification_channel); - - ok(notification, "Received notification (%d/%d)", i + 1, - notification_count); - - /* Error. */ - if (notification == NULL) { - goto end; - } - - ret = validator_notification_trigger_name(notification, trigger_name); - lttng_notification_destroy(notification); - if (ret) { - goto end; - } - } - -end: - suspend_application(); - - lttng_unregister_trigger(trigger); - lttng_notification_channel_destroy(notification_channel); - lttng_trigger_destroy(trigger); - lttng_event_rule_destroy(event_rule); - lttng_condition_destroy(condition); - return; -} - -static int generate_capture_descr(struct lttng_condition *condition) -{ - int ret, i; - struct lttng_event_expr *expr = NULL; - const unsigned int basic_field_count = sizeof(test_capture_base_fields) / - sizeof(*test_capture_base_fields); - enum lttng_condition_status cond_status; - - for (i = 0; i < basic_field_count; i++) { - diag("Adding capture descriptor '%s'", - test_capture_base_fields[i].field_name); - - switch (test_capture_base_fields[i].field_type) { - case FIELD_TYPE_PAYLOAD: - expr = lttng_event_expr_event_payload_field_create( - test_capture_base_fields[i].field_name); - break; - case FIELD_TYPE_CONTEXT: - expr = lttng_event_expr_channel_context_field_create( - test_capture_base_fields[i].field_name); - break; - case FIELD_TYPE_ARRAY_FIELD: - { - int nb_matches; - unsigned int index; - char field_name[FIELD_NAME_MAX_LEN]; - struct lttng_event_expr *array_expr = NULL; - - nb_matches = sscanf(test_capture_base_fields[i].field_name, - "%[^[][%u]", field_name, &index); - if (nb_matches != 2) { - fail("Unexpected array field name format: field name = '%s'", - test_capture_base_fields[i].field_name); - ret = 1; - goto end; - } - - array_expr = lttng_event_expr_event_payload_field_create( - field_name); - - expr = lttng_event_expr_array_field_element_create( - array_expr, index); - break; - } - case FIELD_TYPE_APP_CONTEXT: - fail("Application context tests are not implemented yet."); - /* fallthrough. */ - default: - ret = 1; - goto end; - } - - if (expr == NULL) { - fail("Failed to create capture expression"); - ret = -1; - goto end; - } - - cond_status = lttng_condition_event_rule_matches_append_capture_descriptor( - condition, expr); - if (cond_status != LTTNG_CONDITION_STATUS_OK) { - fail("Failed to append capture descriptor"); - ret = -1; - lttng_event_expr_destroy(expr); - goto end; - } - } - - ret = 0; - -end: - return ret; -} - -static int validator_notification_trigger_capture( - enum lttng_domain_type domain, - struct lttng_notification *notification, - const int iteration) -{ - int ret; - unsigned int capture_count, i; - enum lttng_evaluation_event_rule_matches_status - event_rule_matches_evaluation_status; - enum lttng_event_field_value_status event_field_value_status; - const struct lttng_evaluation *evaluation; - const struct lttng_event_field_value *captured_fields; - bool at_least_one_error = false; - - evaluation = lttng_notification_get_evaluation(notification); - if (evaluation == NULL) { - fail("Failed to get evaluation from notification during trigger capture test"); - ret = 1; - goto end; - } - - event_rule_matches_evaluation_status = - lttng_evaluation_event_rule_matches_get_captured_values( - evaluation, &captured_fields); - if (event_rule_matches_evaluation_status != - LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_OK) { - diag("Failed to get event rule evaluation captured values: status = %d", - (int) event_rule_matches_evaluation_status); - ret = 1; - goto end; - } - - event_field_value_status = - lttng_event_field_value_array_get_length(captured_fields, - &capture_count); - if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - fail("Failed to get count of captured value field array"); - ret = 1; - goto end; - } - - for (i = 0; i < capture_count; i++) { - const struct lttng_event_field_value *captured_field = NULL; - validate_cb validate; - bool expected; - - diag("Validating capture of field '%s'", - test_capture_base_fields[i].field_name); - event_field_value_status = - lttng_event_field_value_array_get_element_at_index( - captured_fields, i, - &captured_field); - - switch(domain) { - case LTTNG_DOMAIN_UST: - expected = test_capture_base_fields[i].expected_ust; - break; - case LTTNG_DOMAIN_KERNEL: - expected = test_capture_base_fields[i].expected_kernel; - break; - default: - fail("Unexpected domain encountered: domain = %d", - (int) domain); - ret = 1; - goto end; - } - - if (domain == LTTNG_DOMAIN_UST) { - validate = test_capture_base_fields[i].validate_ust; - } else { - validate = test_capture_base_fields[i].validate_kernel; - } - - if (!expected) { - ok(event_field_value_status == LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE, - "No payload captured"); - continue; - } - - if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { - if (event_field_value_status == - LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) { - fail("Expected a capture but it is unavailable"); - } else { - fail("lttng_event_field_value_array_get_element_at_index returned an error: status = %d", - (int) event_field_value_status); - } - - ret = 1; - goto end; - } - - diag("Captured field of type %s", - field_value_type_to_str( - lttng_event_field_value_get_type(captured_field))); - - LTTNG_ASSERT(validate); - ret = validate(captured_field, iteration); - if (ret) { - at_least_one_error = true; - } - } - - ret = at_least_one_error; - -end: - return ret; -} - -static void test_tracepoint_event_rule_notification_capture( - enum lttng_domain_type domain_type) -{ - enum lttng_notification_channel_status nc_status; - - int i, ret; - struct lttng_condition *condition = NULL; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_trigger *trigger = NULL; - const char *trigger_name = "my_precious"; - const char *pattern; - - if (domain_type == LTTNG_DOMAIN_UST) { - pattern = "tp:tptest"; - } else { - pattern = "lttng_test_filter_event"; - } - - create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, - NULL, domain_type, generate_capture_descr, &condition, - &trigger); - - notification_channel = lttng_notification_channel_create( - lttng_session_daemon_notification_endpoint); - ok(notification_channel, "Notification channel object creation"); - - nc_status = lttng_notification_channel_subscribe( - notification_channel, condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, - "Subscribe to tracepoint event rule condition"); - - resume_application(); - - /* Get 3 notifications */ - for (i = 0; i < 3; i++) { - struct lttng_notification *notification = get_next_notification( - notification_channel); - ok(notification, "Received notification"); - - /* Error */ - if (notification == NULL) { - goto end; - } - - ret = validator_notification_trigger_name(notification, trigger_name); - if (ret) { - lttng_notification_destroy(notification); - goto end; - } - - ret = validator_notification_trigger_capture(domain_type, notification, i); - if (ret) { - lttng_notification_destroy(notification); - goto end; - } - - lttng_notification_destroy(notification); - } - -end: - suspend_application(); - lttng_notification_channel_destroy(notification_channel); - lttng_unregister_trigger(trigger); - lttng_trigger_destroy(trigger); - lttng_condition_destroy(condition); - return; -} - -int main(int argc, const char *argv[]) -{ - int test_scenario; - const char *domain_type_string = NULL; - enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; - - if (argc < 5) { - fail("Missing test scenario, domain type, pid, or application state file argument(s)"); - goto error; - } - - test_scenario = atoi(argv[1]); - domain_type_string = argv[2]; - app_pid = (pid_t) atoi(argv[3]); - app_state_file = argv[4]; - - if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) { - domain_type = LTTNG_DOMAIN_UST; - } - if (!strcmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) { - domain_type = LTTNG_DOMAIN_KERNEL; - } - if (domain_type == LTTNG_DOMAIN_NONE) { - fail("Unknown domain type"); - goto error; - } - - /* - * Test cases are responsible for resuming the app when needed - * and making sure it's suspended when returning. - */ - suspend_application(); - - switch (test_scenario) { - case 1: - { - plan_tests(41); - - /* Test cases that need gen-ust-event testapp. */ - diag("Test basic notification error paths for %s domain", - domain_type_string); - test_invalid_channel_subscription(domain_type); - - diag("Test tracepoint event rule notifications for domain %s", - domain_type_string); - test_tracepoint_event_rule_notification(domain_type); - - diag("Test tracepoint event rule notifications with filter for domain %s", - domain_type_string); - test_tracepoint_event_rule_notification_filter(domain_type); - break; - } - case 2: - { - const char *session_name, *channel_name; - - /* Test cases that need a tracing session enabled. */ - plan_tests(99); - - /* - * Argument 7 and upward are named pipe location for consumerd - * control. - */ - named_pipe_args_start = 7; - - if (argc < 8) { - fail("Missing parameter for tests to run %d", argc); - goto error; - } - - nb_args = argc; - - session_name = argv[5]; - channel_name = argv[6]; - - test_subscription_twice(session_name, channel_name, - domain_type); - - diag("Test trigger for domain %s with buffer_usage_low condition", - domain_type_string); - test_triggers_buffer_usage_condition(session_name, channel_name, - domain_type, - LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); - - diag("Test trigger for domain %s with buffer_usage_high condition", - domain_type_string); - test_triggers_buffer_usage_condition(session_name, channel_name, - domain_type, - LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); - - diag("Test buffer usage notification channel api for domain %s", - domain_type_string); - test_buffer_usage_notification_channel(session_name, channel_name, - domain_type, argv); - break; - } - case 3: - { - /* - * Test cases that need a test app with more than one event - * type. - */ - plan_tests(23); - - /* - * At the moment, the only test case of this scenario is - * exclusion which is only supported by UST. - */ - LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_UST); - diag("Test tracepoint event rule notifications with exclusion for domain %s", - domain_type_string); - test_tracepoint_event_rule_notification_exclusion(domain_type); - - break; - } - case 4: - { - plan_tests(11); - /* Test cases that need the kernel tracer. */ - LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_KERNEL); - - diag("Test kprobe event rule notifications for domain %s", - domain_type_string); - - test_kprobe_event_rule_notification(domain_type); - - break; - } - case 5: - { - plan_tests(23); - /* Test cases that need the kernel tracer. */ - LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_KERNEL); - - diag("Test syscall event rule notifications for domain %s", - domain_type_string); - - test_syscall_event_rule_notification(domain_type); - - diag("Test syscall filtering event rule notifications for domain %s", - domain_type_string); - - test_syscall_event_rule_notification_filter(domain_type); - - break; - } - case 6: - { - const char *testapp_path, *test_symbol_name; - - plan_tests(11); - - if (argc < 7) { - fail("Missing parameter for tests to run %d", argc); - goto error; - } - - testapp_path = argv[5]; - test_symbol_name = argv[6]; - /* Test cases that need the kernel tracer. */ - LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_KERNEL); - - diag("Test userspace-probe event rule notifications for domain %s", - domain_type_string); - - test_uprobe_event_rule_notification( - domain_type, testapp_path, test_symbol_name); - - break; - } - case 7: - { - switch(domain_type) { - case LTTNG_DOMAIN_UST: - plan_tests(221); - break; - case LTTNG_DOMAIN_KERNEL: - plan_tests(215); - break; - default: - abort(); - } - - diag("Test tracepoint event rule notification captures for domain %s", - domain_type_string); - test_tracepoint_event_rule_notification_capture(domain_type); - - break; - } - - default: - abort(); - } - -error: - return exit_status(); -} - diff --git a/tests/regression/tools/notification/notification.cpp b/tests/regression/tools/notification/notification.cpp new file mode 100644 index 000000000..7a0b5dc91 --- /dev/null +++ b/tests/regression/tools/notification/notification.cpp @@ -0,0 +1,2689 @@ +/* + * notification.c + * + * Tests suite for LTTng notification API + * + * Copyright (C) 2017 Jonathan Rajotte + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define FIELD_NAME_MAX_LEN 256 + +/* A callback to populate the condition capture descriptor. */ +typedef int (*condition_capture_desc_cb)(struct lttng_condition *condition); + +/* A callback for captured field validation. */ +typedef int (*validate_cb)(const struct lttng_event_field_value *event_field, unsigned iteration); + +int nb_args = 0; +int named_pipe_args_start = 0; +pid_t app_pid = 0; +const char *app_state_file = NULL; + +enum field_type { + FIELD_TYPE_PAYLOAD, + FIELD_TYPE_CONTEXT, + FIELD_TYPE_APP_CONTEXT, + FIELD_TYPE_ARRAY_FIELD, +}; + +struct capture_base_field_tuple { + const char *field_name; + enum field_type field_type; + /* Do we expect a userspace capture? */ + bool expected_ust; + /* Do we expect a kernel capture? */ + bool expected_kernel; + validate_cb validate_ust; + validate_cb validate_kernel; +}; + +static +const char *field_value_type_to_str(enum lttng_event_field_value_type type) +{ + switch (type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN: + return "UNKNOWN"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID: + return "INVALID"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: + return "UNSIGNED INT"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: + return "SIGNED INT"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + return "UNSIGNED ENUM"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + return "SIGNED ENUM"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL: + return "REAL"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: + return "STRING"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: + return "ARRAY"; + default: + abort(); + } +} + +static int validate_type(const struct lttng_event_field_value *event_field, + enum lttng_event_field_value_type expect) +{ + int ret; + enum lttng_event_field_value_type value; + + value = lttng_event_field_value_get_type(event_field); + if (value == LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID) { + ret = 1; + goto end; + } + + ok(expect == value, "Expected field type %s, got %s", + field_value_type_to_str(expect), + field_value_type_to_str(value)); + + ret = expect != value; + +end: + return ret; +} + +/* + * Validate unsigned captured field against the iteration number. + */ +static int validate_unsigned_int_field( + const struct lttng_event_field_value *event_field, + unsigned int expected_value) +{ + int ret; + uint64_t value; + enum lttng_event_field_value_status status; + + ret = validate_type( + event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value returned an error: status = %d", + (int) status); + ret = 1; + goto end; + } + + ok(value == (uint64_t) expected_value, + "Expected unsigned integer value %u, got %" PRIu64, + expected_value, value); + + ret = value != (uint64_t) expected_value; + +end: + return ret; +} + +/* + * Validate signed captured field. + */ +static int validate_signed_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + const int64_t expected = -1; + int64_t value; + enum lttng_event_field_value_status status; + + /* Unused. */ + (void) iteration; + + ret = validate_type( + event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_signed_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_signed_int_get_value returned an error: status = %d", + (int) status); + ret = 1; + goto end; + } + + ok(value == expected, + "Expected signed integer value %" PRId64 + ", got %" PRId64, + expected, value); + + ret = value != expected; + +end: + + return ret; +} + +/* + * Validate array of unsigned int. + */ +static int validate_array_unsigned_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + const unsigned int expected = 3; + unsigned int i, count; + + /* Unused. */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); + if (ret) { + goto end; + } + + status = lttng_event_field_value_array_get_length(event_field, &count); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; + } + + ok(count == expected, "Expected %d subelements, got %d", expected, + count); + if (count != expected) { + ret = 1; + goto end; + } + + for (i = 1; i < count + 1; i++) { + const struct lttng_event_field_value *value; + + status = lttng_event_field_value_array_get_element_at_index( + event_field, i - 1, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_element_at_index returned an error: status = %d", + (int) status); + ret = 1; + goto end; + } + + ret = validate_unsigned_int_field(value, i); + if (ret) { + goto end; + } + } + + ret = 0; +end: + + return ret; +} + +static int validate_array_unsigned_int_field_at_index( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + const uint64_t expected_value = 2; + enum lttng_event_field_value_status status; + uint64_t value; + + /* Unused. */ + (void) iteration; + + ret = validate_type( + event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value returned an error: status = %d", + (int) status); + ret = 1; + goto end; + } + + ok(value == expected_value, + "Expected unsigned integer value %u, got %" PRIu64, + expected_value, value); + + ret = 0; +end: + return ret; +} + +/* + * Validate sequence for a string (seqfield1): + * + * Value: "test" encoded in UTF-8: [116, 101, 115, 116] + */ +static int validate_seqfield1(const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + unsigned int i, count; + const unsigned int expect[] = {116, 101, 115, 116}; + const size_t array_count = sizeof(expect) / sizeof(*expect); + + /* Unused. */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); + if (ret) { + goto end; + } + + status = lttng_event_field_value_array_get_length(event_field, &count); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_length returned an error: status = %d", + (int) status); + ret = 1; + goto end; + } + + ok(count == array_count, "Expected %zu array sub-elements, got %d", + array_count, count); + if (count != array_count) { + ret = 1; + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_event_field_value *value; + + status = lttng_event_field_value_array_get_element_at_index( + event_field, i, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_element_at_index returned an error: status = %d", + (int) status); + ret = 1; + goto end; + } + + ret = validate_unsigned_int_field(value, expect[i]); + if (ret) { + goto end; + } + } + + ret = 0; +end: + return ret; +} + +static int validate_string( + const struct lttng_event_field_value *event_field, + const char *expect) +{ + int ret; + const char *value = NULL; + enum lttng_event_field_value_status status; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_STRING); + if (ret) { + goto end; + } + + status = lttng_event_field_value_string_get_value(event_field, &value); + if (!value) { + fail("lttng_event_field_value_array_get_length returned an error: status = %d", + (int) status); + ret = 1; + goto end; + } + + ok(!strcmp(value, expect), "Expected string value \"%s\", got \"%s\"", + expect, value); + + ret = 0; +end: + + return ret; +} + +/* + * Validate string. Expected value is "test". + */ +static int validate_string_test( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + const char * const expect = "test"; + + /* Unused. */ + (void) iteration; + + return validate_string(event_field, expect); +} + +/* + * Validate escaped string. Expected value is "\*". + */ +static int validate_string_escaped( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + const char * const expect = "\\*"; + + /* Unused. */ + (void) iteration; + + return validate_string(event_field, expect); +} + +/* + * Validate real field. + */ +static int validate_real( + const struct lttng_event_field_value *event_field, + double expect) +{ + int ret; + double value; + enum lttng_event_field_value_status status; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_REAL); + if (ret) { + goto end; + } + + status = lttng_event_field_value_real_get_value(event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_real_get_value returned an error: status = %d", + (int) status); + ret = 1; + goto end; + } + + ok(value == expect, "Expected real value %f, got %f", expect, value); + ret = value != expect; +end: + return ret; +} + +/* + * Validate floatfield. + */ +static int validate_floatfield( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + const double expect = 2222.0; + + /* Unused. */ + (void) iteration; + + return validate_real(event_field, expect); +} + +/* + * Validate doublefield. + */ +static int validate_doublefield( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + const double expect = 2.0; + + /* Unused. */ + (void) iteration; + + return validate_real(event_field, expect); +} + +/* + * Validate enum0: enum0 = ( "AUTO: EXPECT 0" : container = 0 ) + */ +static int validate_enum0(const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + uint64_t value; + const uint64_t expected_value = 0; + + /* Unused. */ + (void) iteration; + + ret = validate_type(event_field, + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value returned an error: status = %d", + (int) status); + ret = 1; + goto end; + } + + ok(value == expected_value, + "Expected enum value %" PRIu64 ", got %" PRIu64, + expected_value, value); + +end: + return ret; +} + +/* + * Validate enumnegative: enumnegative = ( "AUTO: EXPECT 0" : container = 0 ) + * + * We expect 2 labels here. + */ +static int validate_enumnegative( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + int64_t value; + const int64_t expected_value = -1; + + /* Unused. */ + (void) iteration; + + ret = validate_type(event_field, + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM); + if (ret) { + goto end; + } + + status = lttng_event_field_value_signed_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ok(value == expected_value, + "Expected enum value %" PRId64 ", got %" PRId64, + expected_value, value); + +end: + return ret; +} + +static int validate_context_procname_ust( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + /* Unused. */ + (void) iteration; + return validate_string(event_field, "gen-ust-events"); +} + +static int validate_context_procname_kernel( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + /* Unused. */ + (void) iteration; + return validate_string(event_field, "echo"); +} + +struct capture_base_field_tuple test_capture_base_fields[] = { + { "DOESNOTEXIST", FIELD_TYPE_PAYLOAD, false, false, NULL, NULL }, + { "intfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field }, + { "longfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field }, + { "signedfield", FIELD_TYPE_PAYLOAD, true, true, validate_signed_int_field, validate_signed_int_field }, + { "arrfield1", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field }, + { "arrfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test }, + { "arrfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field }, + { "seqfield1", FIELD_TYPE_PAYLOAD, true, true, validate_seqfield1, validate_seqfield1 }, + { "seqfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test }, + { "seqfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field }, + { "seqfield4", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field }, + { "arrfield1[1]", FIELD_TYPE_ARRAY_FIELD, true, true, validate_array_unsigned_int_field_at_index, validate_array_unsigned_int_field_at_index }, + { "stringfield", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test }, + { "stringfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_escaped, validate_string_escaped }, + { "floatfield", FIELD_TYPE_PAYLOAD, true, false, validate_floatfield, validate_floatfield }, + { "doublefield", FIELD_TYPE_PAYLOAD, true, false, validate_doublefield, validate_doublefield }, + { "enum0", FIELD_TYPE_PAYLOAD, true, true, validate_enum0, validate_enum0 }, + { "enumnegative", FIELD_TYPE_PAYLOAD, true, true, validate_enumnegative, validate_enumnegative }, + { "$ctx.procname", FIELD_TYPE_CONTEXT, true, true, validate_context_procname_ust, validate_context_procname_kernel }, +}; + +static const char *get_notification_trigger_name( + struct lttng_notification *notification) +{ + const char *trigger_name = NULL; + enum lttng_trigger_status trigger_status; + const struct lttng_trigger *trigger; + + trigger = lttng_notification_get_trigger(notification); + if (!trigger) { + fail("Failed to get trigger from notification"); + goto end; + } + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + switch (trigger_status) { + case LTTNG_TRIGGER_STATUS_OK: + break; + case LTTNG_TRIGGER_STATUS_UNSET: + trigger_name = "(anonymous)"; + break; + default: + fail("Failed to get name from notification's trigger"); + goto end; + } + +end: + return trigger_name; +} + +static int validator_notification_trigger_name( + struct lttng_notification *notification, + const char *trigger_name) +{ + int ret; + bool name_is_equal; + const char *name; + + LTTNG_ASSERT(notification); + LTTNG_ASSERT(trigger_name); + + name = get_notification_trigger_name(notification); + if (name == NULL) { + ret = 1; + goto end; + } + + name_is_equal = (strcmp(trigger_name, name) == 0); + ok(name_is_equal, "Expected trigger name: %s got %s", trigger_name, + name); + + ret = !name_is_equal; + +end: + return ret; +} + +static +void wait_on_file(const char *path, bool file_exist) +{ + if (!path) { + return; + } + for (;;) { + int ret; + struct stat buf; + + ret = stat(path, &buf); + if (ret == -1 && errno == ENOENT) { + if (file_exist) { + /* + * The file does not exist. wait a bit and + * continue looping until it does. + */ + (void) poll(NULL, 0, 10); + continue; + } + + /* + * File does not exist and the exit condition we want. + * Break from the loop and return. + */ + break; + } + if (ret) { + perror("stat"); + exit(EXIT_FAILURE); + } + /* + * stat() returned 0, so the file exists. break now only if + * that's the exit condition we want. + */ + if (file_exist) { + break; + } + } +} + +static +int write_pipe(const char *path, uint8_t data) +{ + int ret = 0; + int fd = 0; + + fd = open(path, O_WRONLY | O_NONBLOCK); + if (fd < 0) { + perror("Could not open consumer control named pipe"); + goto end; + } + + ret = write(fd, &data , sizeof(data)); + if (ret < 1) { + perror("Named pipe write failed"); + if (close(fd)) { + perror("Named pipe close failed"); + } + ret = -1; + goto end; + } + + ret = close(fd); + if (ret < 0) { + perror("Name pipe closing failed"); + ret = -1; + goto end; + } +end: + return ret; +} + +static +int stop_consumer(const char **argv) +{ + int ret = 0, i; + + for (i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 49); + } + return ret; +} + +static +int resume_consumer(const char **argv) +{ + int ret = 0, i; + + for (i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 0); + } + return ret; +} + +static +int suspend_application(void) +{ + int ret; + struct stat buf; + + if (!stat(app_state_file, &buf)) { + fail("App is already in a suspended state."); + ret = -1; + goto error; + } + + /* + * Send SIGUSR1 to application instructing it to bypass tracepoint. + */ + LTTNG_ASSERT(app_pid > 1); + + ret = kill(app_pid, SIGUSR1); + if (ret) { + fail("SIGUSR1 failed. errno %d", errno); + ret = -1; + goto error; + } + + wait_on_file(app_state_file, true); + +error: + return ret; + +} + +static +int resume_application(void) +{ + int ret; + struct stat buf; + + ret = stat(app_state_file, &buf); + if (ret == -1 && errno == ENOENT) { + fail("State file does not exist"); + goto error; + } + if (ret) { + perror("stat"); + goto error; + } + + LTTNG_ASSERT(app_pid > 1); + + ret = kill(app_pid, SIGUSR1); + if (ret) { + fail("SIGUSR1 failed. errno %d", errno); + ret = -1; + goto error; + } + + wait_on_file(app_state_file, false); + +error: + return ret; + +} + + +static +void test_triggers_buffer_usage_condition(const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + enum lttng_condition_type condition_type) +{ + unsigned int test_vector_size = 5, i; + enum lttng_condition_status condition_status; + struct lttng_action *action; + + /* Set-up */ + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + /* Test lttng_register_trigger with null value */ + ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected"); + + /* Test: register a trigger */ + + for (i = 0; i < pow(2,test_vector_size); i++) { + int loop_ret = 0; + char *test_tuple_string = NULL; + unsigned int mask_position = 0; + bool session_name_set = false; + bool channel_name_set = false; + bool threshold_ratio_set = false; + bool threshold_byte_set = false; + bool domain_type_set = false; + + struct lttng_trigger *trigger = NULL; + struct lttng_condition *condition = NULL; + + /* Create base condition */ + switch (condition_type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + condition = lttng_condition_buffer_usage_low_create(); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + condition = lttng_condition_buffer_usage_high_create(); + break; + default: + loop_ret = 1; + goto loop_end; + } + + if (!condition) { + loop_ret = 1; + goto loop_end; + + } + + /* Prepare the condition for trigger registration test */ + + /* Set session name */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_session_name( + condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + session_name_set = true; + } + mask_position++; + + /* Set channel name */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_channel_name( + condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + channel_name_set = true; + } + mask_position++; + + /* Set threshold ratio */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + condition, 0.0); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + threshold_ratio_set = true; + } + mask_position++; + + /* Set threshold byte */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_threshold( + condition, 0); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + threshold_byte_set = true; + } + mask_position++; + + /* Set domain type */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_domain_type( + condition, LTTNG_DOMAIN_UST); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + domain_type_set = true; + } + + /* Safety check */ + if (mask_position != test_vector_size -1) { + LTTNG_ASSERT("Logic error for test vector generation"); + } + + loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s", + session_name_set ? "set" : "unset", + channel_name_set ? "set" : "unset", + threshold_ratio_set ? "set" : "unset", + threshold_byte_set ? "set" : "unset", + domain_type_set? "set" : "unset"); + if (!test_tuple_string || loop_ret < 0) { + loop_ret = 1; + goto loop_end; + } + + /* Create trigger */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + loop_ret = 1; + goto loop_end; + } + + loop_ret = lttng_register_trigger(trigger); + +loop_end: + if (loop_ret == 1) { + fail("Setup error occurred for tuple: %s", test_tuple_string); + goto loop_cleanup; + } + + /* This combination happens three times */ + if (session_name_set && channel_name_set + && (threshold_ratio_set || threshold_byte_set) + && domain_type_set) { + ok(loop_ret == 0, "Trigger is registered: %s", test_tuple_string); + + /* + * Test that a trigger cannot be registered + * multiple time. + */ + loop_ret = lttng_register_trigger(trigger); + ok(loop_ret == -LTTNG_ERR_TRIGGER_EXISTS, "Re-register trigger fails as expected: %s", test_tuple_string); + + /* Test that a trigger can be unregistered */ + loop_ret = lttng_unregister_trigger(trigger); + ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string); + + /* + * Test that unregistration of a non-previously + * registered trigger fail. + */ + loop_ret = lttng_unregister_trigger(trigger); + ok(loop_ret == -LTTNG_ERR_TRIGGER_NOT_FOUND, "Unregister of a non-registered trigger fails as expected: %s", test_tuple_string); + } else { + ok(loop_ret == -LTTNG_ERR_INVALID_TRIGGER, "Trigger is invalid as expected and cannot be registered: %s", test_tuple_string); + } + +loop_cleanup: + free(test_tuple_string); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + } + +end: + lttng_action_destroy(action); +} + +static +void wait_data_pending(const char *session_name) +{ + int ret; + + do { + ret = lttng_data_pending(session_name); + LTTNG_ASSERT(ret >= 0); + } while (ret != 0); +} + +static +int setup_buffer_usage_condition(struct lttng_condition *condition, + const char *condition_name, + const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type) +{ + enum lttng_condition_status condition_status; + int ret = 0; + + condition_status = lttng_condition_buffer_usage_set_session_name( + condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Failed to set session name on creation of condition `%s`", + condition_name); + ret = -1; + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_channel_name( + condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Failed to set channel name on creation of condition `%s`", + condition_name); + ret = -1; + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_domain_type( + condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Failed to set domain type on creation of condition `%s`", + condition_name); + ret = -1; + goto end; + } + +end: + return ret; +} + +static +void test_invalid_channel_subscription( + const enum lttng_domain_type domain_type) +{ + enum lttng_condition_status condition_status; + enum lttng_notification_channel_status nc_status; + struct lttng_condition *dummy_condition = NULL; + struct lttng_condition *dummy_invalid_condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + int ret = 0; + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { + goto end; + } + + /* + * Create a dummy, empty (thus invalid) condition to test error paths. + */ + dummy_invalid_condition = lttng_condition_buffer_usage_low_create(); + if (!dummy_invalid_condition) { + fail("Setup error on condition creation"); + goto end; + } + + /* + * Test subscription and unsubscription of an invalid condition to/from + * a channel. + */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Subscribing to an invalid condition"); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Unsubscribing from an invalid condition"); + + /* Create a valid dummy condition with a ratio of 0.5 */ + dummy_condition = lttng_condition_buffer_usage_low_create(); + if (!dummy_condition) { + fail("Setup error on dummy_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + dummy_condition, 0.5); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on condition creation"); + goto end; + } + + ret = setup_buffer_usage_condition(dummy_condition, "dummy_condition", + "dummy_session", "dummy_channel", domain_type); + if (ret) { + fail("Setup error on dummy condition creation"); + goto end; + } + + /* + * Test subscription and unsubscription to/from a channel with invalid + * parameters. + */ + nc_status = lttng_notification_channel_subscribe(NULL, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Notification channel subscription is invalid: NULL, NULL"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Notification channel subscription is invalid: NON-NULL, NULL"); + + nc_status = lttng_notification_channel_subscribe(NULL, dummy_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Notification channel subscription is invalid: NULL, NON-NULL"); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, dummy_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, + "Unsubscribing from a valid unknown condition"); + +end: + lttng_notification_channel_destroy(notification_channel); + lttng_condition_destroy(dummy_invalid_condition); + lttng_condition_destroy(dummy_condition); + return; +} + +enum buffer_usage_type { + BUFFER_USAGE_TYPE_LOW, + BUFFER_USAGE_TYPE_HIGH, +}; + +static int register_buffer_usage_notify_trigger(const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type, + enum buffer_usage_type buffer_usage_type, + double ratio, + struct lttng_condition **condition, + struct lttng_action **action, + struct lttng_trigger **trigger) +{ + enum lttng_condition_status condition_status; + struct lttng_action *tmp_action = NULL; + struct lttng_condition *tmp_condition = NULL; + struct lttng_trigger *tmp_trigger = NULL; + int ret = 0; + + /* Set-up */ + tmp_action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + ret = -1; + goto error; + } + + if (buffer_usage_type == BUFFER_USAGE_TYPE_LOW) { + tmp_condition = lttng_condition_buffer_usage_low_create(); + } else { + tmp_condition = lttng_condition_buffer_usage_high_create(); + } + + if (!tmp_condition) { + fail("Setup error on condition creation"); + ret = -1; + goto error; + } + + /* Set the buffer usage threashold */ + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + tmp_condition, ratio); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on condition creation"); + ret = -1; + goto error; + } + + ret = setup_buffer_usage_condition(tmp_condition, "condition_name", + session_name, channel_name, domain_type); + if (ret) { + fail("Setup error on condition creation"); + ret = -1; + goto error; + } + + /* Register the trigger for condition. */ + tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action); + if (!tmp_trigger) { + fail("Setup error on trigger creation"); + ret = -1; + goto error; + } + + ret = lttng_register_trigger(tmp_trigger); + if (ret) { + fail("Setup error on trigger registration"); + ret = -1; + goto error; + } + + *condition = tmp_condition; + *trigger = tmp_trigger; + *action = tmp_action; + goto end; + +error: + lttng_action_destroy(tmp_action); + lttng_condition_destroy(tmp_condition); + lttng_trigger_destroy(tmp_trigger); + +end: + return ret; +} + +static void test_subscription_twice(const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type) +{ + int ret = 0; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *action = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + + struct lttng_condition *condition = NULL; + + ret = register_buffer_usage_notify_trigger(session_name, channel_name, + domain_type, BUFFER_USAGE_TYPE_LOW, 0.99, &condition, + &action, &trigger); + if (ret) { + fail("Setup error on trigger registration in %s()", + __FUNCTION__); + goto end; + } + + /* Begin testing. */ + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { + goto end; + } + + /* Subscribe a valid condition. */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to condition"); + + /* Subscribing again should fail. */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, + "Subscribe to a condition for which subscription was already done"); + +end: + ret = lttng_unregister_trigger(trigger); + if (ret) { + fail("Failed to unregister trigger in %s()", __FUNCTION__); + } + + lttng_trigger_destroy(trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_action_destroy(action); + lttng_condition_destroy(condition); +} + +static void test_buffer_usage_notification_channel(const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type, + const char **argv) +{ + int ret = 0; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *low_action = NULL; + struct lttng_action *high_action = NULL; + struct lttng_notification *notification = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *low_trigger = NULL; + struct lttng_trigger *high_trigger = NULL; + + struct lttng_condition *low_condition = NULL; + struct lttng_condition *high_condition = NULL; + + const double low_ratio = 0.0; + const double high_ratio = 0.90; + + ret = register_buffer_usage_notify_trigger(session_name, channel_name, + domain_type, BUFFER_USAGE_TYPE_LOW, low_ratio, + &low_condition, &low_action, &low_trigger); + if (ret) { + fail("Setup error on low trigger registration"); + goto end; + } + + ret = register_buffer_usage_notify_trigger(session_name, channel_name, + domain_type, BUFFER_USAGE_TYPE_HIGH, high_ratio, + &high_condition, &high_action, &high_trigger); + if (ret) { + fail("Setup error on high trigger registration"); + goto end; + } + + /* Begin testing */ + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { + goto end; + } + + /* Subscribe a valid low condition */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to low condition"); + + /* Subscribe a valid high condition */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to high condition"); + + resume_application(); + + /* Wait for notification to happen */ + stop_consumer(argv); + lttng_start_tracing(session_name); + + /* Wait for high notification */ + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + /* + * Test that communication still work even if there is notification + * waiting for consumption. + */ + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Unsubscribe with pending notification"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe with pending notification"); + + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + "Low notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + /* Stop consumer to force a high notification */ + stop_consumer(argv); + resume_application(); + lttng_start_tracing(session_name); + + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + "Low notification received after re-subscription"); + lttng_notification_destroy(notification); + notification = NULL; + + stop_consumer(argv); + resume_application(); + /* Stop consumer to force a high notification */ + lttng_start_tracing(session_name); + + do { + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + + /* Resume consumer to allow event consumption */ + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Unsubscribe low condition with pending notification"); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Unsubscribe high condition with pending notification"); + +end: + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(low_trigger); + lttng_trigger_destroy(high_trigger); + lttng_action_destroy(low_action); + lttng_action_destroy(high_action); + lttng_condition_destroy(low_condition); + lttng_condition_destroy(high_condition); +} + +static void create_tracepoint_event_rule_trigger(const char *event_pattern, + const char *trigger_name, + const char *filter, + unsigned int exclusion_count, + const char * const *exclusions, + enum lttng_domain_type domain_type, + condition_capture_desc_cb capture_desc_cb, + struct lttng_condition **condition, + struct lttng_trigger **trigger) +{ + typedef struct lttng_event_rule *(*event_rule_create)(void); + typedef enum lttng_event_rule_status ( + *event_rule_set_name_pattern)( + struct lttng_event_rule *rule, + const char *pattern); + typedef enum lttng_event_rule_status (*event_rule_set_filter)( + struct lttng_event_rule *rule, + const char *expression); + typedef enum lttng_event_rule_status ( + *event_rule_add_name_pattern_exclusion)( + struct lttng_event_rule * rule, const char *exclusion); + + enum lttng_event_rule_status event_rule_status; + struct lttng_action *tmp_action = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_condition *tmp_condition = NULL; + struct lttng_trigger *tmp_trigger = NULL; + int ret; + enum lttng_error_code ret_code; + event_rule_create create; + event_rule_set_name_pattern set_name_pattern; + event_rule_set_filter set_filter; + event_rule_add_name_pattern_exclusion add_name_pattern_exclusion; + + LTTNG_ASSERT(event_pattern); + LTTNG_ASSERT(trigger_name); + LTTNG_ASSERT(condition); + LTTNG_ASSERT(trigger); + + /* Set the function pointers based on the domain type. */ + switch (domain_type) { + case LTTNG_DOMAIN_UST: + create = lttng_event_rule_user_tracepoint_create; + set_name_pattern = lttng_event_rule_user_tracepoint_set_name_pattern; + set_filter = lttng_event_rule_user_tracepoint_set_filter; + add_name_pattern_exclusion = lttng_event_rule_user_tracepoint_add_name_pattern_exclusion; + break; + case LTTNG_DOMAIN_KERNEL: + create = lttng_event_rule_kernel_tracepoint_create; + set_name_pattern = lttng_event_rule_kernel_tracepoint_set_name_pattern; + set_filter = lttng_event_rule_kernel_tracepoint_set_filter; + add_name_pattern_exclusion = NULL; + break; + default: + abort(); + break; + } + + event_rule = create(); + ok(event_rule, "Tracepoint event rule object creation"); + + event_rule_status = set_name_pattern(event_rule, event_pattern); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting tracepoint event rule pattern: '%s'", + event_pattern); + + if (filter) { + event_rule_status = set_filter(event_rule, filter); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting tracepoint event rule filter: '%s'", + filter); + } + + if (exclusions) { + int i; + bool success = true; + + LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_UST); + LTTNG_ASSERT(add_name_pattern_exclusion != NULL); + LTTNG_ASSERT(exclusion_count > 0); + + for (i = 0; i < exclusion_count; i++) { + event_rule_status = add_name_pattern_exclusion( + event_rule, exclusions[i]); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fail("Setting tracepoint event rule exclusion '%s'.", + exclusions[i]); + success = false; + } + } + + ok(success, "Setting tracepoint event rule exclusions"); + } + + tmp_condition = lttng_condition_event_rule_matches_create(event_rule); + ok(tmp_condition, "Condition event rule object creation"); + + if (capture_desc_cb) { + ret = capture_desc_cb(tmp_condition); + if (ret) { + fail("Failed to generate the condition capture descriptor"); + abort(); + } + } + + tmp_action = lttng_action_notify_create(); + ok(tmp_action, "Action event rule object creation"); + + tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action); + ok(tmp_trigger, "Trigger object creation %s", trigger_name); + + ret_code = lttng_register_trigger_with_name(tmp_trigger, trigger_name); + ok(ret_code == LTTNG_OK, "Trigger registration %s", trigger_name); + + lttng_event_rule_destroy(event_rule); + + *condition = tmp_condition; + *trigger = tmp_trigger; + + return; +} + +static struct lttng_notification *get_next_notification( + struct lttng_notification_channel *notification_channel) +{ + struct lttng_notification *local_notification = NULL; + enum lttng_notification_channel_status status; + + /* Receive the next notification. */ + status = lttng_notification_channel_get_next_notification( + notification_channel, &local_notification); + + switch (status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + fail("Notifications have been dropped"); + local_notification = NULL; + break; + default: + /* Unhandled conditions / errors. */ + fail("Failed to get next notification (unknown notification channel status): status = %d", + (int) status); + local_notification = NULL; + break; + } + + return local_notification; +} + +static void test_tracepoint_event_rule_notification( + enum lttng_domain_type domain_type) +{ + int i; + int ret; + const int notification_count = 3; + enum lttng_notification_channel_status nc_status; + struct lttng_action *action = NULL; + struct lttng_condition *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + const char * const trigger_name = "my_precious"; + const char *pattern; + + if (domain_type == LTTNG_DOMAIN_UST) { + pattern = "tp:tptest"; + } else { + pattern = "lttng_test_filter_event"; + } + + create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, + NULL, domain_type, NULL, &condition, &trigger); + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + /* Get notifications. */ + for (i = 0; i < notification_count; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + + ok(notification, "Received notification (%d/%d)", i + 1, + notification_count); + + /* Error. */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } + +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(condition); + return; +} + +static void test_tracepoint_event_rule_notification_filter( + enum lttng_domain_type domain_type) +{ + int i; + const int notification_count = 3; + enum lttng_notification_channel_status nc_status; + struct lttng_condition *ctrl_condition = NULL, *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL; + const char * const ctrl_trigger_name = "control_trigger"; + const char * const trigger_name = "trigger"; + const char *pattern; + int ctrl_count = 0, count = 0; + + if (domain_type == LTTNG_DOMAIN_UST) { + pattern = "tp:tptest"; + } else { + pattern = "lttng_test_filter_event"; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, + 0, NULL, domain_type, NULL, &ctrl_condition, &ctrl_trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, ctrl_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + /* + * Attach a filter expression to get notification only if the + * `intfield` is even. + */ + create_tracepoint_event_rule_trigger(pattern, trigger_name, + "(intfield & 1) == 0", 0, NULL, domain_type, NULL, &condition, + &trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + /* + * We registered 2 notifications triggers, one with a filter and one + * without (control). The one with a filter will only fired when the + * `intfield` is a multiple of 2. We should get two times as many + * control notifications as filter notifications. + */ + resume_application(); + + /* + * Get 3 notifications. We should get 1 for the regular trigger (with + * the filter) and 2 from the control trigger. This works whatever + * the order we receive the notifications. + */ + for (i = 0; i < notification_count; i++) { + const char *name; + struct lttng_notification *notification = get_next_notification( + notification_channel); + + ok(notification, "Received notification (%d/%d)", i + 1, + notification_count); + + /* Error. */ + if (notification == NULL) { + goto end; + } + + name = get_notification_trigger_name(notification); + if (name == NULL) { + lttng_notification_destroy(notification); + goto end; + } + + if (strcmp(ctrl_trigger_name, name) == 0) { + ctrl_count++; + } else if (strcmp(trigger_name, name) == 0) { + count++; + } + + lttng_notification_destroy(notification); + } + + ok(ctrl_count / 2 == count, + "Get twice as many control notif as of regular notif"); + +end: + suspend_application(); + + lttng_unregister_trigger(trigger); + lttng_unregister_trigger(ctrl_trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_trigger_destroy(ctrl_trigger); + lttng_condition_destroy(condition); + lttng_condition_destroy(ctrl_condition); +} + +static void test_tracepoint_event_rule_notification_exclusion( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + struct lttng_condition *ctrl_condition = NULL, *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL; + int ctrl_count = 0, count = 0, i; + const int notification_count = 6; + const char * const ctrl_trigger_name = "control_exclusion_trigger"; + const char * const trigger_name = "exclusion_trigger"; + const char * const pattern = "tp:tptest*"; + const char * const exclusions[] = { + "tp:tptest2", + "tp:tptest3", + "tp:tptest4", + "tp:tptest5" + }; + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, + 0, NULL, domain_type, NULL, &ctrl_condition, + &ctrl_trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, ctrl_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 4, + exclusions, domain_type, NULL, &condition, + &trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + /* + * We registered 2 notifications triggers, one with an exclusion and + * one without (control). + * - The trigger with an exclusion will fire once every iteration. + * - The trigger without an exclusion will fire 5 times every + * iteration. + * + * We should get 5 times as many notifications from the control + * trigger. + */ + resume_application(); + + /* + * Get 6 notifications. We should get 1 for the regular trigger (with + * the exclusion) and 5 from the control trigger. This works whatever + * the order we receive the notifications. + */ + for (i = 0; i < notification_count; i++) { + const char *name; + struct lttng_notification *notification = get_next_notification( + notification_channel); + + ok(notification, "Received notification (%d/%d)", i + 1, + notification_count); + + /* Error. */ + if (notification == NULL) { + goto end; + } + + name = get_notification_trigger_name(notification); + if (name == NULL) { + lttng_notification_destroy(notification); + goto end; + } + + if (strcmp(ctrl_trigger_name, name) == 0) { + ctrl_count++; + } else if (strcmp(trigger_name, name) == 0) { + count++; + } + + lttng_notification_destroy(notification); + } + + ok(ctrl_count / 5 == count, + "Got 5 times as many control notif as of regular notif"); + +end: + suspend_application(); + + lttng_unregister_trigger(trigger); + lttng_unregister_trigger(ctrl_trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_trigger_destroy(ctrl_trigger); + lttng_condition_destroy(condition); + lttng_condition_destroy(ctrl_condition); + return; +} + +static void test_kprobe_event_rule_notification( + enum lttng_domain_type domain_type) +{ + int i, ret; + enum lttng_error_code ret_code; + const int notification_count = 3; + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_kernel_probe_location *location = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char * const trigger_name = "kprobe_trigger"; + const char * const symbol_name = "lttng_test_filter_event_write"; + + action = lttng_action_notify_create(); + if (!action) { + fail("Failed to create notify action"); + goto end; + } + + location = lttng_kernel_probe_location_symbol_create(symbol_name, 0); + if (!location) { + fail("Failed to create kernel probe location"); + goto end; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_kernel_kprobe_create(location); + ok(event_rule, "kprobe event rule object creation"); + + event_rule_status = lttng_event_rule_kernel_kprobe_set_event_name( + event_rule, trigger_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting kprobe event rule name: '%s'", trigger_name); + + condition = lttng_condition_event_rule_matches_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the trigger for condition. */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Failed to create trigger with kernel probe event rule condition and notify action"); + goto end; + } + + ret_code = lttng_register_trigger_with_name(trigger, trigger_name); + if (ret_code != LTTNG_OK) { + fail("Failed to register trigger with kernel probe event rule condition and notify action"); + goto end; + } + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < notification_count; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + + ok(notification, "Received notification (%d/%d)", i + 1, + notification_count); + + /* Error. */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } + +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_event_rule_destroy(event_rule); + lttng_condition_destroy(condition); + lttng_kernel_probe_location_destroy(location); + return; +} + +static void test_uprobe_event_rule_notification( + enum lttng_domain_type domain_type, + const char *testapp_path, + const char *test_symbol_name) +{ + int i, ret; + enum lttng_error_code ret_code; + const int notification_count = 3; + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_userspace_probe_location *probe_location = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = + NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char * const trigger_name = "uprobe_trigger"; + + action = lttng_action_notify_create(); + if (!action) { + fail("Failed to create notify action"); + goto end; + } + + lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!lookup_method) { + fail("Setup error on userspace probe lookup method creation"); + goto end; + } + + probe_location = lttng_userspace_probe_location_function_create( + testapp_path, test_symbol_name, lookup_method); + if (!probe_location) { + fail("Failed to create userspace probe location"); + goto end; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_kernel_uprobe_create(probe_location); + ok(event_rule, "kprobe event rule object creation"); + + event_rule_status = lttng_event_rule_kernel_uprobe_set_event_name( + event_rule, trigger_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting uprobe event rule name: '%s'", trigger_name); + + condition = lttng_condition_event_rule_matches_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the trigger for condition. */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Failed to create trigger with userspace probe event rule condition and notify action"); + goto end; + } + + ret_code = lttng_register_trigger_with_name(trigger, trigger_name); + if (ret_code != LTTNG_OK) { + fail("Failed to register trigger with userspace probe event rule condition and notify action"); + goto end; + } + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < 3; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + + ok(notification, "Received notification (%d/%d)", i + 1, + notification_count); + + /* Error. */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } +end: + suspend_application(); + + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_userspace_probe_location_destroy(probe_location); + lttng_event_rule_destroy(event_rule); + lttng_condition_destroy(condition); + return; +} + +static void test_syscall_event_rule_notification( + enum lttng_domain_type domain_type) +{ + int i, ret; + enum lttng_error_code ret_code; + const int notification_count = 3; + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char * const trigger_name = "syscall_trigger"; + const char * const syscall_name = "openat"; + + action = lttng_action_notify_create(); + if (!action) { + fail("Failed to create notify action"); + goto end; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_kernel_syscall_create(LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY); + ok(event_rule, "syscall event rule object creation"); + + event_rule_status = lttng_event_rule_kernel_syscall_set_name_pattern( + event_rule, syscall_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting syscall event rule pattern: '%s'", syscall_name); + + condition = lttng_condition_event_rule_matches_create(event_rule); + ok(condition, "Condition syscall event rule object creation"); + + /* Register the trigger for condition. */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Failed to create trigger with syscall event rule condition and notify action"); + goto end; + } + + ret_code = lttng_register_trigger_with_name(trigger, trigger_name); + if (ret_code != LTTNG_OK) { + fail("Failed to register trigger with syscall event rule condition and notify action"); + goto end; + } + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < notification_count; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + + ok(notification, "Received notification (%d/%d)", i + 1, + notification_count); + + /* Error. */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(condition); + return; +} + +static void test_syscall_event_rule_notification_filter( + enum lttng_domain_type domain_type) +{ + int i, ret; + enum lttng_error_code ret_code; + const int notification_count = 3; + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char * const trigger_name = "syscall_trigger"; + const char * const syscall_name = "openat"; + const char * const filter_pattern = "filename == \"/proc/cpuinfo\""; + + action = lttng_action_notify_create(); + if (!action) { + fail("Failed to create notify action"); + goto end; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_kernel_syscall_create(LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY); + ok(event_rule, "syscall event rule object creation"); + + event_rule_status = lttng_event_rule_kernel_syscall_set_name_pattern( + event_rule, syscall_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting syscall event rule pattern: '%s'", syscall_name); + + event_rule_status = lttng_event_rule_kernel_syscall_set_filter( + event_rule, filter_pattern); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting filter: '%s'", filter_pattern); + + condition = lttng_condition_event_rule_matches_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the triggers for condition */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Failed to create trigger with syscall filtering event rule condition and notify action"); + goto end; + } + + ret_code = lttng_register_trigger_with_name(trigger, trigger_name); + if (ret_code != LTTNG_OK) { + fail("Failed to register trigger with syscall filtering event rule condition and notify action"); + goto end; + } + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < notification_count; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + + ok(notification, "Received notification (%d/%d)", i + 1, + notification_count); + + /* Error. */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } + +end: + suspend_application(); + + lttng_unregister_trigger(trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_event_rule_destroy(event_rule); + lttng_condition_destroy(condition); + return; +} + +static int generate_capture_descr(struct lttng_condition *condition) +{ + int ret, i; + struct lttng_event_expr *expr = NULL; + const unsigned int basic_field_count = sizeof(test_capture_base_fields) / + sizeof(*test_capture_base_fields); + enum lttng_condition_status cond_status; + + for (i = 0; i < basic_field_count; i++) { + diag("Adding capture descriptor '%s'", + test_capture_base_fields[i].field_name); + + switch (test_capture_base_fields[i].field_type) { + case FIELD_TYPE_PAYLOAD: + expr = lttng_event_expr_event_payload_field_create( + test_capture_base_fields[i].field_name); + break; + case FIELD_TYPE_CONTEXT: + expr = lttng_event_expr_channel_context_field_create( + test_capture_base_fields[i].field_name); + break; + case FIELD_TYPE_ARRAY_FIELD: + { + int nb_matches; + unsigned int index; + char field_name[FIELD_NAME_MAX_LEN]; + struct lttng_event_expr *array_expr = NULL; + + nb_matches = sscanf(test_capture_base_fields[i].field_name, + "%[^[][%u]", field_name, &index); + if (nb_matches != 2) { + fail("Unexpected array field name format: field name = '%s'", + test_capture_base_fields[i].field_name); + ret = 1; + goto end; + } + + array_expr = lttng_event_expr_event_payload_field_create( + field_name); + + expr = lttng_event_expr_array_field_element_create( + array_expr, index); + break; + } + case FIELD_TYPE_APP_CONTEXT: + fail("Application context tests are not implemented yet."); + /* fallthrough. */ + default: + ret = 1; + goto end; + } + + if (expr == NULL) { + fail("Failed to create capture expression"); + ret = -1; + goto end; + } + + cond_status = lttng_condition_event_rule_matches_append_capture_descriptor( + condition, expr); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + fail("Failed to append capture descriptor"); + ret = -1; + lttng_event_expr_destroy(expr); + goto end; + } + } + + ret = 0; + +end: + return ret; +} + +static int validator_notification_trigger_capture( + enum lttng_domain_type domain, + struct lttng_notification *notification, + const int iteration) +{ + int ret; + unsigned int capture_count, i; + enum lttng_evaluation_event_rule_matches_status + event_rule_matches_evaluation_status; + enum lttng_event_field_value_status event_field_value_status; + const struct lttng_evaluation *evaluation; + const struct lttng_event_field_value *captured_fields; + bool at_least_one_error = false; + + evaluation = lttng_notification_get_evaluation(notification); + if (evaluation == NULL) { + fail("Failed to get evaluation from notification during trigger capture test"); + ret = 1; + goto end; + } + + event_rule_matches_evaluation_status = + lttng_evaluation_event_rule_matches_get_captured_values( + evaluation, &captured_fields); + if (event_rule_matches_evaluation_status != + LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_OK) { + diag("Failed to get event rule evaluation captured values: status = %d", + (int) event_rule_matches_evaluation_status); + ret = 1; + goto end; + } + + event_field_value_status = + lttng_event_field_value_array_get_length(captured_fields, + &capture_count); + if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("Failed to get count of captured value field array"); + ret = 1; + goto end; + } + + for (i = 0; i < capture_count; i++) { + const struct lttng_event_field_value *captured_field = NULL; + validate_cb validate; + bool expected; + + diag("Validating capture of field '%s'", + test_capture_base_fields[i].field_name); + event_field_value_status = + lttng_event_field_value_array_get_element_at_index( + captured_fields, i, + &captured_field); + + switch(domain) { + case LTTNG_DOMAIN_UST: + expected = test_capture_base_fields[i].expected_ust; + break; + case LTTNG_DOMAIN_KERNEL: + expected = test_capture_base_fields[i].expected_kernel; + break; + default: + fail("Unexpected domain encountered: domain = %d", + (int) domain); + ret = 1; + goto end; + } + + if (domain == LTTNG_DOMAIN_UST) { + validate = test_capture_base_fields[i].validate_ust; + } else { + validate = test_capture_base_fields[i].validate_kernel; + } + + if (!expected) { + ok(event_field_value_status == LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE, + "No payload captured"); + continue; + } + + if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + if (event_field_value_status == + LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) { + fail("Expected a capture but it is unavailable"); + } else { + fail("lttng_event_field_value_array_get_element_at_index returned an error: status = %d", + (int) event_field_value_status); + } + + ret = 1; + goto end; + } + + diag("Captured field of type %s", + field_value_type_to_str( + lttng_event_field_value_get_type(captured_field))); + + LTTNG_ASSERT(validate); + ret = validate(captured_field, iteration); + if (ret) { + at_least_one_error = true; + } + } + + ret = at_least_one_error; + +end: + return ret; +} + +static void test_tracepoint_event_rule_notification_capture( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + + int i, ret; + struct lttng_condition *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "my_precious"; + const char *pattern; + + if (domain_type == LTTNG_DOMAIN_UST) { + pattern = "tp:tptest"; + } else { + pattern = "lttng_test_filter_event"; + } + + create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, + NULL, domain_type, generate_capture_descr, &condition, + &trigger); + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + /* Get 3 notifications */ + for (i = 0; i < 3; i++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + if (ret) { + lttng_notification_destroy(notification); + goto end; + } + + ret = validator_notification_trigger_capture(domain_type, notification, i); + if (ret) { + lttng_notification_destroy(notification); + goto end; + } + + lttng_notification_destroy(notification); + } + +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + return; +} + +int main(int argc, const char *argv[]) +{ + int test_scenario; + const char *domain_type_string = NULL; + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + + if (argc < 5) { + fail("Missing test scenario, domain type, pid, or application state file argument(s)"); + goto error; + } + + test_scenario = atoi(argv[1]); + domain_type_string = argv[2]; + app_pid = (pid_t) atoi(argv[3]); + app_state_file = argv[4]; + + if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) { + domain_type = LTTNG_DOMAIN_UST; + } + if (!strcmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) { + domain_type = LTTNG_DOMAIN_KERNEL; + } + if (domain_type == LTTNG_DOMAIN_NONE) { + fail("Unknown domain type"); + goto error; + } + + /* + * Test cases are responsible for resuming the app when needed + * and making sure it's suspended when returning. + */ + suspend_application(); + + switch (test_scenario) { + case 1: + { + plan_tests(41); + + /* Test cases that need gen-ust-event testapp. */ + diag("Test basic notification error paths for %s domain", + domain_type_string); + test_invalid_channel_subscription(domain_type); + + diag("Test tracepoint event rule notifications for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification(domain_type); + + diag("Test tracepoint event rule notifications with filter for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_filter(domain_type); + break; + } + case 2: + { + const char *session_name, *channel_name; + + /* Test cases that need a tracing session enabled. */ + plan_tests(99); + + /* + * Argument 7 and upward are named pipe location for consumerd + * control. + */ + named_pipe_args_start = 7; + + if (argc < 8) { + fail("Missing parameter for tests to run %d", argc); + goto error; + } + + nb_args = argc; + + session_name = argv[5]; + channel_name = argv[6]; + + test_subscription_twice(session_name, channel_name, + domain_type); + + diag("Test trigger for domain %s with buffer_usage_low condition", + domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, + domain_type, + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); + + diag("Test trigger for domain %s with buffer_usage_high condition", + domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, + domain_type, + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); + + diag("Test buffer usage notification channel api for domain %s", + domain_type_string); + test_buffer_usage_notification_channel(session_name, channel_name, + domain_type, argv); + break; + } + case 3: + { + /* + * Test cases that need a test app with more than one event + * type. + */ + plan_tests(23); + + /* + * At the moment, the only test case of this scenario is + * exclusion which is only supported by UST. + */ + LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_UST); + diag("Test tracepoint event rule notifications with exclusion for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_exclusion(domain_type); + + break; + } + case 4: + { + plan_tests(11); + /* Test cases that need the kernel tracer. */ + LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_KERNEL); + + diag("Test kprobe event rule notifications for domain %s", + domain_type_string); + + test_kprobe_event_rule_notification(domain_type); + + break; + } + case 5: + { + plan_tests(23); + /* Test cases that need the kernel tracer. */ + LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_KERNEL); + + diag("Test syscall event rule notifications for domain %s", + domain_type_string); + + test_syscall_event_rule_notification(domain_type); + + diag("Test syscall filtering event rule notifications for domain %s", + domain_type_string); + + test_syscall_event_rule_notification_filter(domain_type); + + break; + } + case 6: + { + const char *testapp_path, *test_symbol_name; + + plan_tests(11); + + if (argc < 7) { + fail("Missing parameter for tests to run %d", argc); + goto error; + } + + testapp_path = argv[5]; + test_symbol_name = argv[6]; + /* Test cases that need the kernel tracer. */ + LTTNG_ASSERT(domain_type == LTTNG_DOMAIN_KERNEL); + + diag("Test userspace-probe event rule notifications for domain %s", + domain_type_string); + + test_uprobe_event_rule_notification( + domain_type, testapp_path, test_symbol_name); + + break; + } + case 7: + { + switch(domain_type) { + case LTTNG_DOMAIN_UST: + plan_tests(221); + break; + case LTTNG_DOMAIN_KERNEL: + plan_tests(215); + break; + default: + abort(); + } + + diag("Test tracepoint event rule notification captures for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_capture(domain_type); + + break; + } + + default: + abort(); + } + +error: + return exit_status(); +} + diff --git a/tests/regression/tools/trigger/hidden/Makefile.am b/tests/regression/tools/trigger/hidden/Makefile.am index ef383fde9..205331c92 100644 --- a/tests/regression/tools/trigger/hidden/Makefile.am +++ b/tests/regression/tools/trigger/hidden/Makefile.am @@ -6,8 +6,7 @@ LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la noinst_PROGRAMS = hidden_trigger -hidden_trigger_CFLAGS = $(AM_CFLAGS) -hidden_trigger_SOURCES = hidden_trigger.c +hidden_trigger_SOURCES = hidden_trigger.cpp hidden_trigger_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) noinst_SCRIPTS = test_hidden_trigger diff --git a/tests/regression/tools/trigger/hidden/hidden_trigger.c b/tests/regression/tools/trigger/hidden/hidden_trigger.c deleted file mode 100644 index 8edd97ab2..000000000 --- a/tests/regression/tools/trigger/hidden/hidden_trigger.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * trigger_name.c - * - * Test that hidden triggers are not visible to liblttng-ctl. - * - * Copyright (C) 2021 Jérémie Galarneau - * - * SPDX-License-Identifier: MIT - * - */ - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#define TEST_COUNT 1 - -#define TEST_SESSION_NAME "test_session" -#define TEST_CHANNEL_NAME "test_channel" - -static -int get_registered_triggers_count(void) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_trigger_status trigger_status; - struct lttng_triggers *triggers = NULL; - unsigned int trigger_count; - - ret_code = lttng_list_triggers(&triggers); - if (ret_code != LTTNG_OK) { - fail("Failed to list triggers"); - ret = -1; - goto end; - } - - trigger_status = lttng_triggers_get_count(triggers, &trigger_count); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - fail("Failed to get count of triggers returned by listing"); - ret = -1; - goto end; - } - - ret = (int) trigger_count; - -end: - lttng_triggers_destroy(triggers); - return ret; -} - -static -int setup_session_with_size_rotation_schedule(const char *session_output_path) -{ - int ret; - struct lttng_session_descriptor *session_desriptor = NULL; - enum lttng_error_code ret_code; - struct lttng_handle ust_channel_handle = { - .session_name = TEST_SESSION_NAME, - .domain.type = LTTNG_DOMAIN_UST, - .domain.buf_type = LTTNG_BUFFER_PER_UID, - }; - struct lttng_channel channel_cfg = { - .name = TEST_CHANNEL_NAME, - .enabled = 1, - .attr.overwrite = -1, - .attr.subbuf_size = sysconf(_SC_PAGE_SIZE) * 8, - .attr.num_subbuf = 8, - .attr.output = LTTNG_EVENT_MMAP, - }; - enum lttng_rotation_status rotation_status; - struct lttng_rotation_schedule *rotation_schedule = NULL; - - session_desriptor = lttng_session_descriptor_local_create( - TEST_SESSION_NAME, session_output_path); - if (!session_desriptor) { - fail("Failed to create session descriptor for session `%s`", - TEST_SESSION_NAME); - ret = -1; - goto end; - } - - ret_code = lttng_create_session_ext(session_desriptor); - if (ret_code != LTTNG_OK) { - fail("Failed to create session `%s`: %s", TEST_SESSION_NAME, - lttng_strerror(-ret_code)); - ret = -1; - goto end; - } - - ret = lttng_enable_channel(&ust_channel_handle, &channel_cfg); - if (ret) { - fail("Failed to enable channel `%s`: %s", TEST_CHANNEL_NAME, - lttng_strerror(ret)); - ret = -1; - goto end; - } - - ret = lttng_start_tracing(TEST_SESSION_NAME); - if (ret) { - fail("Failed to start session `%s`: %s", TEST_SESSION_NAME, - lttng_strerror(ret)); - ret = -1; - goto end; - } - - rotation_schedule = lttng_rotation_schedule_size_threshold_create(); - if (!rotation_schedule) { - fail("Failed to create rotation schedule descriptor"); - ret = -1; - goto end; - } - - /* - * The rotation schedule size threshold doesn't matter; no event rules - * were specified so the session consumed size should not grow over - * time. - */ - rotation_status = lttng_rotation_schedule_size_threshold_set_threshold( - rotation_schedule, sysconf(_SC_PAGE_SIZE) * 4096); - if (rotation_status != LTTNG_ROTATION_STATUS_OK) { - fail("Failed to set size threshold of session rotation schedule"); - ret = -1; - goto end; - } - - rotation_status = lttng_session_add_rotation_schedule( - TEST_SESSION_NAME, rotation_schedule); - if (rotation_status != LTTNG_ROTATION_STATUS_OK) { - fail("Failed to set size-based rotation schedule on session `%s`", - TEST_SESSION_NAME); - ret = -1; - goto end; - } - - ret = 0; -end: - lttng_session_descriptor_destroy(session_desriptor); - lttng_rotation_schedule_destroy(rotation_schedule); - return ret; -} - -int main(int argc, const char **argv) -{ - int ret; - - if (argc != 2) { - fail("Missing trace path"); - goto end; - } - - plan_tests(TEST_COUNT); - - if (get_registered_triggers_count() != 0) { - fail("Session daemon already has registered triggers, bailing out"); - goto end; - } - - ret = setup_session_with_size_rotation_schedule(argv[1]); - if (ret) { - goto end; - } - - ok(get_registered_triggers_count() == 0, - "No triggers visible while session has an enabled size-based rotation schedule"); - - ret = lttng_destroy_session(TEST_SESSION_NAME); - if (ret) { - fail("Failed to destroy session `%s`", TEST_SESSION_NAME); - goto end; - } -end: - return exit_status(); -} diff --git a/tests/regression/tools/trigger/hidden/hidden_trigger.cpp b/tests/regression/tools/trigger/hidden/hidden_trigger.cpp new file mode 100644 index 000000000..668a60fb3 --- /dev/null +++ b/tests/regression/tools/trigger/hidden/hidden_trigger.cpp @@ -0,0 +1,183 @@ +/* + * trigger_name.c + * + * Test that hidden triggers are not visible to liblttng-ctl. + * + * Copyright (C) 2021 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define TEST_COUNT 1 + +#define TEST_SESSION_NAME "test_session" +#define TEST_CHANNEL_NAME "test_channel" + +static +int get_registered_triggers_count(void) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_trigger_status trigger_status; + struct lttng_triggers *triggers = NULL; + unsigned int trigger_count; + + ret_code = lttng_list_triggers(&triggers); + if (ret_code != LTTNG_OK) { + fail("Failed to list triggers"); + ret = -1; + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &trigger_count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fail("Failed to get count of triggers returned by listing"); + ret = -1; + goto end; + } + + ret = (int) trigger_count; + +end: + lttng_triggers_destroy(triggers); + return ret; +} + +static +int setup_session_with_size_rotation_schedule(const char *session_output_path) +{ + int ret; + struct lttng_session_descriptor *session_desriptor = NULL; + enum lttng_error_code ret_code; + struct lttng_handle ust_channel_handle = { + TEST_SESSION_NAME, + { + .type = LTTNG_DOMAIN_UST, + .buf_type = LTTNG_BUFFER_PER_UID, + } + }; + + lttng_channel channel_cfg {}; + strcpy(channel_cfg.name, TEST_CHANNEL_NAME); + channel_cfg.enabled = 1; + channel_cfg.attr.overwrite = -1; + channel_cfg.attr.subbuf_size = (uint64_t) sysconf(_SC_PAGE_SIZE) * 8; + channel_cfg.attr.num_subbuf = 8; + channel_cfg.attr.output = LTTNG_EVENT_MMAP; + + enum lttng_rotation_status rotation_status; + struct lttng_rotation_schedule *rotation_schedule = NULL; + + session_desriptor = lttng_session_descriptor_local_create( + TEST_SESSION_NAME, session_output_path); + if (!session_desriptor) { + fail("Failed to create session descriptor for session `%s`", + TEST_SESSION_NAME); + ret = -1; + goto end; + } + + ret_code = lttng_create_session_ext(session_desriptor); + if (ret_code != LTTNG_OK) { + fail("Failed to create session `%s`: %s", TEST_SESSION_NAME, + lttng_strerror(-ret_code)); + ret = -1; + goto end; + } + + ret = lttng_enable_channel(&ust_channel_handle, &channel_cfg); + if (ret) { + fail("Failed to enable channel `%s`: %s", TEST_CHANNEL_NAME, + lttng_strerror(ret)); + ret = -1; + goto end; + } + + ret = lttng_start_tracing(TEST_SESSION_NAME); + if (ret) { + fail("Failed to start session `%s`: %s", TEST_SESSION_NAME, + lttng_strerror(ret)); + ret = -1; + goto end; + } + + rotation_schedule = lttng_rotation_schedule_size_threshold_create(); + if (!rotation_schedule) { + fail("Failed to create rotation schedule descriptor"); + ret = -1; + goto end; + } + + /* + * The rotation schedule size threshold doesn't matter; no event rules + * were specified so the session consumed size should not grow over + * time. + */ + rotation_status = lttng_rotation_schedule_size_threshold_set_threshold( + rotation_schedule, sysconf(_SC_PAGE_SIZE) * 4096); + if (rotation_status != LTTNG_ROTATION_STATUS_OK) { + fail("Failed to set size threshold of session rotation schedule"); + ret = -1; + goto end; + } + + rotation_status = lttng_session_add_rotation_schedule( + TEST_SESSION_NAME, rotation_schedule); + if (rotation_status != LTTNG_ROTATION_STATUS_OK) { + fail("Failed to set size-based rotation schedule on session `%s`", + TEST_SESSION_NAME); + ret = -1; + goto end; + } + + ret = 0; +end: + lttng_session_descriptor_destroy(session_desriptor); + lttng_rotation_schedule_destroy(rotation_schedule); + return ret; +} + +int main(int argc, const char **argv) +{ + int ret; + + if (argc != 2) { + fail("Missing trace path"); + goto end; + } + + plan_tests(TEST_COUNT); + + if (get_registered_triggers_count() != 0) { + fail("Session daemon already has registered triggers, bailing out"); + goto end; + } + + ret = setup_session_with_size_rotation_schedule(argv[1]); + if (ret) { + goto end; + } + + ok(get_registered_triggers_count() == 0, + "No triggers visible while session has an enabled size-based rotation schedule"); + + ret = lttng_destroy_session(TEST_SESSION_NAME); + if (ret) { + fail("Failed to destroy session `%s`", TEST_SESSION_NAME); + goto end; + } +end: + return exit_status(); +} diff --git a/tests/regression/tools/trigger/name/Makefile.am b/tests/regression/tools/trigger/name/Makefile.am index d4bc5ed06..bd267b886 100644 --- a/tests/regression/tools/trigger/name/Makefile.am +++ b/tests/regression/tools/trigger/name/Makefile.am @@ -7,8 +7,8 @@ LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la noinst_PROGRAMS = trigger_name # This test explicitly tests APIs that were marked as deprecated. -trigger_name_CFLAGS = -Wno-deprecated-declarations $(AM_CFLAGS) -trigger_name_SOURCES = trigger_name.c +trigger_name_CXXFLAGS = -Wno-deprecated-declarations $(AM_CXXFLAGS) +trigger_name_SOURCES = trigger_name.cpp trigger_name_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) noinst_SCRIPTS = test_trigger_name_backwards_compat diff --git a/tests/regression/tools/trigger/name/trigger_name.c b/tests/regression/tools/trigger/name/trigger_name.c deleted file mode 100644 index 3bb3099ce..000000000 --- a/tests/regression/tools/trigger/name/trigger_name.c +++ /dev/null @@ -1,643 +0,0 @@ -/* - * trigger_name.c - * - * Tests suite for anonymous, named, and automatic name triggers. - * - * Copyright (C) 2021 Jérémie Galarneau - * - * SPDX-License-Identifier: MIT - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#define TEST_COUNT 70 - -enum unregistration_trigger_instance { - UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION, - UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING, -}; - -typedef void (*test_function)(enum unregistration_trigger_instance); - -static -const char *get_trigger_name(const struct lttng_trigger *trigger) -{ - const char *trigger_name; - enum lttng_trigger_status trigger_status; - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - switch (trigger_status) { - case LTTNG_TRIGGER_STATUS_OK: - break; - case LTTNG_TRIGGER_STATUS_UNSET: - trigger_name = "(anonymous)"; - break; - default: - trigger_name = "(failed to get name)"; - break; - } - - return trigger_name; -} - -static -const char *unregistration_trigger_instance_name( - enum unregistration_trigger_instance unregistration_trigger) -{ - const char *name; - - switch (unregistration_trigger) { - case UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING: - name = "from listing"; - break; - case UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION: - name = "used for registration"; - break; - default: - abort(); - } - - return name; -} - -/* - * Returns a negative error code on error, else the number of unregistered - * triggers. - */ -static -int unregister_all_triggers(void) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_trigger_status trigger_status; - struct lttng_triggers *triggers = NULL; - unsigned int trigger_count, i, unregistered_trigger_count = 0; - - ret_code = lttng_list_triggers(&triggers); - if (ret_code != LTTNG_OK) { - fail("Failed to list triggers"); - ret = -1; - goto end; - } - - trigger_status = lttng_triggers_get_count(triggers, &trigger_count); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - fail("Failed to get count of triggers returned by listing"); - ret = -1; - goto end; - } - - for (i = 0; i < trigger_count; i++) { - const struct lttng_trigger *trigger; - - trigger = lttng_triggers_get_at_index(triggers, i); - LTTNG_ASSERT(trigger); - - ret = lttng_unregister_trigger(trigger); - if (ret) { - fail("Failed to unregister trigger: trigger name = '%s'"); - goto end; - } - - unregistered_trigger_count++; - } - - ret = (int) unregistered_trigger_count; - -end: - lttng_triggers_destroy(triggers); - return ret; -} - -static -int get_registered_triggers_count(void) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_trigger_status trigger_status; - struct lttng_triggers *triggers = NULL; - unsigned int trigger_count; - - ret_code = lttng_list_triggers(&triggers); - if (ret_code != LTTNG_OK) { - fail("Failed to list triggers"); - ret = -1; - goto end; - } - - trigger_status = lttng_triggers_get_count(triggers, &trigger_count); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - fail("Failed to get count of triggers returned by listing"); - ret = -1; - goto end; - } - - ret = (int) trigger_count; - -end: - lttng_triggers_destroy(triggers); - return ret; -} - -/* - * Create a generic trigger. The specifics of the condition and action are not - * important for the purposes of this test. - */ -static -struct lttng_trigger *create_trigger(uint64_t threshold) -{ - struct lttng_condition *condition = NULL; - struct lttng_action *action = NULL; - struct lttng_trigger *trigger = NULL; - enum lttng_condition_status condition_status; - const char * const session_name = "test session"; - - condition = lttng_condition_session_consumed_size_create(); - if (!condition) { - fail("Failed to create 'session consumed size' condition"); - goto end; - } - - condition_status = lttng_condition_session_consumed_size_set_session_name(condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Failed to set session name on 'session consumed size' condition"); - goto end; - } - - condition_status = lttng_condition_session_consumed_size_set_threshold( - condition, threshold); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Failed to set threshold on 'session consumed size' condition"); - goto end; - } - - action = lttng_action_notify_create(); - if (!action) { - fail("Failed to create 'notify' action"); - goto end; - } - - trigger = lttng_trigger_create(condition, action); - if (!trigger) { - fail("Failed to create trigger"); - goto end; - } - -end: - lttng_condition_destroy(condition); - lttng_action_destroy(action); - return trigger; -} - -static -void register_anonymous_trigger( - enum unregistration_trigger_instance unregistration_trigger) -{ - int ret; - struct lttng_trigger *trigger = create_trigger(0xbadc0ffee); - enum lttng_trigger_status trigger_status; - const char *trigger_name; - struct lttng_triggers *triggers = NULL; - unsigned int trigger_count, i; - enum lttng_error_code ret_code; - - diag("Register an anonymous trigger (Unregistration performed with the trigger instance %s)", - unregistration_trigger_instance_name( - unregistration_trigger)); - - if (!trigger) { - fail("Failed to create trigger"); - goto end; - } - - ret = lttng_register_trigger(trigger); - ok(ret == 0, "Registered anonymous trigger"); - - trigger_status = lttng_trigger_get_name(trigger, &trigger_name); - ok(trigger_status == LTTNG_TRIGGER_STATUS_UNSET, - "Anonymous trigger name remains unset after registration: trigger name = '%s'", - get_trigger_name(trigger)); - - ret_code = lttng_list_triggers(&triggers); - if (ret_code != LTTNG_OK) { - fail("Failed to list triggers"); - ret = -1; - goto end; - } - - trigger_status = lttng_triggers_get_count(triggers, &trigger_count); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - fail("Failed to get count of triggers returned by listing"); - ret = -1; - goto end; - } - - ok(trigger_count == 1, "Trigger listing returns 1 trigger"); - - for (i = 0; i < trigger_count; i++) { - const struct lttng_trigger *trigger_from_listing; - - trigger_from_listing = lttng_triggers_get_at_index(triggers, i); - LTTNG_ASSERT(trigger_from_listing); - - trigger_status = lttng_trigger_get_name(trigger_from_listing, &trigger_name); - ok(trigger_status == LTTNG_TRIGGER_STATUS_UNSET, - "Anonymous trigger returned by listing has an unset name: trigger name = '%s'", - get_trigger_name(trigger_from_listing)); - - if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING) { - ret = lttng_unregister_trigger(trigger_from_listing); - ok(ret == 0, "Successfully unregistered anonymous trigger using the trigger instance returned by the listing"); - } - } - - if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { - ret = lttng_unregister_trigger(trigger); - ok(ret == 0, "Successfully unregistered anonymous trigger using the trigger instance used on registration"); - } - -end: - lttng_triggers_destroy(triggers); - lttng_trigger_destroy(trigger); -} - -static -void register_named_trigger( - enum unregistration_trigger_instance unregistration_trigger) -{ - int ret; - struct lttng_trigger *trigger = create_trigger(0xbadc0ffee); - enum lttng_trigger_status trigger_status; - const char *returned_trigger_name; - struct lttng_triggers *triggers = NULL; - unsigned int trigger_count, i; - enum lttng_error_code ret_code; - const char * const trigger_name = "some name that is hopefully unique"; - - diag("Register a named trigger (Unregistration performed with the trigger instance %s)", - unregistration_trigger_instance_name( - unregistration_trigger)); - - if (!trigger) { - fail("Failed to create trigger"); - goto end; - } - - ret_code = lttng_register_trigger_with_name(trigger, trigger_name); - ok(ret_code == LTTNG_OK, "Registered trigger with name: trigger name = '%s'", - get_trigger_name(trigger)); - - trigger_status = lttng_trigger_get_name(trigger, &returned_trigger_name); - ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, - "Trigger name is set after registration: trigger name = '%s'", - get_trigger_name(trigger)); - - ok(!strcmp(get_trigger_name(trigger), trigger_name), - "Name set on trigger after registration is correct"); - - ret_code = lttng_list_triggers(&triggers); - if (ret_code != LTTNG_OK) { - fail("Failed to list triggers"); - ret = -1; - goto end; - } - - trigger_status = lttng_triggers_get_count(triggers, &trigger_count); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - fail("Failed to get count of triggers returned by listing"); - ret = -1; - goto end; - } - - ok(trigger_count == 1, "Trigger listing returns 1 trigger"); - - for (i = 0; i < trigger_count; i++) { - const struct lttng_trigger *trigger_from_listing; - - trigger_from_listing = lttng_triggers_get_at_index(triggers, i); - LTTNG_ASSERT(trigger_from_listing); - - trigger_status = lttng_trigger_get_name(trigger_from_listing, &returned_trigger_name); - ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, - "Trigger returned by listing has a name: trigger name = '%s'", - get_trigger_name(trigger_from_listing)); - - ok(!strcmp(get_trigger_name(trigger_from_listing), - trigger_name), - "Name set on trigger returned from listing is correct: name returned from listing = '%s', expected name = '%s'", - get_trigger_name(trigger_from_listing), - trigger_name); - - if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING) { - ret = lttng_unregister_trigger(trigger_from_listing); - ok(ret == 0, "Successfully unregistered named trigger using the trigger instance returned by the listing"); - } - } - - if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { - ret = lttng_unregister_trigger(trigger); - ok(ret == 0, "Successfully unregistered named trigger using the trigger instance used on registration"); - } - -end: - lttng_triggers_destroy(triggers); - lttng_trigger_destroy(trigger); -} - -static -void register_automatic_name_trigger( - enum unregistration_trigger_instance unregistration_trigger) -{ - int ret; - struct lttng_trigger *trigger = create_trigger(0xbadc0ffee); - enum lttng_trigger_status trigger_status; - const char *returned_trigger_name; - struct lttng_triggers *triggers = NULL; - unsigned int trigger_count, i; - enum lttng_error_code ret_code; - - diag("Register an automatic name trigger (Unregistration performed with the trigger instance %s)", - unregistration_trigger_instance_name( - unregistration_trigger)); - - if (!trigger) { - fail("Failed to create trigger"); - goto end; - } - - ret_code = lttng_register_trigger_with_automatic_name(trigger); - ok(ret_code == LTTNG_OK, "Registered trigger with automatic name"); - - trigger_status = lttng_trigger_get_name(trigger, &returned_trigger_name); - ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, - "Trigger name is set after registration: trigger name = '%s'", - get_trigger_name(trigger)); - - ok(returned_trigger_name && strlen(returned_trigger_name) > 0, - "Automatic name set on trigger after registration longer is not an empty string"); - - ret_code = lttng_list_triggers(&triggers); - if (ret_code != LTTNG_OK) { - fail("Failed to list triggers"); - ret = -1; - goto end; - } - - trigger_status = lttng_triggers_get_count(triggers, &trigger_count); - if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { - fail("Failed to get count of triggers returned by listing"); - ret = -1; - goto end; - } - - ok(trigger_count == 1, "Trigger listing returns 1 trigger"); - - for (i = 0; i < trigger_count; i++) { - const struct lttng_trigger *trigger_from_listing; - - trigger_from_listing = lttng_triggers_get_at_index(triggers, i); - LTTNG_ASSERT(trigger_from_listing); - - trigger_status = lttng_trigger_get_name(trigger_from_listing, &returned_trigger_name); - ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, - "Trigger returned by listing has a name: trigger name = '%s'", - get_trigger_name(trigger_from_listing)); - - if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING) { - ret = lttng_unregister_trigger(trigger_from_listing); - ok(ret == 0, "Successfully unregistered automatic name trigger using the trigger instance returned by the listing"); - } - } - - if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { - ret = lttng_unregister_trigger(trigger); - ok(ret == 0, "Successfully unregistered automatic trigger using the trigger instance used on registration"); - } - -end: - lttng_triggers_destroy(triggers); - lttng_trigger_destroy(trigger); -} - -static -void double_register_anonymous_trigger( - enum unregistration_trigger_instance unregistration_trigger) -{ - int ret; - struct lttng_trigger *trigger = create_trigger(0xbadc0ffee); - struct lttng_triggers *triggers = NULL; - - diag("Register duplicate anonymous trigger (Unregistration performed with the trigger instance %s)", - unregistration_trigger_instance_name( - unregistration_trigger)); - - if (!trigger) { - fail("Failed to create trigger"); - goto end; - } - - ret = lttng_register_trigger(trigger); - ok(ret == 0, "Registered anonymous trigger"); - - ret = lttng_register_trigger(trigger); - ok(ret == -LTTNG_ERR_TRIGGER_EXISTS, - "Registering identical anonymous trigger fails with `LTTNG_ERR_TRIGGER_EXISTS`"); - - - if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { - ret = lttng_unregister_trigger(trigger); - ok(ret == 0, "Successfully unregistered anonymous trigger using the trigger instance used on registration"); - } else { - ok(get_registered_triggers_count() == 1, - "Trigger listing returns 1 trigger"); - ok(unregister_all_triggers() == 1, - "Successfully unregistered anonymous trigger using the trigger instance returned by the listing"); - } - -end: - lttng_triggers_destroy(triggers); - lttng_trigger_destroy(trigger); -} - -static -void double_register_named_trigger( - enum unregistration_trigger_instance unregistration_trigger) -{ - int ret; - struct lttng_trigger *trigger_a = create_trigger(0xbadc0ffee); - struct lttng_trigger *trigger_b = create_trigger(0xbadc0ffee); - struct lttng_triggers *triggers = NULL; - const char * const trigger_name = "a unique trigger name"; - enum lttng_error_code ret_code; - - diag("Register duplicate named trigger (Unregistration performed with the trigger instance %s)", - unregistration_trigger_instance_name( - unregistration_trigger)); - - if (!trigger_a || !trigger_b) { - fail("Failed to create triggers"); - goto end; - } - - ret_code = lttng_register_trigger_with_name(trigger_a, trigger_name); - ok(ret_code == LTTNG_OK, "Registered named trigger"); - - ret = lttng_register_trigger(trigger_a); - ok(ret == -LTTNG_ERR_INVALID, - "Registering a trigger instance already used for registration fails with `LTTNG_ERR_INVALID` (anonymous registration)"); - - ret_code = lttng_register_trigger_with_name(trigger_a, trigger_name); - ok(ret_code == LTTNG_ERR_INVALID, - "Registering a trigger instance already used for registration fails with `LTTNG_ERR_INVALID` (register with name)"); - - ret_code = lttng_register_trigger_with_automatic_name(trigger_a); - ok(ret_code == LTTNG_ERR_INVALID, - "Registering a trigger instance already used for registration fails with `LTTNG_ERR_INVALID` (register with automatic name)"); - - ret_code = lttng_register_trigger_with_name(trigger_b, trigger_name); - ok(ret_code == LTTNG_ERR_TRIGGER_EXISTS, "Registering trigger with an already used name fails with `LTTNG_ERR_TRIGGER_EXISTS`"); - - if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { - ret = lttng_unregister_trigger(trigger_a); - ok(ret == 0, "Successfully unregistered named trigger using the trigger instance used on registration"); - } else { - ok(get_registered_triggers_count() == 1, - "Trigger listing returns 1 trigger"); - ok(unregister_all_triggers() == 1, - "Successfully unregistered named trigger using the trigger instance returned by the listing"); - } - -end: - lttng_triggers_destroy(triggers); - lttng_trigger_destroy(trigger_a); - lttng_trigger_destroy(trigger_b); -} - -static -void double_register_automatic_name_trigger( - enum unregistration_trigger_instance unregistration_trigger) -{ - int ret; - struct lttng_trigger *trigger_a = create_trigger(0xbadc0ffee); - struct lttng_trigger *trigger_b = create_trigger(0xbadc0ffee); - struct lttng_triggers *triggers = NULL; - enum lttng_error_code ret_code; - - diag("Register duplicate automatic name trigger (Unregistration performed with the trigger instance %s)", - unregistration_trigger_instance_name( - unregistration_trigger)); - - if (!trigger_a || !trigger_b) { - fail("Failed to create triggers"); - goto end; - } - - ret_code = lttng_register_trigger_with_automatic_name(trigger_a); - ok(ret_code == LTTNG_OK, "Registered automatic name trigger: trigger name = '%s'", get_trigger_name(trigger_a)); - - ret = lttng_register_trigger_with_automatic_name(trigger_b); - ok(ret_code == LTTNG_OK, "Registering an identical trigger instance with an automatic name succeeds: trigger name = '%s'", get_trigger_name(trigger_b)); - - ok(strcmp(get_trigger_name(trigger_a), get_trigger_name(trigger_b)), - "Two identical triggers registered with an automatic name have different names"); - - if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { - ret = lttng_unregister_trigger(trigger_a); - ok(ret == 0, "Successfully unregistered automatic trigger A using the trigger instance used on registration"); - - ret = lttng_unregister_trigger(trigger_b); - ok(ret == 0, "Successfully unregistered automatic trigger B using the trigger instance used on registration"); - } else { - ok(get_registered_triggers_count() == 2, - "Trigger listing returns 2 trigger"); - ok(unregister_all_triggers() == 2, - "Successfully unregistered automatic name triggers using the trigger instance returned by the listing"); - } - -end: - lttng_triggers_destroy(triggers); - lttng_trigger_destroy(trigger_a); - lttng_trigger_destroy(trigger_b); -} - -static -void register_multiple_anonymous_triggers(void) -{ - int ret; - struct lttng_trigger *trigger_a = create_trigger(0xbadc0ffee); - struct lttng_trigger *trigger_b = create_trigger(0xbadf00d); - - diag("Register two different anonymous triggers"); - - if (!trigger_a || !trigger_b) { - fail("Failed to create triggers"); - goto end; - } - - ret = lttng_register_trigger(trigger_a); - ok(ret == 0, "Registered first anonymous trigger"); - - ret = lttng_register_trigger(trigger_b); - ok(ret == 0, "Registered second anonymous trigger"); - - ok(get_registered_triggers_count() == 2, - "Trigger listing returns 2 trigger"); - ok(unregister_all_triggers() == 2, - "Successfully unregistered two anonymous triggers"); - -end: - lttng_trigger_destroy(trigger_a); - lttng_trigger_destroy(trigger_b); -} - -const test_function test_functions[] = { - register_anonymous_trigger, - register_named_trigger, - register_automatic_name_trigger, - double_register_anonymous_trigger, - double_register_named_trigger, - double_register_automatic_name_trigger, -}; - -int main(int argc, const char **argv) -{ - size_t i; - - plan_tests(TEST_COUNT); - - if (get_registered_triggers_count() != 0) { - fail("Session daemon already has registered triggers, bailing out"); - goto end; - } - - for (i = 0; i < ARRAY_SIZE(test_functions); i++) { - const test_function fn = test_functions[i]; - - fn(UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING); - if (get_registered_triggers_count() != 0) { - fail("Previous test left registered triggers, bailing out"); - goto end; - } - } - - for (i = 0; i < ARRAY_SIZE(test_functions); i++) { - const test_function fn = test_functions[i]; - - fn(UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION); - if (get_registered_triggers_count() != 0) { - fail("Previous test left registered triggers, bailing out"); - goto end; - } - } - - register_multiple_anonymous_triggers(); -end: - return exit_status(); -} diff --git a/tests/regression/tools/trigger/name/trigger_name.cpp b/tests/regression/tools/trigger/name/trigger_name.cpp new file mode 100644 index 000000000..3bb3099ce --- /dev/null +++ b/tests/regression/tools/trigger/name/trigger_name.cpp @@ -0,0 +1,643 @@ +/* + * trigger_name.c + * + * Tests suite for anonymous, named, and automatic name triggers. + * + * Copyright (C) 2021 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define TEST_COUNT 70 + +enum unregistration_trigger_instance { + UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION, + UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING, +}; + +typedef void (*test_function)(enum unregistration_trigger_instance); + +static +const char *get_trigger_name(const struct lttng_trigger *trigger) +{ + const char *trigger_name; + enum lttng_trigger_status trigger_status; + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + switch (trigger_status) { + case LTTNG_TRIGGER_STATUS_OK: + break; + case LTTNG_TRIGGER_STATUS_UNSET: + trigger_name = "(anonymous)"; + break; + default: + trigger_name = "(failed to get name)"; + break; + } + + return trigger_name; +} + +static +const char *unregistration_trigger_instance_name( + enum unregistration_trigger_instance unregistration_trigger) +{ + const char *name; + + switch (unregistration_trigger) { + case UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING: + name = "from listing"; + break; + case UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION: + name = "used for registration"; + break; + default: + abort(); + } + + return name; +} + +/* + * Returns a negative error code on error, else the number of unregistered + * triggers. + */ +static +int unregister_all_triggers(void) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_trigger_status trigger_status; + struct lttng_triggers *triggers = NULL; + unsigned int trigger_count, i, unregistered_trigger_count = 0; + + ret_code = lttng_list_triggers(&triggers); + if (ret_code != LTTNG_OK) { + fail("Failed to list triggers"); + ret = -1; + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &trigger_count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fail("Failed to get count of triggers returned by listing"); + ret = -1; + goto end; + } + + for (i = 0; i < trigger_count; i++) { + const struct lttng_trigger *trigger; + + trigger = lttng_triggers_get_at_index(triggers, i); + LTTNG_ASSERT(trigger); + + ret = lttng_unregister_trigger(trigger); + if (ret) { + fail("Failed to unregister trigger: trigger name = '%s'"); + goto end; + } + + unregistered_trigger_count++; + } + + ret = (int) unregistered_trigger_count; + +end: + lttng_triggers_destroy(triggers); + return ret; +} + +static +int get_registered_triggers_count(void) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_trigger_status trigger_status; + struct lttng_triggers *triggers = NULL; + unsigned int trigger_count; + + ret_code = lttng_list_triggers(&triggers); + if (ret_code != LTTNG_OK) { + fail("Failed to list triggers"); + ret = -1; + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &trigger_count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fail("Failed to get count of triggers returned by listing"); + ret = -1; + goto end; + } + + ret = (int) trigger_count; + +end: + lttng_triggers_destroy(triggers); + return ret; +} + +/* + * Create a generic trigger. The specifics of the condition and action are not + * important for the purposes of this test. + */ +static +struct lttng_trigger *create_trigger(uint64_t threshold) +{ + struct lttng_condition *condition = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + enum lttng_condition_status condition_status; + const char * const session_name = "test session"; + + condition = lttng_condition_session_consumed_size_create(); + if (!condition) { + fail("Failed to create 'session consumed size' condition"); + goto end; + } + + condition_status = lttng_condition_session_consumed_size_set_session_name(condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Failed to set session name on 'session consumed size' condition"); + goto end; + } + + condition_status = lttng_condition_session_consumed_size_set_threshold( + condition, threshold); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Failed to set threshold on 'session consumed size' condition"); + goto end; + } + + action = lttng_action_notify_create(); + if (!action) { + fail("Failed to create 'notify' action"); + goto end; + } + + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Failed to create trigger"); + goto end; + } + +end: + lttng_condition_destroy(condition); + lttng_action_destroy(action); + return trigger; +} + +static +void register_anonymous_trigger( + enum unregistration_trigger_instance unregistration_trigger) +{ + int ret; + struct lttng_trigger *trigger = create_trigger(0xbadc0ffee); + enum lttng_trigger_status trigger_status; + const char *trigger_name; + struct lttng_triggers *triggers = NULL; + unsigned int trigger_count, i; + enum lttng_error_code ret_code; + + diag("Register an anonymous trigger (Unregistration performed with the trigger instance %s)", + unregistration_trigger_instance_name( + unregistration_trigger)); + + if (!trigger) { + fail("Failed to create trigger"); + goto end; + } + + ret = lttng_register_trigger(trigger); + ok(ret == 0, "Registered anonymous trigger"); + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_UNSET, + "Anonymous trigger name remains unset after registration: trigger name = '%s'", + get_trigger_name(trigger)); + + ret_code = lttng_list_triggers(&triggers); + if (ret_code != LTTNG_OK) { + fail("Failed to list triggers"); + ret = -1; + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &trigger_count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fail("Failed to get count of triggers returned by listing"); + ret = -1; + goto end; + } + + ok(trigger_count == 1, "Trigger listing returns 1 trigger"); + + for (i = 0; i < trigger_count; i++) { + const struct lttng_trigger *trigger_from_listing; + + trigger_from_listing = lttng_triggers_get_at_index(triggers, i); + LTTNG_ASSERT(trigger_from_listing); + + trigger_status = lttng_trigger_get_name(trigger_from_listing, &trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_UNSET, + "Anonymous trigger returned by listing has an unset name: trigger name = '%s'", + get_trigger_name(trigger_from_listing)); + + if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING) { + ret = lttng_unregister_trigger(trigger_from_listing); + ok(ret == 0, "Successfully unregistered anonymous trigger using the trigger instance returned by the listing"); + } + } + + if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { + ret = lttng_unregister_trigger(trigger); + ok(ret == 0, "Successfully unregistered anonymous trigger using the trigger instance used on registration"); + } + +end: + lttng_triggers_destroy(triggers); + lttng_trigger_destroy(trigger); +} + +static +void register_named_trigger( + enum unregistration_trigger_instance unregistration_trigger) +{ + int ret; + struct lttng_trigger *trigger = create_trigger(0xbadc0ffee); + enum lttng_trigger_status trigger_status; + const char *returned_trigger_name; + struct lttng_triggers *triggers = NULL; + unsigned int trigger_count, i; + enum lttng_error_code ret_code; + const char * const trigger_name = "some name that is hopefully unique"; + + diag("Register a named trigger (Unregistration performed with the trigger instance %s)", + unregistration_trigger_instance_name( + unregistration_trigger)); + + if (!trigger) { + fail("Failed to create trigger"); + goto end; + } + + ret_code = lttng_register_trigger_with_name(trigger, trigger_name); + ok(ret_code == LTTNG_OK, "Registered trigger with name: trigger name = '%s'", + get_trigger_name(trigger)); + + trigger_status = lttng_trigger_get_name(trigger, &returned_trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Trigger name is set after registration: trigger name = '%s'", + get_trigger_name(trigger)); + + ok(!strcmp(get_trigger_name(trigger), trigger_name), + "Name set on trigger after registration is correct"); + + ret_code = lttng_list_triggers(&triggers); + if (ret_code != LTTNG_OK) { + fail("Failed to list triggers"); + ret = -1; + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &trigger_count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fail("Failed to get count of triggers returned by listing"); + ret = -1; + goto end; + } + + ok(trigger_count == 1, "Trigger listing returns 1 trigger"); + + for (i = 0; i < trigger_count; i++) { + const struct lttng_trigger *trigger_from_listing; + + trigger_from_listing = lttng_triggers_get_at_index(triggers, i); + LTTNG_ASSERT(trigger_from_listing); + + trigger_status = lttng_trigger_get_name(trigger_from_listing, &returned_trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Trigger returned by listing has a name: trigger name = '%s'", + get_trigger_name(trigger_from_listing)); + + ok(!strcmp(get_trigger_name(trigger_from_listing), + trigger_name), + "Name set on trigger returned from listing is correct: name returned from listing = '%s', expected name = '%s'", + get_trigger_name(trigger_from_listing), + trigger_name); + + if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING) { + ret = lttng_unregister_trigger(trigger_from_listing); + ok(ret == 0, "Successfully unregistered named trigger using the trigger instance returned by the listing"); + } + } + + if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { + ret = lttng_unregister_trigger(trigger); + ok(ret == 0, "Successfully unregistered named trigger using the trigger instance used on registration"); + } + +end: + lttng_triggers_destroy(triggers); + lttng_trigger_destroy(trigger); +} + +static +void register_automatic_name_trigger( + enum unregistration_trigger_instance unregistration_trigger) +{ + int ret; + struct lttng_trigger *trigger = create_trigger(0xbadc0ffee); + enum lttng_trigger_status trigger_status; + const char *returned_trigger_name; + struct lttng_triggers *triggers = NULL; + unsigned int trigger_count, i; + enum lttng_error_code ret_code; + + diag("Register an automatic name trigger (Unregistration performed with the trigger instance %s)", + unregistration_trigger_instance_name( + unregistration_trigger)); + + if (!trigger) { + fail("Failed to create trigger"); + goto end; + } + + ret_code = lttng_register_trigger_with_automatic_name(trigger); + ok(ret_code == LTTNG_OK, "Registered trigger with automatic name"); + + trigger_status = lttng_trigger_get_name(trigger, &returned_trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Trigger name is set after registration: trigger name = '%s'", + get_trigger_name(trigger)); + + ok(returned_trigger_name && strlen(returned_trigger_name) > 0, + "Automatic name set on trigger after registration longer is not an empty string"); + + ret_code = lttng_list_triggers(&triggers); + if (ret_code != LTTNG_OK) { + fail("Failed to list triggers"); + ret = -1; + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &trigger_count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fail("Failed to get count of triggers returned by listing"); + ret = -1; + goto end; + } + + ok(trigger_count == 1, "Trigger listing returns 1 trigger"); + + for (i = 0; i < trigger_count; i++) { + const struct lttng_trigger *trigger_from_listing; + + trigger_from_listing = lttng_triggers_get_at_index(triggers, i); + LTTNG_ASSERT(trigger_from_listing); + + trigger_status = lttng_trigger_get_name(trigger_from_listing, &returned_trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Trigger returned by listing has a name: trigger name = '%s'", + get_trigger_name(trigger_from_listing)); + + if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING) { + ret = lttng_unregister_trigger(trigger_from_listing); + ok(ret == 0, "Successfully unregistered automatic name trigger using the trigger instance returned by the listing"); + } + } + + if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { + ret = lttng_unregister_trigger(trigger); + ok(ret == 0, "Successfully unregistered automatic trigger using the trigger instance used on registration"); + } + +end: + lttng_triggers_destroy(triggers); + lttng_trigger_destroy(trigger); +} + +static +void double_register_anonymous_trigger( + enum unregistration_trigger_instance unregistration_trigger) +{ + int ret; + struct lttng_trigger *trigger = create_trigger(0xbadc0ffee); + struct lttng_triggers *triggers = NULL; + + diag("Register duplicate anonymous trigger (Unregistration performed with the trigger instance %s)", + unregistration_trigger_instance_name( + unregistration_trigger)); + + if (!trigger) { + fail("Failed to create trigger"); + goto end; + } + + ret = lttng_register_trigger(trigger); + ok(ret == 0, "Registered anonymous trigger"); + + ret = lttng_register_trigger(trigger); + ok(ret == -LTTNG_ERR_TRIGGER_EXISTS, + "Registering identical anonymous trigger fails with `LTTNG_ERR_TRIGGER_EXISTS`"); + + + if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { + ret = lttng_unregister_trigger(trigger); + ok(ret == 0, "Successfully unregistered anonymous trigger using the trigger instance used on registration"); + } else { + ok(get_registered_triggers_count() == 1, + "Trigger listing returns 1 trigger"); + ok(unregister_all_triggers() == 1, + "Successfully unregistered anonymous trigger using the trigger instance returned by the listing"); + } + +end: + lttng_triggers_destroy(triggers); + lttng_trigger_destroy(trigger); +} + +static +void double_register_named_trigger( + enum unregistration_trigger_instance unregistration_trigger) +{ + int ret; + struct lttng_trigger *trigger_a = create_trigger(0xbadc0ffee); + struct lttng_trigger *trigger_b = create_trigger(0xbadc0ffee); + struct lttng_triggers *triggers = NULL; + const char * const trigger_name = "a unique trigger name"; + enum lttng_error_code ret_code; + + diag("Register duplicate named trigger (Unregistration performed with the trigger instance %s)", + unregistration_trigger_instance_name( + unregistration_trigger)); + + if (!trigger_a || !trigger_b) { + fail("Failed to create triggers"); + goto end; + } + + ret_code = lttng_register_trigger_with_name(trigger_a, trigger_name); + ok(ret_code == LTTNG_OK, "Registered named trigger"); + + ret = lttng_register_trigger(trigger_a); + ok(ret == -LTTNG_ERR_INVALID, + "Registering a trigger instance already used for registration fails with `LTTNG_ERR_INVALID` (anonymous registration)"); + + ret_code = lttng_register_trigger_with_name(trigger_a, trigger_name); + ok(ret_code == LTTNG_ERR_INVALID, + "Registering a trigger instance already used for registration fails with `LTTNG_ERR_INVALID` (register with name)"); + + ret_code = lttng_register_trigger_with_automatic_name(trigger_a); + ok(ret_code == LTTNG_ERR_INVALID, + "Registering a trigger instance already used for registration fails with `LTTNG_ERR_INVALID` (register with automatic name)"); + + ret_code = lttng_register_trigger_with_name(trigger_b, trigger_name); + ok(ret_code == LTTNG_ERR_TRIGGER_EXISTS, "Registering trigger with an already used name fails with `LTTNG_ERR_TRIGGER_EXISTS`"); + + if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { + ret = lttng_unregister_trigger(trigger_a); + ok(ret == 0, "Successfully unregistered named trigger using the trigger instance used on registration"); + } else { + ok(get_registered_triggers_count() == 1, + "Trigger listing returns 1 trigger"); + ok(unregister_all_triggers() == 1, + "Successfully unregistered named trigger using the trigger instance returned by the listing"); + } + +end: + lttng_triggers_destroy(triggers); + lttng_trigger_destroy(trigger_a); + lttng_trigger_destroy(trigger_b); +} + +static +void double_register_automatic_name_trigger( + enum unregistration_trigger_instance unregistration_trigger) +{ + int ret; + struct lttng_trigger *trigger_a = create_trigger(0xbadc0ffee); + struct lttng_trigger *trigger_b = create_trigger(0xbadc0ffee); + struct lttng_triggers *triggers = NULL; + enum lttng_error_code ret_code; + + diag("Register duplicate automatic name trigger (Unregistration performed with the trigger instance %s)", + unregistration_trigger_instance_name( + unregistration_trigger)); + + if (!trigger_a || !trigger_b) { + fail("Failed to create triggers"); + goto end; + } + + ret_code = lttng_register_trigger_with_automatic_name(trigger_a); + ok(ret_code == LTTNG_OK, "Registered automatic name trigger: trigger name = '%s'", get_trigger_name(trigger_a)); + + ret = lttng_register_trigger_with_automatic_name(trigger_b); + ok(ret_code == LTTNG_OK, "Registering an identical trigger instance with an automatic name succeeds: trigger name = '%s'", get_trigger_name(trigger_b)); + + ok(strcmp(get_trigger_name(trigger_a), get_trigger_name(trigger_b)), + "Two identical triggers registered with an automatic name have different names"); + + if (unregistration_trigger == UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION) { + ret = lttng_unregister_trigger(trigger_a); + ok(ret == 0, "Successfully unregistered automatic trigger A using the trigger instance used on registration"); + + ret = lttng_unregister_trigger(trigger_b); + ok(ret == 0, "Successfully unregistered automatic trigger B using the trigger instance used on registration"); + } else { + ok(get_registered_triggers_count() == 2, + "Trigger listing returns 2 trigger"); + ok(unregister_all_triggers() == 2, + "Successfully unregistered automatic name triggers using the trigger instance returned by the listing"); + } + +end: + lttng_triggers_destroy(triggers); + lttng_trigger_destroy(trigger_a); + lttng_trigger_destroy(trigger_b); +} + +static +void register_multiple_anonymous_triggers(void) +{ + int ret; + struct lttng_trigger *trigger_a = create_trigger(0xbadc0ffee); + struct lttng_trigger *trigger_b = create_trigger(0xbadf00d); + + diag("Register two different anonymous triggers"); + + if (!trigger_a || !trigger_b) { + fail("Failed to create triggers"); + goto end; + } + + ret = lttng_register_trigger(trigger_a); + ok(ret == 0, "Registered first anonymous trigger"); + + ret = lttng_register_trigger(trigger_b); + ok(ret == 0, "Registered second anonymous trigger"); + + ok(get_registered_triggers_count() == 2, + "Trigger listing returns 2 trigger"); + ok(unregister_all_triggers() == 2, + "Successfully unregistered two anonymous triggers"); + +end: + lttng_trigger_destroy(trigger_a); + lttng_trigger_destroy(trigger_b); +} + +const test_function test_functions[] = { + register_anonymous_trigger, + register_named_trigger, + register_automatic_name_trigger, + double_register_anonymous_trigger, + double_register_named_trigger, + double_register_automatic_name_trigger, +}; + +int main(int argc, const char **argv) +{ + size_t i; + + plan_tests(TEST_COUNT); + + if (get_registered_triggers_count() != 0) { + fail("Session daemon already has registered triggers, bailing out"); + goto end; + } + + for (i = 0; i < ARRAY_SIZE(test_functions); i++) { + const test_function fn = test_functions[i]; + + fn(UNREGISTRATION_TRIGGER_INSTANCE_FROM_LISTING); + if (get_registered_triggers_count() != 0) { + fail("Previous test left registered triggers, bailing out"); + goto end; + } + } + + for (i = 0; i < ARRAY_SIZE(test_functions); i++) { + const test_function fn = test_functions[i]; + + fn(UNREGISTRATION_TRIGGER_INSTANCE_USED_FOR_REGISTRATION); + if (get_registered_triggers_count() != 0) { + fail("Previous test left registered triggers, bailing out"); + goto end; + } + } + + register_multiple_anonymous_triggers(); +end: + return exit_status(); +} diff --git a/tests/regression/tools/trigger/utils/Makefile.am b/tests/regression/tools/trigger/utils/Makefile.am index 21fb53112..dd3b0278f 100644 --- a/tests/regression/tools/trigger/utils/Makefile.am +++ b/tests/regression/tools/trigger/utils/Makefile.am @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -AM_CFLAGS += -I$(srcdir) -I$(top_srcdir)/tests/utils +AM_CPPFLAGS += -I$(srcdir) -I$(top_srcdir)/tests/utils LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la noinst_PROGRAMS = \ @@ -11,7 +11,7 @@ notification_client_SOURCES = notification-client.c notification_client_LDADD = $(LIBLTTNG_CTL) \ $(top_builddir)/tests/utils/libtestutils.la -register_some_triggers_SOURCES = register-some-triggers.c +register_some_triggers_SOURCES = register-some-triggers.cpp register_some_triggers_LDADD = $(LIBLTTNG_CTL) \ $(top_builddir)/src/common/filter/libfilter.la \ $(top_builddir)/src/common/bytecode/libbytecode.la diff --git a/tests/regression/tools/trigger/utils/register-some-triggers.c b/tests/regression/tools/trigger/utils/register-some-triggers.c deleted file mode 100644 index 54eb78303..000000000 --- a/tests/regression/tools/trigger/utils/register-some-triggers.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (C) 2021 Simon Marchi - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -/* Utility to register some triggers, for test purposes. */ - -#include -#include -#include - -#include -#include - -static void register_trigger(const char *trigger_name, - struct lttng_condition *condition, - struct lttng_action *action) -{ - struct lttng_trigger *trigger; - enum lttng_error_code ret; - - trigger = lttng_trigger_create(condition, action); - ret = lttng_register_trigger_with_name(trigger, trigger_name); - LTTNG_ASSERT(ret == LTTNG_OK); -} - -/* - * Register a trigger with the given condition and an action list containing a - * single notify action. - */ -static void register_trigger_action_list_notify( - const char *trigger_name, struct lttng_condition *condition) -{ - struct lttng_action *action_notify; - struct lttng_action *action_list; - enum lttng_action_status action_status; - - action_list = lttng_action_list_create(); - action_notify = lttng_action_notify_create(); - action_status = lttng_action_list_add_action( - action_list, action_notify); - LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); - - register_trigger(trigger_name, condition, action_list); -} - -static struct lttng_condition *create_session_consumed_size_condition( - const char *session_name, uint64_t threshold) -{ - struct lttng_condition *condition; - enum lttng_condition_status condition_status; - - condition = lttng_condition_session_consumed_size_create(); - condition_status = - lttng_condition_session_consumed_size_set_session_name( - condition, session_name); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - condition_status = lttng_condition_session_consumed_size_set_threshold( - condition, threshold); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - - return condition; -} - -static void test_session_consumed_size_condition(void) -{ - register_trigger_action_list_notify( - "trigger-with-session-consumed-size-condition", - create_session_consumed_size_condition( - "the-session-name", 1234)); -} - -static void fill_buffer_usage_condition(struct lttng_condition *condition, - const char *session_name, - const char *channel_name, - enum lttng_domain_type domain_type) -{ - enum lttng_condition_status condition_status; - - condition_status = lttng_condition_buffer_usage_set_session_name( - condition, session_name); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - condition_status = lttng_condition_buffer_usage_set_channel_name( - condition, channel_name); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); - condition_status = lttng_condition_buffer_usage_set_domain_type( - condition, domain_type); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); -} - -static void fill_buffer_usage_bytes_condition(struct lttng_condition *condition, - const char *session_name, - const char *channel_name, - enum lttng_domain_type domain_type, - uint64_t threshold) -{ - enum lttng_condition_status condition_status; - - fill_buffer_usage_condition( - condition, session_name, channel_name, domain_type); - condition_status = lttng_condition_buffer_usage_set_threshold( - condition, threshold); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); -} - -static void fill_buffer_usage_ratio_condition(struct lttng_condition *condition, - const char *session_name, - const char *channel_name, - enum lttng_domain_type domain_type, - double ratio) -{ - enum lttng_condition_status condition_status; - - fill_buffer_usage_condition( - condition, session_name, channel_name, domain_type); - condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - condition, ratio); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); -} - -static struct lttng_condition *create_buffer_usage_high_bytes_condition( - const char *session_name, - const char *channel_name, - enum lttng_domain_type domain_type, - uint64_t threshold) -{ - struct lttng_condition *condition; - - condition = lttng_condition_buffer_usage_high_create(); - fill_buffer_usage_bytes_condition(condition, session_name, channel_name, - domain_type, threshold); - - return condition; -} - -static struct lttng_condition *create_buffer_usage_low_bytes_condition( - const char *session_name, - const char *channel_name, - enum lttng_domain_type domain_type, - uint64_t threshold) -{ - struct lttng_condition *condition; - - condition = lttng_condition_buffer_usage_low_create(); - fill_buffer_usage_bytes_condition(condition, session_name, channel_name, - domain_type, threshold); - - return condition; -} - -static struct lttng_condition *create_buffer_usage_high_ratio_condition( - const char *session_name, - const char *channel_name, - enum lttng_domain_type domain_type, - double ratio) -{ - struct lttng_condition *condition; - - condition = lttng_condition_buffer_usage_high_create(); - fill_buffer_usage_ratio_condition(condition, session_name, channel_name, - domain_type, ratio); - - return condition; -} - -static struct lttng_condition *create_buffer_usage_low_ratio_condition( - const char *session_name, - const char *channel_name, - enum lttng_domain_type domain_type, - double ratio) -{ - struct lttng_condition *condition; - - condition = lttng_condition_buffer_usage_low_create(); - fill_buffer_usage_ratio_condition(condition, session_name, channel_name, - domain_type, ratio); - - return condition; -} - -static void test_buffer_usage_conditions(void) -{ - register_trigger_action_list_notify( - "trigger-with-buffer-usage-high-bytes-condition", - create_buffer_usage_high_bytes_condition( - "the-session-name", "the-channel-name", - LTTNG_DOMAIN_UST, 1234)); - - register_trigger_action_list_notify( - "trigger-with-buffer-usage-low-bytes-condition", - create_buffer_usage_low_bytes_condition( - "the-session-name", "the-channel-name", - LTTNG_DOMAIN_UST, 2345)); - - register_trigger_action_list_notify( - "trigger-with-buffer-usage-high-ratio-condition", - create_buffer_usage_high_ratio_condition( - "the-session-name", "the-channel-name", - LTTNG_DOMAIN_UST, 0.25)); - - register_trigger_action_list_notify( - "trigger-with-buffer-usage-low-ratio-condition", - create_buffer_usage_low_ratio_condition( - "the-session-name", "the-channel-name", - LTTNG_DOMAIN_UST, 0.4)); -} - -static void fill_session_rotation_condition( - struct lttng_condition *condition, const char *session_name) -{ - enum lttng_condition_status condition_status; - - condition_status = lttng_condition_session_rotation_set_session_name( - condition, session_name); - LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); -} - -static struct lttng_condition *create_session_rotation_ongoing_condition( - const char *session_name) -{ - struct lttng_condition *condition; - - condition = lttng_condition_session_rotation_ongoing_create(); - - fill_session_rotation_condition(condition, session_name); - - return condition; -} - -static struct lttng_condition *create_session_rotation_completed_condition( - const char *session_name) -{ - struct lttng_condition *condition; - - condition = lttng_condition_session_rotation_completed_create(); - - fill_session_rotation_condition(condition, session_name); - - return condition; -} - -static void test_session_rotation_conditions(void) -{ - register_trigger_action_list_notify( - "trigger-with-session-rotation-ongoing-condition", - create_session_rotation_ongoing_condition( - "the-session-name")); - - register_trigger_action_list_notify( - "trigger-with-session-rotation-completed-condition", - create_session_rotation_completed_condition( - "the-session-name")); -} - -static struct { - const char *name; - void (*callback)(void); -} tests[] = { - { - "test_session_consumed_size_condition", - test_session_consumed_size_condition, - }, - {"test_buffer_usage_conditions", test_buffer_usage_conditions}, - {"test_session_rotation_conditions", - test_session_rotation_conditions}, -}; - -static void show_known_tests(void) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(tests); i++) { - fprintf(stderr, " - %s\n", tests[i].name); - } -} - -int main(int argc, char **argv) -{ - const char *test; - size_t i; - int ret; - - if (argc != 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); - fprintf(stderr, "\n"); - fprintf(stderr, "Test must be one of:\n"); - show_known_tests(); - goto error; - } - - test = argv[1]; - - for (i = 0; i < ARRAY_SIZE(tests); i++) { - if (strcmp(tests[i].name, test) == 0) { - break; - } - } - - if (i == ARRAY_SIZE(tests)) { - fprintf(stderr, "Unrecognized test `%s`\n", test); - fprintf(stderr, "\n"); - fprintf(stderr, "Known tests:\n"); - show_known_tests(); - goto error; - } - - tests[i].callback(); - - ret = 0; - goto end; - -error: - ret = 1; - -end: - return ret; -} diff --git a/tests/regression/tools/trigger/utils/register-some-triggers.cpp b/tests/regression/tools/trigger/utils/register-some-triggers.cpp new file mode 100644 index 000000000..54eb78303 --- /dev/null +++ b/tests/regression/tools/trigger/utils/register-some-triggers.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2021 Simon Marchi + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +/* Utility to register some triggers, for test purposes. */ + +#include +#include +#include + +#include +#include + +static void register_trigger(const char *trigger_name, + struct lttng_condition *condition, + struct lttng_action *action) +{ + struct lttng_trigger *trigger; + enum lttng_error_code ret; + + trigger = lttng_trigger_create(condition, action); + ret = lttng_register_trigger_with_name(trigger, trigger_name); + LTTNG_ASSERT(ret == LTTNG_OK); +} + +/* + * Register a trigger with the given condition and an action list containing a + * single notify action. + */ +static void register_trigger_action_list_notify( + const char *trigger_name, struct lttng_condition *condition) +{ + struct lttng_action *action_notify; + struct lttng_action *action_list; + enum lttng_action_status action_status; + + action_list = lttng_action_list_create(); + action_notify = lttng_action_notify_create(); + action_status = lttng_action_list_add_action( + action_list, action_notify); + LTTNG_ASSERT(action_status == LTTNG_ACTION_STATUS_OK); + + register_trigger(trigger_name, condition, action_list); +} + +static struct lttng_condition *create_session_consumed_size_condition( + const char *session_name, uint64_t threshold) +{ + struct lttng_condition *condition; + enum lttng_condition_status condition_status; + + condition = lttng_condition_session_consumed_size_create(); + condition_status = + lttng_condition_session_consumed_size_set_session_name( + condition, session_name); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + condition_status = lttng_condition_session_consumed_size_set_threshold( + condition, threshold); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + + return condition; +} + +static void test_session_consumed_size_condition(void) +{ + register_trigger_action_list_notify( + "trigger-with-session-consumed-size-condition", + create_session_consumed_size_condition( + "the-session-name", 1234)); +} + +static void fill_buffer_usage_condition(struct lttng_condition *condition, + const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type) +{ + enum lttng_condition_status condition_status; + + condition_status = lttng_condition_buffer_usage_set_session_name( + condition, session_name); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + condition_status = lttng_condition_buffer_usage_set_channel_name( + condition, channel_name); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); + condition_status = lttng_condition_buffer_usage_set_domain_type( + condition, domain_type); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); +} + +static void fill_buffer_usage_bytes_condition(struct lttng_condition *condition, + const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + uint64_t threshold) +{ + enum lttng_condition_status condition_status; + + fill_buffer_usage_condition( + condition, session_name, channel_name, domain_type); + condition_status = lttng_condition_buffer_usage_set_threshold( + condition, threshold); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); +} + +static void fill_buffer_usage_ratio_condition(struct lttng_condition *condition, + const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + double ratio) +{ + enum lttng_condition_status condition_status; + + fill_buffer_usage_condition( + condition, session_name, channel_name, domain_type); + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + condition, ratio); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); +} + +static struct lttng_condition *create_buffer_usage_high_bytes_condition( + const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + uint64_t threshold) +{ + struct lttng_condition *condition; + + condition = lttng_condition_buffer_usage_high_create(); + fill_buffer_usage_bytes_condition(condition, session_name, channel_name, + domain_type, threshold); + + return condition; +} + +static struct lttng_condition *create_buffer_usage_low_bytes_condition( + const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + uint64_t threshold) +{ + struct lttng_condition *condition; + + condition = lttng_condition_buffer_usage_low_create(); + fill_buffer_usage_bytes_condition(condition, session_name, channel_name, + domain_type, threshold); + + return condition; +} + +static struct lttng_condition *create_buffer_usage_high_ratio_condition( + const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + double ratio) +{ + struct lttng_condition *condition; + + condition = lttng_condition_buffer_usage_high_create(); + fill_buffer_usage_ratio_condition(condition, session_name, channel_name, + domain_type, ratio); + + return condition; +} + +static struct lttng_condition *create_buffer_usage_low_ratio_condition( + const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + double ratio) +{ + struct lttng_condition *condition; + + condition = lttng_condition_buffer_usage_low_create(); + fill_buffer_usage_ratio_condition(condition, session_name, channel_name, + domain_type, ratio); + + return condition; +} + +static void test_buffer_usage_conditions(void) +{ + register_trigger_action_list_notify( + "trigger-with-buffer-usage-high-bytes-condition", + create_buffer_usage_high_bytes_condition( + "the-session-name", "the-channel-name", + LTTNG_DOMAIN_UST, 1234)); + + register_trigger_action_list_notify( + "trigger-with-buffer-usage-low-bytes-condition", + create_buffer_usage_low_bytes_condition( + "the-session-name", "the-channel-name", + LTTNG_DOMAIN_UST, 2345)); + + register_trigger_action_list_notify( + "trigger-with-buffer-usage-high-ratio-condition", + create_buffer_usage_high_ratio_condition( + "the-session-name", "the-channel-name", + LTTNG_DOMAIN_UST, 0.25)); + + register_trigger_action_list_notify( + "trigger-with-buffer-usage-low-ratio-condition", + create_buffer_usage_low_ratio_condition( + "the-session-name", "the-channel-name", + LTTNG_DOMAIN_UST, 0.4)); +} + +static void fill_session_rotation_condition( + struct lttng_condition *condition, const char *session_name) +{ + enum lttng_condition_status condition_status; + + condition_status = lttng_condition_session_rotation_set_session_name( + condition, session_name); + LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK); +} + +static struct lttng_condition *create_session_rotation_ongoing_condition( + const char *session_name) +{ + struct lttng_condition *condition; + + condition = lttng_condition_session_rotation_ongoing_create(); + + fill_session_rotation_condition(condition, session_name); + + return condition; +} + +static struct lttng_condition *create_session_rotation_completed_condition( + const char *session_name) +{ + struct lttng_condition *condition; + + condition = lttng_condition_session_rotation_completed_create(); + + fill_session_rotation_condition(condition, session_name); + + return condition; +} + +static void test_session_rotation_conditions(void) +{ + register_trigger_action_list_notify( + "trigger-with-session-rotation-ongoing-condition", + create_session_rotation_ongoing_condition( + "the-session-name")); + + register_trigger_action_list_notify( + "trigger-with-session-rotation-completed-condition", + create_session_rotation_completed_condition( + "the-session-name")); +} + +static struct { + const char *name; + void (*callback)(void); +} tests[] = { + { + "test_session_consumed_size_condition", + test_session_consumed_size_condition, + }, + {"test_buffer_usage_conditions", test_buffer_usage_conditions}, + {"test_session_rotation_conditions", + test_session_rotation_conditions}, +}; + +static void show_known_tests(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + fprintf(stderr, " - %s\n", tests[i].name); + } +} + +int main(int argc, char **argv) +{ + const char *test; + size_t i; + int ret; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "Test must be one of:\n"); + show_known_tests(); + goto error; + } + + test = argv[1]; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + if (strcmp(tests[i].name, test) == 0) { + break; + } + } + + if (i == ARRAY_SIZE(tests)) { + fprintf(stderr, "Unrecognized test `%s`\n", test); + fprintf(stderr, "\n"); + fprintf(stderr, "Known tests:\n"); + show_known_tests(); + goto error; + } + + tests[i].callback(); + + ret = 0; + goto end; + +error: + ret = 1; + +end: + return ret; +} diff --git a/tests/utils/Makefile.am b/tests/utils/Makefile.am index 1c7c95326..96dbe2852 100644 --- a/tests/utils/Makefile.am +++ b/tests/utils/Makefile.am @@ -7,7 +7,9 @@ EXTRA_DIST = utils.sh test_utils.py babelstats.pl warn_processes.sh \ dist_noinst_SCRIPTS = utils.sh test_utils.py babelstats.pl tap-driver.sh noinst_LTLIBRARIES = libtestutils.la -libtestutils_la_SOURCES = utils.c utils.h +libtestutils_la_SOURCES = \ + utils.cpp \ + utils.h all-local: @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ diff --git a/tests/utils/testapp/gen-ns-events/Makefile.am b/tests/utils/testapp/gen-ns-events/Makefile.am index adc76ee52..5c1cddefe 100644 --- a/tests/utils/testapp/gen-ns-events/Makefile.am +++ b/tests/utils/testapp/gen-ns-events/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/tests/utils -I$(srcdir) \ -I$(top_srcdir)/tests/utils/testapp noinst_PROGRAMS = gen-ns-events -gen_ns_events_SOURCES = gen-ns-events.c +gen_ns_events_SOURCES = gen-ns-events.cpp gen_ns_events_LDADD = \ $(top_builddir)/tests/utils/libtestutils.la \ $(DL_LIBS) $(POPT_LIBS) diff --git a/tests/utils/testapp/gen-ns-events/gen-ns-events.c b/tests/utils/testapp/gen-ns-events/gen-ns-events.c deleted file mode 100644 index 7c2b6478c..000000000 --- a/tests/utils/testapp/gen-ns-events/gen-ns-events.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2019 Michael Jeanson - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "signal-helper.h" -#include "utils.h" - -#define LTTNG_PROC_NS_PATH_MAX 40 - -/* - * The runner of this test validates that the kernel supports the - * namespace for which it is invoked. However, these defines are added - * to allow tests to run on systems that support a given namespace, - * but that use a libc that doesn't define its associated clone flag. - */ -#ifndef CLONE_NEWNS -#define CLONE_NEWNS 0x00020000 -#endif -#ifndef CLONE_NEWCGROUP -#define CLONE_NEWCGROUP 0x02000000 -#endif -#ifndef CLONE_NEWUTS -#define CLONE_NEWUTS 0x04000000 -#endif -#ifndef CLONE_NEWIPC -#define CLONE_NEWIPC 0x08000000 -#endif -#ifndef CLONE_NEWUSER -#define CLONE_NEWUSER 0x10000000 -#endif -#ifndef CLONE_NEWPID -#define CLONE_NEWPID 0x20000000 -#endif -#ifndef CLONE_NEWNET -#define CLONE_NEWNET 0x40000000 -#endif -#ifndef CLONE_NEWTIME -#define CLONE_NEWTIME 0x00000080 -#endif - -static int debug = 0; -static char *ns_opt = NULL; -static char *before_unshare_wait_file_path = NULL; -static char *after_unshare_wait_file_path = NULL; -static char *after_unshare_signal_file_path = NULL; - -static struct poptOption opts[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "debug", 'd', POPT_ARG_NONE, &debug, 0, "Enable debug output", NULL }, - { "ns", 'n', POPT_ARG_STRING, &ns_opt, 0, "Namespace short identifier", NULL }, - { "before", 'b', POPT_ARG_STRING, &before_unshare_wait_file_path, 0, "Wait for file before unshare", NULL }, - { "after", 'a', POPT_ARG_STRING, &after_unshare_wait_file_path, 0, "Wait for file after unshare", NULL }, - { "signal", 's', POPT_ARG_STRING, &after_unshare_signal_file_path, 0, "Create signal file after unshare", NULL }, - POPT_AUTOHELP - { NULL, 0, 0, NULL, 0 } -}; - -static void debug_printf(const char *format, ...) -{ - va_list args; - va_start(args, format); - - if (debug) { - vfprintf(stderr, format, args); - } - - va_end(args); -} - -static int get_ns_inum(const char *ns, ino_t *ns_inum) -{ - int ret = 0; - struct stat sb; - char proc_ns_path[LTTNG_PROC_NS_PATH_MAX]; - - /* - * /proc/thread-self was introduced in kernel v3.17 - */ - if (snprintf(proc_ns_path, LTTNG_PROC_NS_PATH_MAX, - "/proc/thread-self/ns/%s", ns) >= 0) { - if (stat(proc_ns_path, &sb) == 0) { - *ns_inum = sb.st_ino; - } else { - ret = -1; - } - goto end; - } - - if (snprintf(proc_ns_path, LTTNG_PROC_NS_PATH_MAX, - "/proc/self/task/%d/%s/net", lttng_gettid(), ns) >= 0) { - if (stat(proc_ns_path, &sb) == 0) { - *ns_inum = sb.st_ino; - } else { - ret = -1; - } - goto end; - } -end: - return ret; -} - -static int do_the_needful(int ns_flag, const char *ns_str) -{ - int ret = 0; - ino_t ns1, ns2; - - ret = get_ns_inum(ns_str, &ns1); - if (ret) { - debug_printf("Failed to get ns inode number for namespace %s", - ns_str); - ret = -1; - goto end; - } - debug_printf("Initial %s ns inode number: %lu\n", ns_str, ns1); - - /* Wait on synchronization before unshare. */ - if (before_unshare_wait_file_path) { - ret = wait_on_file(before_unshare_wait_file_path); - if (ret != 0) { - goto end; - } - } - - ret = unshare(ns_flag); - if (ret == -1) { - perror("Failed to unshare namespace"); - goto end; - } - - ret = get_ns_inum(ns_str, &ns2); - if (ret) { - debug_printf("Failed to get ns inode number for namespace %s", - ns_str); - ret = -1; - goto end; - } - debug_printf("Post unshare %s ns inode number: %lu\n", ns_str, ns2); - - /* Signal that the unshare call is completed. */ - if (after_unshare_signal_file_path) { - ret = create_file(after_unshare_signal_file_path); - if (ret != 0) { - goto end; - } - } - - /* Wait on synchronization after unshare. */ - if (after_unshare_wait_file_path) { - ret = wait_on_file(after_unshare_wait_file_path); - if (ret != 0) { - goto end; - } - } - -end: - return ret; -} - -int main(int argc, const char **argv) -{ - int opt; - int ret = EXIT_SUCCESS; - poptContext pc; - - pc = poptGetContext(NULL, argc, argv, opts, 0); - poptReadDefaultConfig(pc, 0); - - if (argc < 2) { - poptPrintHelp(pc, stderr, 0); - ret = EXIT_FAILURE; - goto end; - } - - while ((opt = poptGetNextOpt(pc)) >= 0) { - switch (opt) { - default: - poptPrintUsage(pc, stderr, 0); - ret = EXIT_FAILURE; - goto end; - } - } - - if (opt < -1) { - /* an error occurred during option processing */ - poptPrintUsage(pc, stderr, 0); - fprintf(stderr, "%s: %s\n", - poptBadOption(pc, POPT_BADOPTION_NOALIAS), - poptStrerror(opt)); - ret = EXIT_FAILURE; - goto end; - } - - if (ns_opt == NULL) { - poptPrintUsage(pc, stderr, 0); - ret = EXIT_FAILURE; - goto end; - } - - if (set_signal_handler()) { - ret = EXIT_FAILURE; - goto end; - } - - if (strncmp(ns_opt, "cgroup", 6) == 0) { - ret = do_the_needful(CLONE_NEWCGROUP, "cgroup"); - } else if (strncmp(ns_opt, "ipc", 3) == 0) { - ret = do_the_needful(CLONE_NEWIPC, "ipc"); - } else if (strncmp(ns_opt, "mnt", 3) == 0) { - ret = do_the_needful(CLONE_NEWNS, "mnt"); - } else if (strncmp(ns_opt, "net", 3) == 0) { - ret = do_the_needful(CLONE_NEWNET, "net"); - } else if (strncmp(ns_opt, "pid", 3) == 0) { - ret = do_the_needful(CLONE_NEWPID, "pid"); - } else if (strncmp(ns_opt, "time", 4) == 0) { - ret = do_the_needful(CLONE_NEWTIME, "time"); - } else if (strncmp(ns_opt, "user", 4) == 0) { - /* - * Will always fail, requires a single threaded application, - * which can't happen with UST. - */ - ret = do_the_needful(CLONE_NEWUSER, "user"); - } else if (strncmp(ns_opt, "uts", 3) == 0) { - ret = do_the_needful(CLONE_NEWUTS, "uts"); - } else { - printf("invalid ns id\n"); - ret = EXIT_FAILURE; - goto end; - } - ret = ret ? EXIT_FAILURE : EXIT_SUCCESS; -end: - poptFreeContext(pc); - return ret; -} diff --git a/tests/utils/testapp/gen-ns-events/gen-ns-events.cpp b/tests/utils/testapp/gen-ns-events/gen-ns-events.cpp new file mode 100644 index 000000000..7c2b6478c --- /dev/null +++ b/tests/utils/testapp/gen-ns-events/gen-ns-events.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2019 Michael Jeanson + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "signal-helper.h" +#include "utils.h" + +#define LTTNG_PROC_NS_PATH_MAX 40 + +/* + * The runner of this test validates that the kernel supports the + * namespace for which it is invoked. However, these defines are added + * to allow tests to run on systems that support a given namespace, + * but that use a libc that doesn't define its associated clone flag. + */ +#ifndef CLONE_NEWNS +#define CLONE_NEWNS 0x00020000 +#endif +#ifndef CLONE_NEWCGROUP +#define CLONE_NEWCGROUP 0x02000000 +#endif +#ifndef CLONE_NEWUTS +#define CLONE_NEWUTS 0x04000000 +#endif +#ifndef CLONE_NEWIPC +#define CLONE_NEWIPC 0x08000000 +#endif +#ifndef CLONE_NEWUSER +#define CLONE_NEWUSER 0x10000000 +#endif +#ifndef CLONE_NEWPID +#define CLONE_NEWPID 0x20000000 +#endif +#ifndef CLONE_NEWNET +#define CLONE_NEWNET 0x40000000 +#endif +#ifndef CLONE_NEWTIME +#define CLONE_NEWTIME 0x00000080 +#endif + +static int debug = 0; +static char *ns_opt = NULL; +static char *before_unshare_wait_file_path = NULL; +static char *after_unshare_wait_file_path = NULL; +static char *after_unshare_signal_file_path = NULL; + +static struct poptOption opts[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "debug", 'd', POPT_ARG_NONE, &debug, 0, "Enable debug output", NULL }, + { "ns", 'n', POPT_ARG_STRING, &ns_opt, 0, "Namespace short identifier", NULL }, + { "before", 'b', POPT_ARG_STRING, &before_unshare_wait_file_path, 0, "Wait for file before unshare", NULL }, + { "after", 'a', POPT_ARG_STRING, &after_unshare_wait_file_path, 0, "Wait for file after unshare", NULL }, + { "signal", 's', POPT_ARG_STRING, &after_unshare_signal_file_path, 0, "Create signal file after unshare", NULL }, + POPT_AUTOHELP + { NULL, 0, 0, NULL, 0 } +}; + +static void debug_printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + + if (debug) { + vfprintf(stderr, format, args); + } + + va_end(args); +} + +static int get_ns_inum(const char *ns, ino_t *ns_inum) +{ + int ret = 0; + struct stat sb; + char proc_ns_path[LTTNG_PROC_NS_PATH_MAX]; + + /* + * /proc/thread-self was introduced in kernel v3.17 + */ + if (snprintf(proc_ns_path, LTTNG_PROC_NS_PATH_MAX, + "/proc/thread-self/ns/%s", ns) >= 0) { + if (stat(proc_ns_path, &sb) == 0) { + *ns_inum = sb.st_ino; + } else { + ret = -1; + } + goto end; + } + + if (snprintf(proc_ns_path, LTTNG_PROC_NS_PATH_MAX, + "/proc/self/task/%d/%s/net", lttng_gettid(), ns) >= 0) { + if (stat(proc_ns_path, &sb) == 0) { + *ns_inum = sb.st_ino; + } else { + ret = -1; + } + goto end; + } +end: + return ret; +} + +static int do_the_needful(int ns_flag, const char *ns_str) +{ + int ret = 0; + ino_t ns1, ns2; + + ret = get_ns_inum(ns_str, &ns1); + if (ret) { + debug_printf("Failed to get ns inode number for namespace %s", + ns_str); + ret = -1; + goto end; + } + debug_printf("Initial %s ns inode number: %lu\n", ns_str, ns1); + + /* Wait on synchronization before unshare. */ + if (before_unshare_wait_file_path) { + ret = wait_on_file(before_unshare_wait_file_path); + if (ret != 0) { + goto end; + } + } + + ret = unshare(ns_flag); + if (ret == -1) { + perror("Failed to unshare namespace"); + goto end; + } + + ret = get_ns_inum(ns_str, &ns2); + if (ret) { + debug_printf("Failed to get ns inode number for namespace %s", + ns_str); + ret = -1; + goto end; + } + debug_printf("Post unshare %s ns inode number: %lu\n", ns_str, ns2); + + /* Signal that the unshare call is completed. */ + if (after_unshare_signal_file_path) { + ret = create_file(after_unshare_signal_file_path); + if (ret != 0) { + goto end; + } + } + + /* Wait on synchronization after unshare. */ + if (after_unshare_wait_file_path) { + ret = wait_on_file(after_unshare_wait_file_path); + if (ret != 0) { + goto end; + } + } + +end: + return ret; +} + +int main(int argc, const char **argv) +{ + int opt; + int ret = EXIT_SUCCESS; + poptContext pc; + + pc = poptGetContext(NULL, argc, argv, opts, 0); + poptReadDefaultConfig(pc, 0); + + if (argc < 2) { + poptPrintHelp(pc, stderr, 0); + ret = EXIT_FAILURE; + goto end; + } + + while ((opt = poptGetNextOpt(pc)) >= 0) { + switch (opt) { + default: + poptPrintUsage(pc, stderr, 0); + ret = EXIT_FAILURE; + goto end; + } + } + + if (opt < -1) { + /* an error occurred during option processing */ + poptPrintUsage(pc, stderr, 0); + fprintf(stderr, "%s: %s\n", + poptBadOption(pc, POPT_BADOPTION_NOALIAS), + poptStrerror(opt)); + ret = EXIT_FAILURE; + goto end; + } + + if (ns_opt == NULL) { + poptPrintUsage(pc, stderr, 0); + ret = EXIT_FAILURE; + goto end; + } + + if (set_signal_handler()) { + ret = EXIT_FAILURE; + goto end; + } + + if (strncmp(ns_opt, "cgroup", 6) == 0) { + ret = do_the_needful(CLONE_NEWCGROUP, "cgroup"); + } else if (strncmp(ns_opt, "ipc", 3) == 0) { + ret = do_the_needful(CLONE_NEWIPC, "ipc"); + } else if (strncmp(ns_opt, "mnt", 3) == 0) { + ret = do_the_needful(CLONE_NEWNS, "mnt"); + } else if (strncmp(ns_opt, "net", 3) == 0) { + ret = do_the_needful(CLONE_NEWNET, "net"); + } else if (strncmp(ns_opt, "pid", 3) == 0) { + ret = do_the_needful(CLONE_NEWPID, "pid"); + } else if (strncmp(ns_opt, "time", 4) == 0) { + ret = do_the_needful(CLONE_NEWTIME, "time"); + } else if (strncmp(ns_opt, "user", 4) == 0) { + /* + * Will always fail, requires a single threaded application, + * which can't happen with UST. + */ + ret = do_the_needful(CLONE_NEWUSER, "user"); + } else if (strncmp(ns_opt, "uts", 3) == 0) { + ret = do_the_needful(CLONE_NEWUTS, "uts"); + } else { + printf("invalid ns id\n"); + ret = EXIT_FAILURE; + goto end; + } + ret = ret ? EXIT_FAILURE : EXIT_SUCCESS; +end: + poptFreeContext(pc); + return ret; +} diff --git a/tests/utils/testapp/gen-syscall-events/Makefile.am b/tests/utils/testapp/gen-syscall-events/Makefile.am index 3f3a77fa7..59ac2f3a9 100644 --- a/tests/utils/testapp/gen-syscall-events/Makefile.am +++ b/tests/utils/testapp/gen-syscall-events/Makefile.am @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only -AM_CFLAGS += -I$(top_srcdir)/tests/utils/ +AM_CPPFLAGS += -I$(top_srcdir)/tests/utils noinst_PROGRAMS = gen-syscall-events -gen_syscall_events_SOURCES = gen-syscall-events.c +gen_syscall_events_SOURCES = gen-syscall-events.cpp gen_syscall_events_LDADD = $(top_builddir)/tests/utils/libtestutils.la diff --git a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c deleted file mode 100644 index 6a246867d..000000000 --- a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2017 Francis Deslauriers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include - -#include "utils.h" - -#define MAX_LEN 16 - -/* - * The LTTng system call tracing facilities can't handle page faults at the - * moment. If a fault would occur while reading a syscall argument, the - * tracer will report an empty string (""). Since the proper execution of the - * tests which use this generator depends on some syscall string arguments being - * present, this util allows us to mitigate the page-fault risk. - * - * This isn't a proper fix; it is simply the best we can do for now. - * See bug #1261 for more context. - */ -static -void prefault_string(const char *p) -{ - const char * const end = p + strlen(p) + 1; - - while (p < end) { - /* - * Trigger a read attempt on *p, faulting-in the pages - * for reading. - */ - asm volatile("" : : "m"(*p)); - p += sysconf(_SC_PAGE_SIZE); - } -} - -static -int open_read_close(const char *path) -{ - int fd, ret; - char buf[MAX_LEN]; - - /* - * Start generating syscalls. We use syscall(2) to prevent libc from - * changing the underlying syscall (e.g. calling openat(2) instead of - * open(2)). - */ - prefault_string(path); - fd = syscall(SYS_openat, AT_FDCWD, path, O_RDONLY); - if (fd < 0) { - PERROR_NO_LOGGER("Failed to open file with openat(): path = '%s'", path); - ret = -1; - goto error; - } - - ret = syscall(SYS_read, fd, buf, MAX_LEN); - if (ret < 0) { - PERROR_NO_LOGGER("Failed to read file: path = '%s', fd = %d, length = %d", - path, fd, MAX_LEN); - ret = -1; - goto error; - } - - ret = syscall(SYS_close, fd); - if (ret == -1) { - PERROR_NO_LOGGER("Failed to close file: path = '%s', fd = %d", path, fd); - ret = -1; - goto error; - } - -error: - return ret; -} - -/* - * The process waits for the creation of a file passed as argument from an - * external processes to execute a syscall and exiting. This is useful for tests - * in combinaison with LTTng's PID tracker feature where we can trace the kernel - * events generated by our test process only. - */ -int main(int argc, char **argv) -{ - int ret; - const char *start_file, *path1, *path2; - - if (argc != 4) { - fprintf(stderr, "Error: Missing argument\n"); - fprintf(stderr, "USAGE: %s PATH_WAIT_FILE PATH1_TO_OPEN PATH2_TO_OPEN\n", argv[0]); - ret = -1; - goto error; - } - - start_file = argv[1]; - path1 = argv[2]; - path2 = argv[3]; - - /* - * Wait for the start_file to be created by an external process - * (typically the test script) before executing the syscalls. - */ - ret = wait_on_file(start_file); - if (ret != 0) { - goto error; - } - - /* - * Start generating syscalls. We use syscall(2) to prevent libc to change - * the underlying syscall. e.g. calling openat(2) instead of open(2). - */ - ret = open_read_close(path1); - if (ret == -1) { - ret = -1; - goto error; - } - - ret = open_read_close(path2); - if (ret == -1) { - ret = -1; - goto error; - } - -error: - return ret; -} diff --git a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.cpp b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.cpp new file mode 100644 index 000000000..6a246867d --- /dev/null +++ b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2017 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +#define MAX_LEN 16 + +/* + * The LTTng system call tracing facilities can't handle page faults at the + * moment. If a fault would occur while reading a syscall argument, the + * tracer will report an empty string (""). Since the proper execution of the + * tests which use this generator depends on some syscall string arguments being + * present, this util allows us to mitigate the page-fault risk. + * + * This isn't a proper fix; it is simply the best we can do for now. + * See bug #1261 for more context. + */ +static +void prefault_string(const char *p) +{ + const char * const end = p + strlen(p) + 1; + + while (p < end) { + /* + * Trigger a read attempt on *p, faulting-in the pages + * for reading. + */ + asm volatile("" : : "m"(*p)); + p += sysconf(_SC_PAGE_SIZE); + } +} + +static +int open_read_close(const char *path) +{ + int fd, ret; + char buf[MAX_LEN]; + + /* + * Start generating syscalls. We use syscall(2) to prevent libc from + * changing the underlying syscall (e.g. calling openat(2) instead of + * open(2)). + */ + prefault_string(path); + fd = syscall(SYS_openat, AT_FDCWD, path, O_RDONLY); + if (fd < 0) { + PERROR_NO_LOGGER("Failed to open file with openat(): path = '%s'", path); + ret = -1; + goto error; + } + + ret = syscall(SYS_read, fd, buf, MAX_LEN); + if (ret < 0) { + PERROR_NO_LOGGER("Failed to read file: path = '%s', fd = %d, length = %d", + path, fd, MAX_LEN); + ret = -1; + goto error; + } + + ret = syscall(SYS_close, fd); + if (ret == -1) { + PERROR_NO_LOGGER("Failed to close file: path = '%s', fd = %d", path, fd); + ret = -1; + goto error; + } + +error: + return ret; +} + +/* + * The process waits for the creation of a file passed as argument from an + * external processes to execute a syscall and exiting. This is useful for tests + * in combinaison with LTTng's PID tracker feature where we can trace the kernel + * events generated by our test process only. + */ +int main(int argc, char **argv) +{ + int ret; + const char *start_file, *path1, *path2; + + if (argc != 4) { + fprintf(stderr, "Error: Missing argument\n"); + fprintf(stderr, "USAGE: %s PATH_WAIT_FILE PATH1_TO_OPEN PATH2_TO_OPEN\n", argv[0]); + ret = -1; + goto error; + } + + start_file = argv[1]; + path1 = argv[2]; + path2 = argv[3]; + + /* + * Wait for the start_file to be created by an external process + * (typically the test script) before executing the syscalls. + */ + ret = wait_on_file(start_file); + if (ret != 0) { + goto error; + } + + /* + * Start generating syscalls. We use syscall(2) to prevent libc to change + * the underlying syscall. e.g. calling openat(2) instead of open(2). + */ + ret = open_read_close(path1); + if (ret == -1) { + ret = -1; + goto error; + } + + ret = open_read_close(path2); + if (ret == -1) { + ret = -1; + goto error; + } + +error: + return ret; +} diff --git a/tests/utils/testapp/gen-ust-events-ns/Makefile.am b/tests/utils/testapp/gen-ust-events-ns/Makefile.am index 35e6d6700..2e3ad0747 100644 --- a/tests/utils/testapp/gen-ust-events-ns/Makefile.am +++ b/tests/utils/testapp/gen-ust-events-ns/Makefile.am @@ -5,7 +5,10 @@ AM_CPPFLAGS += -I$(top_srcdir)/tests/utils -I$(srcdir) \ if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS = gen-ust-events-ns -gen_ust_events_ns_SOURCES = gen-ust-events-ns.c tp.c tp.h +gen_ust_events_ns_SOURCES = \ + gen-ust-events-ns.cpp \ + tp.c \ + tp.h gen_ust_events_ns_LDADD = $(UST_LIBS) -llttng-ust-fork \ $(top_builddir)/tests/utils/libtestutils.la \ $(DL_LIBS) $(POPT_LIBS) diff --git a/tests/utils/testapp/gen-ust-events-ns/gen-ust-events-ns.c b/tests/utils/testapp/gen-ust-events-ns/gen-ust-events-ns.c deleted file mode 100644 index dadfc3d3a..000000000 --- a/tests/utils/testapp/gen-ust-events-ns/gen-ust-events-ns.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2019 Michael Jeanson - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "signal-helper.h" -#include "utils.h" - -#define TRACEPOINT_DEFINE -#include "tp.h" - -#define LTTNG_PROC_NS_PATH_MAX 40 - -/* - * The runner of this test validates that the kernel supports the - * namespace for which it is invoked. However, these defines are added - * to allow tests to run on systems that support a given namespace, - * but that use a libc that doesn't define its associated clone flag. - */ -#ifndef CLONE_NEWNS -#define CLONE_NEWNS 0x00020000 -#endif -#ifndef CLONE_NEWCGROUP -#define CLONE_NEWCGROUP 0x02000000 -#endif -#ifndef CLONE_NEWUTS -#define CLONE_NEWUTS 0x04000000 -#endif -#ifndef CLONE_NEWIPC -#define CLONE_NEWIPC 0x08000000 -#endif -#ifndef CLONE_NEWUSER -#define CLONE_NEWUSER 0x10000000 -#endif -#ifndef CLONE_NEWPID -#define CLONE_NEWPID 0x20000000 -#endif -#ifndef CLONE_NEWNET -#define CLONE_NEWNET 0x40000000 -#endif -#ifndef CLONE_NEWTIME -#define CLONE_NEWTIME 0x00000080 -#endif - -static int nr_iter = 100; -static int debug = 0; -static char *ns_opt = NULL; -static char *after_unshare_file_path = NULL; -static char *before_second_event_file_path = NULL; - -static -struct poptOption opts[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "debug", 'd', POPT_ARG_NONE, &debug, 0, "Enable debug output", NULL }, - { "ns", 'n', POPT_ARG_STRING, &ns_opt, 0, "Namespace short identifier", NULL }, - { "iter", 'i', POPT_ARG_INT, &nr_iter, 0, "Number of tracepoint iterations", NULL }, - { "after", 'a', POPT_ARG_STRING, &after_unshare_file_path, 0, "after_unshare_file_path,", NULL }, - { "before", 'b', POPT_ARG_STRING, &before_second_event_file_path, 0, "before_second_event_file_path,", NULL }, - POPT_AUTOHELP - { NULL, 0, 0, NULL, 0 } -}; - -static void debug_printf(const char *format, ...) -{ - va_list args; - va_start(args, format); - - if (debug) { - vfprintf(stderr, format, args); - } - - va_end(args); -} - -static int get_ns_inum(const char *ns, ino_t *ns_inum) -{ - int ret = -1; - struct stat sb; - char proc_ns_path[LTTNG_PROC_NS_PATH_MAX]; - - /* - * /proc/thread-self was introduced in kernel v3.17 - */ - if (snprintf(proc_ns_path, LTTNG_PROC_NS_PATH_MAX, - "/proc/thread-self/ns/%s", ns) >= 0) { - if (stat(proc_ns_path, &sb) == 0) { - *ns_inum = sb.st_ino; - ret = 0; - } - goto end; - } - - if (snprintf(proc_ns_path, LTTNG_PROC_NS_PATH_MAX, - "/proc/self/task/%d/%s/net", lttng_gettid(), ns) >= 0) { - if (stat(proc_ns_path, &sb) == 0) { - *ns_inum = sb.st_ino; - ret = 0; - } - goto end; - } -end: - return ret; -} - -static int do_the_needful(int ns_flag, const char *ns_str) -{ - int ret = 0, i; - ino_t ns1, ns2; - - ret = get_ns_inum(ns_str, &ns1); - if (ret) { - debug_printf("Failed to get ns inode number for namespace %s", - ns_str); - ret = -1; - goto end; - } - debug_printf("Initial %s ns inode number: %lu\n", ns_str, ns1); - - for (i = 0; nr_iter < 0 || i < nr_iter; i++) { - tracepoint(tp, tptest, ns1); - if (should_quit) { - break; - } - } - - ret = unshare(ns_flag); - if (ret == -1) { - perror("Failed to unshare namespace"); - goto end; - } - - ret = get_ns_inum(ns_str, &ns2); - if (ret) { - debug_printf("Failed to get ns inode number for namespace %s", - ns_str); - ret = -1; - goto end; - } - debug_printf("Post unshare %s ns inode number: %lu\n", ns_str, ns2); - - /* - * Signal that we emited the first event group and that the - * unshare call is completed. - */ - if (after_unshare_file_path) { - ret = create_file(after_unshare_file_path); - if (ret != 0) { - goto end; - } - } - - /* Wait on synchronization before writing second event group. */ - if (before_second_event_file_path) { - ret = wait_on_file(before_second_event_file_path); - if (ret != 0) { - goto end; - } - } - - for (i = 0; nr_iter < 0 || i < nr_iter; i++) { - tracepoint(tp, tptest, ns2); - if (should_quit) { - break; - } - } - -end: - return ret; -} - -/* - * Send X events, change NS, wait for file to sync with test script, send X - * events in new NS - */ -int main(int argc, const char **argv) -{ - int opt; - int ret = EXIT_SUCCESS; - poptContext pc; - - pc = poptGetContext(NULL, argc, argv, opts, 0); - poptReadDefaultConfig(pc, 0); - - if (argc < 2) { - poptPrintHelp(pc, stderr, 0); - ret = EXIT_FAILURE; - goto end; - } - - while ((opt = poptGetNextOpt(pc)) >= 0) { - switch (opt) { - default: - poptPrintUsage(pc, stderr, 0); - ret = EXIT_FAILURE; - goto end; - } - } - - if (opt < -1) { - /* An error occurred during option processing. */ - poptPrintUsage(pc, stderr, 0); - fprintf(stderr, "%s: %s\n", - poptBadOption(pc, POPT_BADOPTION_NOALIAS), - poptStrerror(opt)); - ret = EXIT_FAILURE; - goto end; - } - - if (ns_opt == NULL) { - poptPrintUsage(pc, stderr, 0); - ret = EXIT_FAILURE; - goto end; - } - - if (set_signal_handler()) { - ret = EXIT_FAILURE; - goto end; - } - - if (strncmp(ns_opt, "cgroup", 6) == 0) { - ret = do_the_needful(CLONE_NEWCGROUP, "cgroup"); - } else if (strncmp(ns_opt, "ipc", 3) == 0) { - ret = do_the_needful(CLONE_NEWIPC, "ipc"); - } else if (strncmp(ns_opt, "mnt", 3) == 0) { - ret = do_the_needful(CLONE_NEWNS, "mnt"); - } else if (strncmp(ns_opt, "net", 3) == 0) { - ret = do_the_needful(CLONE_NEWNET, "net"); - } else if (strncmp(ns_opt, "pid", 3) == 0) { - ret = do_the_needful(CLONE_NEWPID, "pid"); - } else if (strncmp(ns_opt, "time", 4) == 0) { - ret = do_the_needful(CLONE_NEWTIME, "time"); - } else if (strncmp(ns_opt, "user", 4) == 0) { - /* - * Will always fail, requires a single threaded application, - * which can't happen with UST. - */ - ret = do_the_needful(CLONE_NEWUSER, "user"); - } else if (strncmp(ns_opt, "uts", 3) == 0) { - ret = do_the_needful(CLONE_NEWUTS, "uts"); - } else { - printf("invalid ns id\n"); - ret = EXIT_FAILURE; - goto end; - } - ret = ret ? EXIT_FAILURE : EXIT_SUCCESS; -end: - poptFreeContext(pc); - return ret; -} diff --git a/tests/utils/testapp/gen-ust-events-ns/gen-ust-events-ns.cpp b/tests/utils/testapp/gen-ust-events-ns/gen-ust-events-ns.cpp new file mode 100644 index 000000000..dadfc3d3a --- /dev/null +++ b/tests/utils/testapp/gen-ust-events-ns/gen-ust-events-ns.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2019 Michael Jeanson + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "signal-helper.h" +#include "utils.h" + +#define TRACEPOINT_DEFINE +#include "tp.h" + +#define LTTNG_PROC_NS_PATH_MAX 40 + +/* + * The runner of this test validates that the kernel supports the + * namespace for which it is invoked. However, these defines are added + * to allow tests to run on systems that support a given namespace, + * but that use a libc that doesn't define its associated clone flag. + */ +#ifndef CLONE_NEWNS +#define CLONE_NEWNS 0x00020000 +#endif +#ifndef CLONE_NEWCGROUP +#define CLONE_NEWCGROUP 0x02000000 +#endif +#ifndef CLONE_NEWUTS +#define CLONE_NEWUTS 0x04000000 +#endif +#ifndef CLONE_NEWIPC +#define CLONE_NEWIPC 0x08000000 +#endif +#ifndef CLONE_NEWUSER +#define CLONE_NEWUSER 0x10000000 +#endif +#ifndef CLONE_NEWPID +#define CLONE_NEWPID 0x20000000 +#endif +#ifndef CLONE_NEWNET +#define CLONE_NEWNET 0x40000000 +#endif +#ifndef CLONE_NEWTIME +#define CLONE_NEWTIME 0x00000080 +#endif + +static int nr_iter = 100; +static int debug = 0; +static char *ns_opt = NULL; +static char *after_unshare_file_path = NULL; +static char *before_second_event_file_path = NULL; + +static +struct poptOption opts[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "debug", 'd', POPT_ARG_NONE, &debug, 0, "Enable debug output", NULL }, + { "ns", 'n', POPT_ARG_STRING, &ns_opt, 0, "Namespace short identifier", NULL }, + { "iter", 'i', POPT_ARG_INT, &nr_iter, 0, "Number of tracepoint iterations", NULL }, + { "after", 'a', POPT_ARG_STRING, &after_unshare_file_path, 0, "after_unshare_file_path,", NULL }, + { "before", 'b', POPT_ARG_STRING, &before_second_event_file_path, 0, "before_second_event_file_path,", NULL }, + POPT_AUTOHELP + { NULL, 0, 0, NULL, 0 } +}; + +static void debug_printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + + if (debug) { + vfprintf(stderr, format, args); + } + + va_end(args); +} + +static int get_ns_inum(const char *ns, ino_t *ns_inum) +{ + int ret = -1; + struct stat sb; + char proc_ns_path[LTTNG_PROC_NS_PATH_MAX]; + + /* + * /proc/thread-self was introduced in kernel v3.17 + */ + if (snprintf(proc_ns_path, LTTNG_PROC_NS_PATH_MAX, + "/proc/thread-self/ns/%s", ns) >= 0) { + if (stat(proc_ns_path, &sb) == 0) { + *ns_inum = sb.st_ino; + ret = 0; + } + goto end; + } + + if (snprintf(proc_ns_path, LTTNG_PROC_NS_PATH_MAX, + "/proc/self/task/%d/%s/net", lttng_gettid(), ns) >= 0) { + if (stat(proc_ns_path, &sb) == 0) { + *ns_inum = sb.st_ino; + ret = 0; + } + goto end; + } +end: + return ret; +} + +static int do_the_needful(int ns_flag, const char *ns_str) +{ + int ret = 0, i; + ino_t ns1, ns2; + + ret = get_ns_inum(ns_str, &ns1); + if (ret) { + debug_printf("Failed to get ns inode number for namespace %s", + ns_str); + ret = -1; + goto end; + } + debug_printf("Initial %s ns inode number: %lu\n", ns_str, ns1); + + for (i = 0; nr_iter < 0 || i < nr_iter; i++) { + tracepoint(tp, tptest, ns1); + if (should_quit) { + break; + } + } + + ret = unshare(ns_flag); + if (ret == -1) { + perror("Failed to unshare namespace"); + goto end; + } + + ret = get_ns_inum(ns_str, &ns2); + if (ret) { + debug_printf("Failed to get ns inode number for namespace %s", + ns_str); + ret = -1; + goto end; + } + debug_printf("Post unshare %s ns inode number: %lu\n", ns_str, ns2); + + /* + * Signal that we emited the first event group and that the + * unshare call is completed. + */ + if (after_unshare_file_path) { + ret = create_file(after_unshare_file_path); + if (ret != 0) { + goto end; + } + } + + /* Wait on synchronization before writing second event group. */ + if (before_second_event_file_path) { + ret = wait_on_file(before_second_event_file_path); + if (ret != 0) { + goto end; + } + } + + for (i = 0; nr_iter < 0 || i < nr_iter; i++) { + tracepoint(tp, tptest, ns2); + if (should_quit) { + break; + } + } + +end: + return ret; +} + +/* + * Send X events, change NS, wait for file to sync with test script, send X + * events in new NS + */ +int main(int argc, const char **argv) +{ + int opt; + int ret = EXIT_SUCCESS; + poptContext pc; + + pc = poptGetContext(NULL, argc, argv, opts, 0); + poptReadDefaultConfig(pc, 0); + + if (argc < 2) { + poptPrintHelp(pc, stderr, 0); + ret = EXIT_FAILURE; + goto end; + } + + while ((opt = poptGetNextOpt(pc)) >= 0) { + switch (opt) { + default: + poptPrintUsage(pc, stderr, 0); + ret = EXIT_FAILURE; + goto end; + } + } + + if (opt < -1) { + /* An error occurred during option processing. */ + poptPrintUsage(pc, stderr, 0); + fprintf(stderr, "%s: %s\n", + poptBadOption(pc, POPT_BADOPTION_NOALIAS), + poptStrerror(opt)); + ret = EXIT_FAILURE; + goto end; + } + + if (ns_opt == NULL) { + poptPrintUsage(pc, stderr, 0); + ret = EXIT_FAILURE; + goto end; + } + + if (set_signal_handler()) { + ret = EXIT_FAILURE; + goto end; + } + + if (strncmp(ns_opt, "cgroup", 6) == 0) { + ret = do_the_needful(CLONE_NEWCGROUP, "cgroup"); + } else if (strncmp(ns_opt, "ipc", 3) == 0) { + ret = do_the_needful(CLONE_NEWIPC, "ipc"); + } else if (strncmp(ns_opt, "mnt", 3) == 0) { + ret = do_the_needful(CLONE_NEWNS, "mnt"); + } else if (strncmp(ns_opt, "net", 3) == 0) { + ret = do_the_needful(CLONE_NEWNET, "net"); + } else if (strncmp(ns_opt, "pid", 3) == 0) { + ret = do_the_needful(CLONE_NEWPID, "pid"); + } else if (strncmp(ns_opt, "time", 4) == 0) { + ret = do_the_needful(CLONE_NEWTIME, "time"); + } else if (strncmp(ns_opt, "user", 4) == 0) { + /* + * Will always fail, requires a single threaded application, + * which can't happen with UST. + */ + ret = do_the_needful(CLONE_NEWUSER, "user"); + } else if (strncmp(ns_opt, "uts", 3) == 0) { + ret = do_the_needful(CLONE_NEWUTS, "uts"); + } else { + printf("invalid ns id\n"); + ret = EXIT_FAILURE; + goto end; + } + ret = ret ? EXIT_FAILURE : EXIT_SUCCESS; +end: + poptFreeContext(pc); + return ret; +} diff --git a/tests/utils/testapp/gen-ust-events/Makefile.am b/tests/utils/testapp/gen-ust-events/Makefile.am index f377de58b..c05c665a1 100644 --- a/tests/utils/testapp/gen-ust-events/Makefile.am +++ b/tests/utils/testapp/gen-ust-events/Makefile.am @@ -5,7 +5,10 @@ AM_CPPFLAGS += -I$(top_srcdir)/tests/utils -I$(srcdir) \ if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS = gen-ust-events -gen_ust_events_SOURCES = gen-ust-events.c tp.c tp.h +gen_ust_events_SOURCES = \ + gen-ust-events.cpp \ + tp.c \ + tp.h gen_ust_events_LDADD = $(UST_LIBS) \ $(top_builddir)/tests/utils/libtestutils.la \ $(DL_LIBS) diff --git a/tests/utils/testapp/gen-ust-events/gen-ust-events.c b/tests/utils/testapp/gen-ust-events/gen-ust-events.c deleted file mode 100644 index cf8819977..000000000 --- a/tests/utils/testapp/gen-ust-events/gen-ust-events.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2012 David Goulet - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "utils.h" -#include "signal-helper.h" - -#define TRACEPOINT_DEFINE -#include "tp.h" - -static struct option long_options[] = -{ - /* These options set a flag. */ - {"iter", required_argument, 0, 'i'}, - {"wait", required_argument, 0, 'w'}, - {"sync-after-first-event", required_argument, 0, 'a'}, - {"sync-before-last-event", required_argument, 0, 'b'}, - {"sync-before-last-event-touch", required_argument, 0, 'c'}, - {"sync-before-exit", required_argument, 0, 'd'}, - {"sync-before-exit-touch", required_argument, 0, 'e'}, - {"emit-end-event", no_argument, 0, 'f'}, - {0, 0, 0, 0} -}; - -int main(int argc, char **argv) -{ - unsigned int i, netint; - int option_index; - int option; - long values[] = { 1, 2, 3 }; - char text[10] = "test"; - char escape[10] = "\\*"; - double dbl = 2.0; - float flt = 2222.0; - uint32_t net_values[] = { 1, 2, 3 }; - int nr_iter = 100, ret = 0, first_event_file_created = 0; - useconds_t nr_usec = 0; - char *after_first_event_file_path = NULL; - char *before_last_event_file_path = NULL; - /* - * Touch a file to indicate that all events except one were - * generated. - */ - char *before_last_event_file_path_touch = NULL; - /* Touch file when we are exiting */ - char *before_exit_file_path_touch = NULL; - /* Wait on file before exiting */ - char *before_exit_file_path = NULL; - /* Emit an end event */ - bool emit_end_event = false; - - for (i = 0; i < 3; i++) { - net_values[i] = htonl(net_values[i]); - } - - while ((option = getopt_long(argc, argv, "i:w:a:b:c:d:e:f", - long_options, &option_index)) != -1) { - switch (option) { - case 'a': - after_first_event_file_path = strdup(optarg); - break; - case 'b': - before_last_event_file_path = strdup(optarg); - break; - case 'c': - before_last_event_file_path_touch = strdup(optarg); - break; - case 'd': - before_exit_file_path = strdup(optarg); - break; - case 'e': - before_exit_file_path_touch = strdup(optarg); - break; - case 'f': - emit_end_event = true; - break; - case 'i': - nr_iter = atoi(optarg); - break; - case 'w': - nr_usec = atoi(optarg); - break; - case '?': - /* getopt_long already printed an error message. */ - default: - ret = -1; - goto end; - } - } - - if (optind != argc) { - fprintf(stderr, "Error: takes long options only.\n"); - - /* - * Aborting the test program for now because callers typically don't check - * the test program return value, and the transition from positional - * arguments to getopt causes hangs when caller scripts are not updated. - * An abort is easier to diagnose and fix. This is a temporary solution: - * we should eventually ensure that all scripts test and report the test - * app return values. - */ - abort(); - - ret = -1; - goto end; - } - - - if (set_signal_handler()) { - ret = -1; - goto end; - } - - for (i = 0; nr_iter < 0 || i < nr_iter; i++) { - if (nr_iter >= 0 && i == nr_iter - 1) { - if (before_last_event_file_path_touch) { - ret = create_file(before_last_event_file_path_touch); - if (ret != 0) { - goto end; - } - } - - /* - * Wait on synchronization before writing last - * event. - */ - if (before_last_event_file_path) { - ret = wait_on_file(before_last_event_file_path); - if (ret != 0) { - goto end; - } - } - } - netint = htonl(i); - tracepoint(tp, tptest, i, netint, values, text, - strlen(text), escape, net_values, dbl, flt); - - /* - * First loop we create the file if asked to indicate - * that at least one tracepoint has been hit. - */ - if (after_first_event_file_path && first_event_file_created == 0) { - ret = create_file(after_first_event_file_path); - - if (ret != 0) { - goto end; - } else { - first_event_file_created = 1; - } - } - - if (nr_usec) { - if (usleep_safe(nr_usec)) { - ret = -1; - goto end; - } - } - if (should_quit) { - break; - } - } - - if (emit_end_event) { - tracepoint(tp, end); - } - - if (before_exit_file_path_touch) { - ret = create_file(before_exit_file_path_touch); - if (ret != 0) { - goto end; - } - } - if (before_exit_file_path) { - ret = wait_on_file(before_exit_file_path); - if (ret != 0) { - goto end; - } - } -end: - free(after_first_event_file_path); - free(before_last_event_file_path); - free(before_last_event_file_path_touch); - free(before_exit_file_path); - free(before_exit_file_path_touch); - exit(!ret ? EXIT_SUCCESS : EXIT_FAILURE); -} diff --git a/tests/utils/testapp/gen-ust-events/gen-ust-events.cpp b/tests/utils/testapp/gen-ust-events/gen-ust-events.cpp new file mode 100644 index 000000000..cf8819977 --- /dev/null +++ b/tests/utils/testapp/gen-ust-events/gen-ust-events.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2012 David Goulet + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" +#include "signal-helper.h" + +#define TRACEPOINT_DEFINE +#include "tp.h" + +static struct option long_options[] = +{ + /* These options set a flag. */ + {"iter", required_argument, 0, 'i'}, + {"wait", required_argument, 0, 'w'}, + {"sync-after-first-event", required_argument, 0, 'a'}, + {"sync-before-last-event", required_argument, 0, 'b'}, + {"sync-before-last-event-touch", required_argument, 0, 'c'}, + {"sync-before-exit", required_argument, 0, 'd'}, + {"sync-before-exit-touch", required_argument, 0, 'e'}, + {"emit-end-event", no_argument, 0, 'f'}, + {0, 0, 0, 0} +}; + +int main(int argc, char **argv) +{ + unsigned int i, netint; + int option_index; + int option; + long values[] = { 1, 2, 3 }; + char text[10] = "test"; + char escape[10] = "\\*"; + double dbl = 2.0; + float flt = 2222.0; + uint32_t net_values[] = { 1, 2, 3 }; + int nr_iter = 100, ret = 0, first_event_file_created = 0; + useconds_t nr_usec = 0; + char *after_first_event_file_path = NULL; + char *before_last_event_file_path = NULL; + /* + * Touch a file to indicate that all events except one were + * generated. + */ + char *before_last_event_file_path_touch = NULL; + /* Touch file when we are exiting */ + char *before_exit_file_path_touch = NULL; + /* Wait on file before exiting */ + char *before_exit_file_path = NULL; + /* Emit an end event */ + bool emit_end_event = false; + + for (i = 0; i < 3; i++) { + net_values[i] = htonl(net_values[i]); + } + + while ((option = getopt_long(argc, argv, "i:w:a:b:c:d:e:f", + long_options, &option_index)) != -1) { + switch (option) { + case 'a': + after_first_event_file_path = strdup(optarg); + break; + case 'b': + before_last_event_file_path = strdup(optarg); + break; + case 'c': + before_last_event_file_path_touch = strdup(optarg); + break; + case 'd': + before_exit_file_path = strdup(optarg); + break; + case 'e': + before_exit_file_path_touch = strdup(optarg); + break; + case 'f': + emit_end_event = true; + break; + case 'i': + nr_iter = atoi(optarg); + break; + case 'w': + nr_usec = atoi(optarg); + break; + case '?': + /* getopt_long already printed an error message. */ + default: + ret = -1; + goto end; + } + } + + if (optind != argc) { + fprintf(stderr, "Error: takes long options only.\n"); + + /* + * Aborting the test program for now because callers typically don't check + * the test program return value, and the transition from positional + * arguments to getopt causes hangs when caller scripts are not updated. + * An abort is easier to diagnose and fix. This is a temporary solution: + * we should eventually ensure that all scripts test and report the test + * app return values. + */ + abort(); + + ret = -1; + goto end; + } + + + if (set_signal_handler()) { + ret = -1; + goto end; + } + + for (i = 0; nr_iter < 0 || i < nr_iter; i++) { + if (nr_iter >= 0 && i == nr_iter - 1) { + if (before_last_event_file_path_touch) { + ret = create_file(before_last_event_file_path_touch); + if (ret != 0) { + goto end; + } + } + + /* + * Wait on synchronization before writing last + * event. + */ + if (before_last_event_file_path) { + ret = wait_on_file(before_last_event_file_path); + if (ret != 0) { + goto end; + } + } + } + netint = htonl(i); + tracepoint(tp, tptest, i, netint, values, text, + strlen(text), escape, net_values, dbl, flt); + + /* + * First loop we create the file if asked to indicate + * that at least one tracepoint has been hit. + */ + if (after_first_event_file_path && first_event_file_created == 0) { + ret = create_file(after_first_event_file_path); + + if (ret != 0) { + goto end; + } else { + first_event_file_created = 1; + } + } + + if (nr_usec) { + if (usleep_safe(nr_usec)) { + ret = -1; + goto end; + } + } + if (should_quit) { + break; + } + } + + if (emit_end_event) { + tracepoint(tp, end); + } + + if (before_exit_file_path_touch) { + ret = create_file(before_exit_file_path_touch); + if (ret != 0) { + goto end; + } + } + if (before_exit_file_path) { + ret = wait_on_file(before_exit_file_path); + if (ret != 0) { + goto end; + } + } +end: + free(after_first_event_file_path); + free(before_last_event_file_path); + free(before_last_event_file_path_touch); + free(before_exit_file_path); + free(before_exit_file_path_touch); + exit(!ret ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/tests/utils/testapp/gen-ust-nevents-str/Makefile.am b/tests/utils/testapp/gen-ust-nevents-str/Makefile.am index 7330f1fb9..955200c2b 100644 --- a/tests/utils/testapp/gen-ust-nevents-str/Makefile.am +++ b/tests/utils/testapp/gen-ust-nevents-str/Makefile.am @@ -5,7 +5,10 @@ AM_CPPFLAGS += -I$(srcdir) -I$(top_srcdir)/tests/utils \ if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS = gen-ust-nevents-str -gen_ust_nevents_str_SOURCES = gen-ust-nevents-str.c tp.c tp.h +gen_ust_nevents_str_SOURCES = \ + gen-ust-nevents-str.cpp \ + tp.c \ + tp.h gen_ust_nevents_str_LDADD = $(UST_LIBS) \ $(top_builddir)/tests/utils/libtestutils.la \ $(DL_LIBS) diff --git a/tests/utils/testapp/gen-ust-nevents-str/gen-ust-nevents-str.c b/tests/utils/testapp/gen-ust-nevents-str/gen-ust-nevents-str.c deleted file mode 100644 index d68501bee..000000000 --- a/tests/utils/testapp/gen-ust-nevents-str/gen-ust-nevents-str.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2017 Philippe Proulx - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#define _LGPL_SOURCE -#include -#include "signal-helper.h" - -#define TRACEPOINT_DEFINE -#include "tp.h" - -int main(int argc, char **argv) -{ - int count = 0, i = 0, arg_i = 0; - - if (set_signal_handler()) { - return 1; - } - - if (argc <= 3) { - fprintf(stderr, "Usage: %s COUNT STRING [STRING]...\n", - argv[0]); - return 1; - } - - if (argc >= 2) { - count = atoi(argv[1]); - } - - if (count < 0) { - return 0; - } - - for (i = 0, arg_i = 2; i < count; i++) { - tracepoint(tp, the_string, i, arg_i, argv[arg_i]); - - arg_i++; - if (arg_i == argc) { - arg_i = 2; - } - if (should_quit) { - break; - } - } - - return 0; -} diff --git a/tests/utils/testapp/gen-ust-nevents-str/gen-ust-nevents-str.cpp b/tests/utils/testapp/gen-ust-nevents-str/gen-ust-nevents-str.cpp new file mode 100644 index 000000000..d68501bee --- /dev/null +++ b/tests/utils/testapp/gen-ust-nevents-str/gen-ust-nevents-str.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include +#include "signal-helper.h" + +#define TRACEPOINT_DEFINE +#include "tp.h" + +int main(int argc, char **argv) +{ + int count = 0, i = 0, arg_i = 0; + + if (set_signal_handler()) { + return 1; + } + + if (argc <= 3) { + fprintf(stderr, "Usage: %s COUNT STRING [STRING]...\n", + argv[0]); + return 1; + } + + if (argc >= 2) { + count = atoi(argv[1]); + } + + if (count < 0) { + return 0; + } + + for (i = 0, arg_i = 2; i < count; i++) { + tracepoint(tp, the_string, i, arg_i, argv[arg_i]); + + arg_i++; + if (arg_i == argc) { + arg_i = 2; + } + if (should_quit) { + break; + } + } + + return 0; +} diff --git a/tests/utils/testapp/gen-ust-nevents/Makefile.am b/tests/utils/testapp/gen-ust-nevents/Makefile.am index 0ddbd9d39..cb869715d 100644 --- a/tests/utils/testapp/gen-ust-nevents/Makefile.am +++ b/tests/utils/testapp/gen-ust-nevents/Makefile.am @@ -5,7 +5,10 @@ AM_CPPFLAGS += -I$(srcdir) -I$(top_srcdir)/tests/utils \ if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS = gen-ust-nevents -gen_ust_nevents_SOURCES = gen-ust-nevents.c tp.c tp.h +gen_ust_nevents_SOURCES = \ + gen-ust-nevents.cpp \ + tp.c \ + tp.h gen_ust_nevents_LDADD = $(UST_LIBS) \ $(top_builddir)/tests/utils/libtestutils.la \ $(DL_LIBS) diff --git a/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c b/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c deleted file mode 100644 index ec71023de..000000000 --- a/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2012 David Goulet - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "utils.h" -#include "signal-helper.h" - -#define TRACEPOINT_DEFINE -#include "tp.h" - -static struct option long_options[] = -{ - /* These options set a flag. */ - {"iter", required_argument, 0, 'i'}, - {"wait", required_argument, 0, 'w'}, - {"create-in-main", required_argument, 0, 'm'}, - {"wait-before-first-event", required_argument, 0, 'b'}, - {0, 0, 0, 0} -}; - -int main(int argc, char **argv) -{ - int i, netint, ret = 0, option_index, option; - long values[] = { 1, 2, 3 }; - char text[10] = "test"; - double dbl = 2.0; - float flt = 2222.0; - unsigned int nr_iter = 100; - useconds_t nr_usec = 0; - char *wait_before_first_event_file_path = NULL; - char *create_in_main_file_path = NULL; - - while ((option = getopt_long(argc, argv, "i:w:b:m:", - long_options, &option_index)) != -1) { - switch (option) { - case 'b': - wait_before_first_event_file_path = strdup(optarg); - break; - case 'm': - create_in_main_file_path = strdup(optarg); - break; - case 'i': - nr_iter = atoi(optarg); - break; - case 'w': - nr_usec = atoi(optarg); - break; - case '?': - /* getopt_long already printed an error message. */ - default: - ret = -1; - goto end; - } - } - - if (set_signal_handler()) { - ret = -1; - goto end; - } - - /* - * The two following sync points allow for tests to do work after the - * app has started BUT before it generates any events. - */ - if (create_in_main_file_path) { - ret = create_file(create_in_main_file_path); - if (ret != 0) { - goto end; - } - } - - if (wait_before_first_event_file_path) { - ret = wait_on_file(wait_before_first_event_file_path); - if (ret != 0) { - goto end; - } - } - - for (i = 0; i < nr_iter; i++) { - netint = htonl(i); - tracepoint(tp, tptest1, i, netint, values, text, strlen(text), - dbl, flt); - tracepoint(tp, tptest2, i, netint, values, text, strlen(text), - dbl, flt); - tracepoint(tp, tptest3, i, netint, values, text, strlen(text), - dbl, flt); - tracepoint(tp, tptest4, i, netint, values, text, strlen(text), - dbl, flt); - tracepoint(tp, tptest5, i, netint, values, text, strlen(text), - dbl, flt); - if (nr_usec) { - if (usleep_safe(nr_usec)) { - ret = -1; - goto end; - } - } - if (should_quit) { - break; - } - } - -end: - free(create_in_main_file_path); - free(wait_before_first_event_file_path); - exit(!ret ? EXIT_SUCCESS : EXIT_FAILURE); -} diff --git a/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.cpp b/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.cpp new file mode 100644 index 000000000..ec71023de --- /dev/null +++ b/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2012 David Goulet + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" +#include "signal-helper.h" + +#define TRACEPOINT_DEFINE +#include "tp.h" + +static struct option long_options[] = +{ + /* These options set a flag. */ + {"iter", required_argument, 0, 'i'}, + {"wait", required_argument, 0, 'w'}, + {"create-in-main", required_argument, 0, 'm'}, + {"wait-before-first-event", required_argument, 0, 'b'}, + {0, 0, 0, 0} +}; + +int main(int argc, char **argv) +{ + int i, netint, ret = 0, option_index, option; + long values[] = { 1, 2, 3 }; + char text[10] = "test"; + double dbl = 2.0; + float flt = 2222.0; + unsigned int nr_iter = 100; + useconds_t nr_usec = 0; + char *wait_before_first_event_file_path = NULL; + char *create_in_main_file_path = NULL; + + while ((option = getopt_long(argc, argv, "i:w:b:m:", + long_options, &option_index)) != -1) { + switch (option) { + case 'b': + wait_before_first_event_file_path = strdup(optarg); + break; + case 'm': + create_in_main_file_path = strdup(optarg); + break; + case 'i': + nr_iter = atoi(optarg); + break; + case 'w': + nr_usec = atoi(optarg); + break; + case '?': + /* getopt_long already printed an error message. */ + default: + ret = -1; + goto end; + } + } + + if (set_signal_handler()) { + ret = -1; + goto end; + } + + /* + * The two following sync points allow for tests to do work after the + * app has started BUT before it generates any events. + */ + if (create_in_main_file_path) { + ret = create_file(create_in_main_file_path); + if (ret != 0) { + goto end; + } + } + + if (wait_before_first_event_file_path) { + ret = wait_on_file(wait_before_first_event_file_path); + if (ret != 0) { + goto end; + } + } + + for (i = 0; i < nr_iter; i++) { + netint = htonl(i); + tracepoint(tp, tptest1, i, netint, values, text, strlen(text), + dbl, flt); + tracepoint(tp, tptest2, i, netint, values, text, strlen(text), + dbl, flt); + tracepoint(tp, tptest3, i, netint, values, text, strlen(text), + dbl, flt); + tracepoint(tp, tptest4, i, netint, values, text, strlen(text), + dbl, flt); + tracepoint(tp, tptest5, i, netint, values, text, strlen(text), + dbl, flt); + if (nr_usec) { + if (usleep_safe(nr_usec)) { + ret = -1; + goto end; + } + } + if (should_quit) { + break; + } + } + +end: + free(create_in_main_file_path); + free(wait_before_first_event_file_path); + exit(!ret ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/tests/utils/testapp/gen-ust-tracef/Makefile.am b/tests/utils/testapp/gen-ust-tracef/Makefile.am index b16f6ce54..17678c697 100644 --- a/tests/utils/testapp/gen-ust-tracef/Makefile.am +++ b/tests/utils/testapp/gen-ust-tracef/Makefile.am @@ -5,6 +5,6 @@ AM_CPPFLAGS += -I$(srcdir) \ if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS = gen-ust-tracef -gen_ust_tracef_SOURCES = gen-ust-tracef.c +gen_ust_tracef_SOURCES = gen-ust-tracef.cpp gen_ust_tracef_LDADD = $(UST_LIBS) $(DL_LIBS) endif diff --git a/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.c b/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.c deleted file mode 100644 index f0f6cc63c..000000000 --- a/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2012 David Goulet - * Copyright (C) 2014 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "signal-helper.h" - -const char *str = "test string"; - -static -void create_file(const char *path) -{ - int ret; - - LTTNG_ASSERT(path); - - ret = creat(path, S_IRWXU); - if (ret < 0) { - fprintf(stderr, "Failed to create file %s\n", path); - return; - } - - (void) close(ret); -} - -int main(int argc, char **argv) -{ - int i; - unsigned int nr_iter = 100; - useconds_t nr_usec = 0; - char *tmp_file_path = NULL; - - if (set_signal_handler()) { - return 1; - } - - if (argc >= 2) { - nr_iter = atoi(argv[1]); - } - - if (argc >= 3) { - /* By default, don't wait unless user specifies. */ - nr_usec = atoi(argv[2]); - } - - if (argc >= 4) { - tmp_file_path = argv[3]; - } - - for (i = 0; i < nr_iter; i++) { - tracef("Test message %d with string \"%s\"", i, str); - - /* - * First loop we create the file if asked to indicate - * that at least one tracepoint has been hit. - */ - if (i == 0 && tmp_file_path) { - create_file(tmp_file_path); - } - usleep(nr_usec); - if (should_quit) { - break; - } - } - - return 0; -} diff --git a/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.cpp b/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.cpp new file mode 100644 index 000000000..f0f6cc63c --- /dev/null +++ b/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 David Goulet + * Copyright (C) 2014 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "signal-helper.h" + +const char *str = "test string"; + +static +void create_file(const char *path) +{ + int ret; + + LTTNG_ASSERT(path); + + ret = creat(path, S_IRWXU); + if (ret < 0) { + fprintf(stderr, "Failed to create file %s\n", path); + return; + } + + (void) close(ret); +} + +int main(int argc, char **argv) +{ + int i; + unsigned int nr_iter = 100; + useconds_t nr_usec = 0; + char *tmp_file_path = NULL; + + if (set_signal_handler()) { + return 1; + } + + if (argc >= 2) { + nr_iter = atoi(argv[1]); + } + + if (argc >= 3) { + /* By default, don't wait unless user specifies. */ + nr_usec = atoi(argv[2]); + } + + if (argc >= 4) { + tmp_file_path = argv[3]; + } + + for (i = 0; i < nr_iter; i++) { + tracef("Test message %d with string \"%s\"", i, str); + + /* + * First loop we create the file if asked to indicate + * that at least one tracepoint has been hit. + */ + if (i == 0 && tmp_file_path) { + create_file(tmp_file_path); + } + usleep(nr_usec); + if (should_quit) { + break; + } + } + + return 0; +} diff --git a/tests/utils/testapp/signal-helper.h b/tests/utils/testapp/signal-helper.h index 8d2668f31..ceadcb372 100644 --- a/tests/utils/testapp/signal-helper.h +++ b/tests/utils/testapp/signal-helper.h @@ -24,10 +24,9 @@ static int set_signal_handler(void) { int ret; - struct sigaction sa = { - .sa_flags = 0, - .sa_handler = sighandler, - }; + struct sigaction sa {}; + sa.sa_flags = 0; + sa.sa_handler = sighandler; ret = sigemptyset(&sa.sa_mask); if (ret) { diff --git a/tests/utils/utils.c b/tests/utils/utils.c deleted file mode 100644 index fee74e961..000000000 --- a/tests/utils/utils.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2015 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "utils.h" - -static inline -int64_t elapsed_time_ns(struct timespec *t1, struct timespec *t2) -{ - struct timespec delta; - - LTTNG_ASSERT(t1 && t2); - delta.tv_sec = t2->tv_sec - t1->tv_sec; - delta.tv_nsec = t2->tv_nsec - t1->tv_nsec; - return ((int64_t) NSEC_PER_SEC * (int64_t) delta.tv_sec) + - (int64_t) delta.tv_nsec; -} - -int usleep_safe(useconds_t usec) -{ - int ret = 0; - struct timespec t1, t2; - int64_t time_remaining_ns = (int64_t) usec * (int64_t) NSEC_PER_USEC; - - ret = lttng_clock_gettime(CLOCK_MONOTONIC, &t1); - if (ret) { - ret = -1; - perror("clock_gettime"); - goto end; - } - - while (time_remaining_ns > 0) { - ret = usleep(time_remaining_ns / (int64_t) NSEC_PER_USEC); - if (ret && errno != EINTR) { - perror("usleep"); - goto end; - } - - ret = lttng_clock_gettime(CLOCK_MONOTONIC, &t2); - if (ret) { - perror("clock_gettime"); - goto end; - } - - time_remaining_ns -= elapsed_time_ns(&t1, &t2); - } -end: - return ret; -} - -int create_file(const char *path) -{ - int ret; - - if (!path) { - return -1; - } - - ret = creat(path, S_IRWXU); - if (ret < 0) { - perror("creat"); - return -1; - } - - ret = close(ret); - if (ret < 0) { - perror("close"); - return -1; - } - - return 0; -} - -int wait_on_file(const char *path) -{ - int ret; - struct stat buf; - - if (!path) { - return -1; - } - - for (;;) { - ret = stat(path, &buf); - if (ret == -1 && errno == ENOENT) { - ret = poll(NULL, 0, 10); /* 10 ms delay */ - /* Should return 0 everytime */ - if (ret) { - if (ret < 0) { - perror("perror"); - } else { - fprintf(stderr, - "poll return value is larger than zero\n"); - } - return -1; - } - continue; /* retry */ - } - if (ret) { - perror("stat"); - return -1; - } - break; /* found */ - } - - return 0; -} diff --git a/tests/utils/utils.cpp b/tests/utils/utils.cpp new file mode 100644 index 000000000..fee74e961 --- /dev/null +++ b/tests/utils/utils.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "utils.h" + +static inline +int64_t elapsed_time_ns(struct timespec *t1, struct timespec *t2) +{ + struct timespec delta; + + LTTNG_ASSERT(t1 && t2); + delta.tv_sec = t2->tv_sec - t1->tv_sec; + delta.tv_nsec = t2->tv_nsec - t1->tv_nsec; + return ((int64_t) NSEC_PER_SEC * (int64_t) delta.tv_sec) + + (int64_t) delta.tv_nsec; +} + +int usleep_safe(useconds_t usec) +{ + int ret = 0; + struct timespec t1, t2; + int64_t time_remaining_ns = (int64_t) usec * (int64_t) NSEC_PER_USEC; + + ret = lttng_clock_gettime(CLOCK_MONOTONIC, &t1); + if (ret) { + ret = -1; + perror("clock_gettime"); + goto end; + } + + while (time_remaining_ns > 0) { + ret = usleep(time_remaining_ns / (int64_t) NSEC_PER_USEC); + if (ret && errno != EINTR) { + perror("usleep"); + goto end; + } + + ret = lttng_clock_gettime(CLOCK_MONOTONIC, &t2); + if (ret) { + perror("clock_gettime"); + goto end; + } + + time_remaining_ns -= elapsed_time_ns(&t1, &t2); + } +end: + return ret; +} + +int create_file(const char *path) +{ + int ret; + + if (!path) { + return -1; + } + + ret = creat(path, S_IRWXU); + if (ret < 0) { + perror("creat"); + return -1; + } + + ret = close(ret); + if (ret < 0) { + perror("close"); + return -1; + } + + return 0; +} + +int wait_on_file(const char *path) +{ + int ret; + struct stat buf; + + if (!path) { + return -1; + } + + for (;;) { + ret = stat(path, &buf); + if (ret == -1 && errno == ENOENT) { + ret = poll(NULL, 0, 10); /* 10 ms delay */ + /* Should return 0 everytime */ + if (ret) { + if (ret < 0) { + perror("perror"); + } else { + fprintf(stderr, + "poll return value is larger than zero\n"); + } + return -1; + } + continue; /* retry */ + } + if (ret) { + perror("stat"); + return -1; + } + break; /* found */ + } + + return 0; +} diff --git a/tests/utils/utils.h b/tests/utils/utils.h index afacf8d2f..fc06b4ace 100644 --- a/tests/utils/utils.h +++ b/tests/utils/utils.h @@ -8,6 +8,10 @@ #ifndef TEST_UTILS_H #define TEST_UTILS_H +#if defined(__cplusplus) +extern "C" { +#endif + #if !defined(__GLIBC__) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !defined(_GNU_SOURCE)) /* @@ -37,4 +41,8 @@ int usleep_safe(useconds_t usec); int create_file(const char *path); int wait_on_file(const char *path); +#if defined(__cplusplus) +} +#endif + #endif /* TEST_UTILS_H */ diff --git a/tests/utils/xml-utils/Makefile.am b/tests/utils/xml-utils/Makefile.am index 7fac12728..7997d94e1 100644 --- a/tests/utils/xml-utils/Makefile.am +++ b/tests/utils/xml-utils/Makefile.am @@ -1,11 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only noinst_PROGRAMS = validate_xml extract_xml pretty_xml -validate_xml_SOURCES = validate_xml.c +validate_xml_SOURCES = validate_xml.cpp validate_xml_CPPFLAGS = $(libxml2_CFLAGS) $(AM_CPPFLAGS) validate_xml_LDADD = $(libxml2_LIBS) -extract_xml_SOURCES = extract_xml.c +extract_xml_SOURCES = extract_xml.cpp extract_xml_CPPFLAGS = $(libxml2_CFLAGS) $(AM_CPPFLAGS) extract_xml_LDADD = $(libxml2_LIBS) diff --git a/tests/utils/xml-utils/extract_xml.c b/tests/utils/xml-utils/extract_xml.c deleted file mode 100644 index 011f38f4a..000000000 --- a/tests/utils/xml-utils/extract_xml.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (C) 2014 Jonathan Rajotte - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -/* - * Usage: extract_xml [-v|-e] xml_path xpath_expression - * Evaluate XPath expression and prints result node set. - * args[1] path to the xml file - * args[2] xpath expression to extract - * If -e look if node exist return "true" else nothing - * If -v is set the name of the node will appear with his value delimited by - * a semicolon(;) - * Ex: - * Command:extract_xml ../file.xml /test/node/text() - * Output: - * a - * b - * c - * With -v - * node;a; - * node;b; - * node;c; - */ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if defined(LIBXML_XPATH_ENABLED) - -static int opt_verbose; -static int node_exist; -static bool result = false; - -/** - * print_xpath_nodes: - * nodes: the nodes set. - * output: the output file handle. - * - * Print the node content to the file - */ -static int print_xpath_nodes(xmlDocPtr doc, xmlNodeSetPtr nodes, FILE *output) -{ - int ret = 0; - int size; - int i; - - xmlNodePtr cur; - xmlChar *node_child_value_string = NULL; - - LTTNG_ASSERT(output); - size = (nodes) ? nodes->nodeNr : 0; - - for (i = 0; i < size; ++i) { - LTTNG_ASSERT(nodes->nodeTab[i]); - - if (nodes->nodeTab[i]->type == XML_NAMESPACE_DECL) { - fprintf(stderr, "ERR:%s\n", - "This executable does not support xml namespacing\n"); - ret = -1; - goto end; - } else if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE) { - cur = nodes->nodeTab[i]; - - if (xmlChildElementCount(cur) == 0) { - if (xmlNodeIsText(cur->children)) { - node_child_value_string = xmlNodeListGetString(doc, - cur->children, 1); - if (node_exist) { - result = true; - } else if (opt_verbose) { - fprintf(output, "%s;%s;\n", cur->name, - node_child_value_string); - } else { - fprintf(output, "%s\n", - node_child_value_string); - } - xmlFree(node_child_value_string); - } else { - /* We don't want to print non-final element */ - if (node_exist) { - result = true; - } else { - fprintf(stderr, "ERR:%s\n", - "Xpath expression return non-final xml element"); - ret = -1; - goto end; - } - } - } else { - if (node_exist) { - result = true; - } else { - /* We don't want to print non-final element */ - fprintf(stderr, "ERR:%s\n", - "Xpath expression return non-final xml element"); - ret = -1; - goto end; - } - } - - } else { - cur = nodes->nodeTab[i]; - if (node_exist) { - result = true; - } else if (opt_verbose) { - fprintf(output, "%s;%s;\n", cur->parent->name, cur->content); - } else { - fprintf(output, "%s\n", cur->content); - } - } - } - /* Command Success */ - ret = 0; - -end: - return ret; -} - -static int register_lttng_namespace(xmlXPathContextPtr xpathCtx) -{ - int ret; - xmlChar *prefix; - xmlChar *ns = NULL; - - prefix = xmlCharStrdup("lttng"); - if (!prefix) { - ret = -1; - goto end; - } - - ns = xmlCharStrdup(DEFAULT_LTTNG_MI_NAMESPACE); - if (!ns) { - ret = -1; - goto end; - } - - ret = xmlXPathRegisterNs(xpathCtx, prefix, ns); -end: - xmlFree(prefix); - xmlFree(ns); - return ret; -} - -/* - * Extract element corresponding to xpath - * xml_path The path to the xml file - * xpath: The xpath to evaluate. - * - * Evaluate an xpath expression onto an xml file. - * and print the result one by line. - * - * Returns 0 on success and a negative value otherwise. - */ -static int extract_xpath(const char *xml_path, const xmlChar *xpath) -{ - int ret; - xmlDocPtr doc = NULL; - xmlXPathContextPtr xpathCtx = NULL; - xmlXPathObjectPtr xpathObj = NULL; - - LTTNG_ASSERT(xml_path); - LTTNG_ASSERT(xpath); - - /* Parse the xml file */ - doc = xmlParseFile(xml_path); - if (!doc) { - fprintf(stderr, "ERR parsing: xml file invalid \"%s\"\n", xml_path); - return -1; - } - - /* Initialize a xpath context */ - xpathCtx = xmlXPathNewContext(doc); - if (!xpathCtx) { - fprintf(stderr, "ERR: XPath context invalid\n"); - xmlFreeDoc(doc); - return -1; - } - - /* Register the LTTng MI namespace */ - ret = register_lttng_namespace(xpathCtx); - if (ret) { - fprintf(stderr, "ERR: Could not register lttng namespace\n"); - xmlXPathFreeContext(xpathCtx); - xmlFreeDoc(doc); - return -1; - } - - /* Evaluate xpath expression */ - xpathObj = xmlXPathEvalExpression(xpath, xpathCtx); - if (!xpathObj) { - fprintf(stderr, "ERR: invalid xpath expression \"%s\"\n", xpath); - xmlXPathFreeContext(xpathCtx); - xmlFreeDoc(doc); - return -1; - } - - /* Print results */ - if (print_xpath_nodes(doc, xpathObj->nodesetval, stdout)) { - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); - xmlFreeDoc(doc); - return -1; - } - if (node_exist && result) { - fprintf(stdout, "true\n"); - } - - /* Cleanup */ - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); - xmlFreeDoc(doc); - - return 0; -} - -int main(int argc, char **argv) -{ - int opt; - - /* Parse command line and process file */ - while ((opt = getopt(argc, argv, "ve")) != -1) { - switch (opt) { - case 'v': - opt_verbose = 1; - break; - case 'e': - node_exist = 1; - break; - default: - abort(); - } - } - - if (!(optind + 1 < argc)) { - fprintf(stderr, "ERR:%s\n", "Arguments missing"); - return -1; - } - - /* Init libxml */ - xmlInitParser(); - xmlKeepBlanksDefault(0); - if (access(argv[optind], F_OK)) { - fprintf(stderr, "ERR:%s\n", "Xml path not valid"); - return -1; - } - /* Do the main job */ - if (extract_xpath(argv[optind], (xmlChar *)argv[optind+1])) { - return -1; - } - - /* Shutdown libxml */ - xmlCleanupParser(); - - return 0; -} - -#else -int main(void) -{ - fprintf(stderr, "XPath support not compiled in\n"); - return -1; -} -#endif diff --git a/tests/utils/xml-utils/extract_xml.cpp b/tests/utils/xml-utils/extract_xml.cpp new file mode 100644 index 000000000..011f38f4a --- /dev/null +++ b/tests/utils/xml-utils/extract_xml.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2014 Jonathan Rajotte + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +/* + * Usage: extract_xml [-v|-e] xml_path xpath_expression + * Evaluate XPath expression and prints result node set. + * args[1] path to the xml file + * args[2] xpath expression to extract + * If -e look if node exist return "true" else nothing + * If -v is set the name of the node will appear with his value delimited by + * a semicolon(;) + * Ex: + * Command:extract_xml ../file.xml /test/node/text() + * Output: + * a + * b + * c + * With -v + * node;a; + * node;b; + * node;c; + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(LIBXML_XPATH_ENABLED) + +static int opt_verbose; +static int node_exist; +static bool result = false; + +/** + * print_xpath_nodes: + * nodes: the nodes set. + * output: the output file handle. + * + * Print the node content to the file + */ +static int print_xpath_nodes(xmlDocPtr doc, xmlNodeSetPtr nodes, FILE *output) +{ + int ret = 0; + int size; + int i; + + xmlNodePtr cur; + xmlChar *node_child_value_string = NULL; + + LTTNG_ASSERT(output); + size = (nodes) ? nodes->nodeNr : 0; + + for (i = 0; i < size; ++i) { + LTTNG_ASSERT(nodes->nodeTab[i]); + + if (nodes->nodeTab[i]->type == XML_NAMESPACE_DECL) { + fprintf(stderr, "ERR:%s\n", + "This executable does not support xml namespacing\n"); + ret = -1; + goto end; + } else if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE) { + cur = nodes->nodeTab[i]; + + if (xmlChildElementCount(cur) == 0) { + if (xmlNodeIsText(cur->children)) { + node_child_value_string = xmlNodeListGetString(doc, + cur->children, 1); + if (node_exist) { + result = true; + } else if (opt_verbose) { + fprintf(output, "%s;%s;\n", cur->name, + node_child_value_string); + } else { + fprintf(output, "%s\n", + node_child_value_string); + } + xmlFree(node_child_value_string); + } else { + /* We don't want to print non-final element */ + if (node_exist) { + result = true; + } else { + fprintf(stderr, "ERR:%s\n", + "Xpath expression return non-final xml element"); + ret = -1; + goto end; + } + } + } else { + if (node_exist) { + result = true; + } else { + /* We don't want to print non-final element */ + fprintf(stderr, "ERR:%s\n", + "Xpath expression return non-final xml element"); + ret = -1; + goto end; + } + } + + } else { + cur = nodes->nodeTab[i]; + if (node_exist) { + result = true; + } else if (opt_verbose) { + fprintf(output, "%s;%s;\n", cur->parent->name, cur->content); + } else { + fprintf(output, "%s\n", cur->content); + } + } + } + /* Command Success */ + ret = 0; + +end: + return ret; +} + +static int register_lttng_namespace(xmlXPathContextPtr xpathCtx) +{ + int ret; + xmlChar *prefix; + xmlChar *ns = NULL; + + prefix = xmlCharStrdup("lttng"); + if (!prefix) { + ret = -1; + goto end; + } + + ns = xmlCharStrdup(DEFAULT_LTTNG_MI_NAMESPACE); + if (!ns) { + ret = -1; + goto end; + } + + ret = xmlXPathRegisterNs(xpathCtx, prefix, ns); +end: + xmlFree(prefix); + xmlFree(ns); + return ret; +} + +/* + * Extract element corresponding to xpath + * xml_path The path to the xml file + * xpath: The xpath to evaluate. + * + * Evaluate an xpath expression onto an xml file. + * and print the result one by line. + * + * Returns 0 on success and a negative value otherwise. + */ +static int extract_xpath(const char *xml_path, const xmlChar *xpath) +{ + int ret; + xmlDocPtr doc = NULL; + xmlXPathContextPtr xpathCtx = NULL; + xmlXPathObjectPtr xpathObj = NULL; + + LTTNG_ASSERT(xml_path); + LTTNG_ASSERT(xpath); + + /* Parse the xml file */ + doc = xmlParseFile(xml_path); + if (!doc) { + fprintf(stderr, "ERR parsing: xml file invalid \"%s\"\n", xml_path); + return -1; + } + + /* Initialize a xpath context */ + xpathCtx = xmlXPathNewContext(doc); + if (!xpathCtx) { + fprintf(stderr, "ERR: XPath context invalid\n"); + xmlFreeDoc(doc); + return -1; + } + + /* Register the LTTng MI namespace */ + ret = register_lttng_namespace(xpathCtx); + if (ret) { + fprintf(stderr, "ERR: Could not register lttng namespace\n"); + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); + return -1; + } + + /* Evaluate xpath expression */ + xpathObj = xmlXPathEvalExpression(xpath, xpathCtx); + if (!xpathObj) { + fprintf(stderr, "ERR: invalid xpath expression \"%s\"\n", xpath); + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); + return -1; + } + + /* Print results */ + if (print_xpath_nodes(doc, xpathObj->nodesetval, stdout)) { + xmlXPathFreeObject(xpathObj); + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); + return -1; + } + if (node_exist && result) { + fprintf(stdout, "true\n"); + } + + /* Cleanup */ + xmlXPathFreeObject(xpathObj); + xmlXPathFreeContext(xpathCtx); + xmlFreeDoc(doc); + + return 0; +} + +int main(int argc, char **argv) +{ + int opt; + + /* Parse command line and process file */ + while ((opt = getopt(argc, argv, "ve")) != -1) { + switch (opt) { + case 'v': + opt_verbose = 1; + break; + case 'e': + node_exist = 1; + break; + default: + abort(); + } + } + + if (!(optind + 1 < argc)) { + fprintf(stderr, "ERR:%s\n", "Arguments missing"); + return -1; + } + + /* Init libxml */ + xmlInitParser(); + xmlKeepBlanksDefault(0); + if (access(argv[optind], F_OK)) { + fprintf(stderr, "ERR:%s\n", "Xml path not valid"); + return -1; + } + /* Do the main job */ + if (extract_xpath(argv[optind], (xmlChar *)argv[optind+1])) { + return -1; + } + + /* Shutdown libxml */ + xmlCleanupParser(); + + return 0; +} + +#else +int main(void) +{ + fprintf(stderr, "XPath support not compiled in\n"); + return -1; +} +#endif diff --git a/tests/utils/xml-utils/validate_xml.c b/tests/utils/xml-utils/validate_xml.c deleted file mode 100644 index bb67e56e1..000000000 --- a/tests/utils/xml-utils/validate_xml.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2014 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - - /* - * This script validate and xml from an xsd. - * argv[1] Path of the xsd - * argv[2] Path to the XML to be validated - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -struct validation_ctx { - xmlSchemaParserCtxtPtr parser_ctx; - xmlSchemaPtr schema; - xmlSchemaValidCtxtPtr schema_validation_ctx; -}; - -enum command_err_code { - CMD_SUCCESS = 0, - CMD_ERROR -}; - -static -void xml_error_handler(void *ctx, const char *format, ...) -{ - char *err_msg; - va_list args; - int ret; - - va_start(args, format); - ret = vasprintf(&err_msg, format, args); - va_end(args); - if (ret == -1) { - fprintf(stderr, "ERR: %s\n", - "String allocation failed in xml error handle"); - return; - } - - fprintf(stderr, "XML Error: %s\n", err_msg); - free(err_msg); -} - -static -void fini_validation_ctx( - struct validation_ctx *ctx) -{ - if (ctx->parser_ctx) { - xmlSchemaFreeParserCtxt(ctx->parser_ctx); - } - - if (ctx->schema) { - xmlSchemaFree(ctx->schema); - } - - if (ctx->schema_validation_ctx) { - xmlSchemaFreeValidCtxt(ctx->schema_validation_ctx); - } - - memset(ctx, 0, sizeof(struct validation_ctx)); -} - -static -int init_validation_ctx( - struct validation_ctx *ctx, char *xsd_path) -{ - int ret; - - if (!xsd_path) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - ctx->parser_ctx = xmlSchemaNewParserCtxt(xsd_path); - if (!ctx->parser_ctx) { - ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; - goto end; - } - xmlSchemaSetParserErrors(ctx->parser_ctx, xml_error_handler, - xml_error_handler, NULL); - - ctx->schema = xmlSchemaParse(ctx->parser_ctx); - if (!ctx->schema) { - ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; - goto end; - } - - ctx->schema_validation_ctx = xmlSchemaNewValidCtxt(ctx->schema); - if (!ctx->schema_validation_ctx) { - ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; - goto end; - } - - xmlSchemaSetValidErrors(ctx->schema_validation_ctx, xml_error_handler, - xml_error_handler, NULL); - ret = 0; - -end: - if (ret) { - fini_validation_ctx(ctx); - } - return ret; -} - -static int validate_xml(const char *xml_file_path, struct validation_ctx *ctx) -{ - int ret; - xmlDocPtr doc = NULL; - - LTTNG_ASSERT(xml_file_path); - LTTNG_ASSERT(ctx); - - /* Open the document */ - doc = xmlParseFile(xml_file_path); - if (!doc) { - ret = LTTNG_ERR_MI_IO_FAIL; - goto end; - } - - /* Validate against the validation ctx (xsd) */ - ret = xmlSchemaValidateDoc(ctx->schema_validation_ctx, doc); - if (ret) { - fprintf(stderr, "ERR: %s\n", "XML is not valid againt provided XSD"); - ret = CMD_ERROR; - goto end; - } - - ret = CMD_SUCCESS; -end: - return ret; - - -} -int main(int argc, char **argv, char *env[]) -{ - int ret; - struct validation_ctx ctx = { 0 }; - - /* Check if we have all argument */ - if (argc < 3) { - fprintf(stderr, "ERR: %s\n", "Missing arguments"); - ret = CMD_ERROR; - goto end; - } - - /* Check if xsd file exist */ - ret = access(argv[1], F_OK); - if (ret < 0) { - fprintf(stderr, "ERR: %s\n", "Xsd path not valid"); - goto end; - } - - /* Check if xml to validate exist */ - ret = access(argv[2], F_OK); - if (ret < 0) { - fprintf(stderr, "ERR: %s\n", "XML path not valid"); - goto end; - } - - /* initialize the validation ctx */ - ret = init_validation_ctx(&ctx, argv[1]); - if (ret) { - goto end; - } - - ret = validate_xml(argv[2], &ctx); - - fini_validation_ctx(&ctx); - -end: - return ret; -} diff --git a/tests/utils/xml-utils/validate_xml.cpp b/tests/utils/xml-utils/validate_xml.cpp new file mode 100644 index 000000000..bb67e56e1 --- /dev/null +++ b/tests/utils/xml-utils/validate_xml.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2014 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + + /* + * This script validate and xml from an xsd. + * argv[1] Path of the xsd + * argv[2] Path to the XML to be validated + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +struct validation_ctx { + xmlSchemaParserCtxtPtr parser_ctx; + xmlSchemaPtr schema; + xmlSchemaValidCtxtPtr schema_validation_ctx; +}; + +enum command_err_code { + CMD_SUCCESS = 0, + CMD_ERROR +}; + +static +void xml_error_handler(void *ctx, const char *format, ...) +{ + char *err_msg; + va_list args; + int ret; + + va_start(args, format); + ret = vasprintf(&err_msg, format, args); + va_end(args); + if (ret == -1) { + fprintf(stderr, "ERR: %s\n", + "String allocation failed in xml error handle"); + return; + } + + fprintf(stderr, "XML Error: %s\n", err_msg); + free(err_msg); +} + +static +void fini_validation_ctx( + struct validation_ctx *ctx) +{ + if (ctx->parser_ctx) { + xmlSchemaFreeParserCtxt(ctx->parser_ctx); + } + + if (ctx->schema) { + xmlSchemaFree(ctx->schema); + } + + if (ctx->schema_validation_ctx) { + xmlSchemaFreeValidCtxt(ctx->schema_validation_ctx); + } + + memset(ctx, 0, sizeof(struct validation_ctx)); +} + +static +int init_validation_ctx( + struct validation_ctx *ctx, char *xsd_path) +{ + int ret; + + if (!xsd_path) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ctx->parser_ctx = xmlSchemaNewParserCtxt(xsd_path); + if (!ctx->parser_ctx) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + xmlSchemaSetParserErrors(ctx->parser_ctx, xml_error_handler, + xml_error_handler, NULL); + + ctx->schema = xmlSchemaParse(ctx->parser_ctx); + if (!ctx->schema) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + ctx->schema_validation_ctx = xmlSchemaNewValidCtxt(ctx->schema); + if (!ctx->schema_validation_ctx) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + xmlSchemaSetValidErrors(ctx->schema_validation_ctx, xml_error_handler, + xml_error_handler, NULL); + ret = 0; + +end: + if (ret) { + fini_validation_ctx(ctx); + } + return ret; +} + +static int validate_xml(const char *xml_file_path, struct validation_ctx *ctx) +{ + int ret; + xmlDocPtr doc = NULL; + + LTTNG_ASSERT(xml_file_path); + LTTNG_ASSERT(ctx); + + /* Open the document */ + doc = xmlParseFile(xml_file_path); + if (!doc) { + ret = LTTNG_ERR_MI_IO_FAIL; + goto end; + } + + /* Validate against the validation ctx (xsd) */ + ret = xmlSchemaValidateDoc(ctx->schema_validation_ctx, doc); + if (ret) { + fprintf(stderr, "ERR: %s\n", "XML is not valid againt provided XSD"); + ret = CMD_ERROR; + goto end; + } + + ret = CMD_SUCCESS; +end: + return ret; + + +} +int main(int argc, char **argv, char *env[]) +{ + int ret; + struct validation_ctx ctx = { 0 }; + + /* Check if we have all argument */ + if (argc < 3) { + fprintf(stderr, "ERR: %s\n", "Missing arguments"); + ret = CMD_ERROR; + goto end; + } + + /* Check if xsd file exist */ + ret = access(argv[1], F_OK); + if (ret < 0) { + fprintf(stderr, "ERR: %s\n", "Xsd path not valid"); + goto end; + } + + /* Check if xml to validate exist */ + ret = access(argv[2], F_OK); + if (ret < 0) { + fprintf(stderr, "ERR: %s\n", "XML path not valid"); + goto end; + } + + /* initialize the validation ctx */ + ret = init_validation_ctx(&ctx, argv[1]); + if (ret) { + goto end; + } + + ret = validate_xml(argv[2], &ctx); + + fini_validation_ctx(&ctx); + +end: + return ret; +}