X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=tests%2Fregression%2Ftools%2Ftrigger%2Futils%2Fnotification-client.cpp;fp=tests%2Fregression%2Ftools%2Ftrigger%2Futils%2Fnotification-client.cpp;h=02dc0626087aa6ff77ac065b8de482b03674a1f8;hp=0000000000000000000000000000000000000000;hb=2460203ad4c64f0dcdb716e833abee10a79ec092;hpb=dc88217e78259cb6b35fbaf6b85ad5aed6a37023 diff --git a/tests/regression/tools/trigger/utils/notification-client.cpp b/tests/regression/tools/trigger/utils/notification-client.cpp new file mode 100644 index 000000000..02dc06260 --- /dev/null +++ b/tests/regression/tools/trigger/utils/notification-client.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include "utils.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct option long_options[] = { + /* These options set a flag. */ + { "trigger", required_argument, 0, 't' }, + { "sync-after-notif-register", required_argument, 0, 'a' }, + /* Default alue for count is 1 */ + { "count", required_argument, 0, 'b' }, + /* + * When end-trigger is present the reception loop is exited only when a + * notification matching the end trigger is received. + * Otherwise the loop is exited when the count of notification received + * for `trigger` math the `count` argument. + */ + { "end-trigger", required_argument, 0, 'c' }, + { 0, 0, 0, 0 } +}; + +static bool action_list_contains_notify(const struct lttng_action *action_list) +{ + const struct lttng_action *sub_action; + + for_each_action_const (sub_action, action_list) { + if (lttng_action_get_type(sub_action) == LTTNG_ACTION_TYPE_NOTIFY) { + return true; + } + } + return false; +} + +/* Only expects named triggers. */ +static bool is_trigger_name(const char *expected_trigger_name, + struct lttng_notification *notification) +{ + const char *trigger_name = NULL; + enum lttng_trigger_status trigger_status; + const struct lttng_trigger *trigger; + bool names_match; + + trigger = lttng_notification_get_trigger(notification); + if (!trigger) { + fprintf(stderr, "Failed to get trigger from notification\n"); + names_match = false; + goto end; + } + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to get name from notification's trigger\n"); + names_match = false; + goto end; + } + + names_match = strcmp(expected_trigger_name, trigger_name) == 0; + if (!names_match) { + fprintf(stderr, + "Got an unexpected trigger name: name = '%s', expected name = '%s'\n", + trigger_name, + expected_trigger_name); + } +end: + return names_match; +} + +int main(int argc, char **argv) +{ + int ret; + int option; + int option_index; + char *expected_trigger_name = NULL; + char *end_trigger_name = NULL; + struct lttng_triggers *triggers = NULL; + unsigned int count, i, subcription_count = 0; + enum lttng_trigger_status trigger_status; + char *after_notif_register_file_path = NULL; + struct lttng_notification_channel *notification_channel = NULL; + int expected_notifications = 1, notification_count = 0; + + while ((option = getopt_long(argc, argv, "a:b:c:t:", long_options, &option_index)) != -1) { + switch (option) { + case 'a': + after_notif_register_file_path = strdup(optarg); + break; + case 'b': + expected_notifications = atoi(optarg); + break; + case 'c': + end_trigger_name = strdup(optarg); + break; + case 't': + expected_trigger_name = strdup(optarg); + break; + case '?': + /* getopt_long already printed an error message. */ + default: + ret = -1; + goto end; + } + } + + if (optind != argc) { + ret = -1; + goto end; + } + + 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"); + ret = -1; + 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; + } + + /* Look for the trigger we want to subscribe to. */ + 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; + + lttng_trigger_get_name(trigger, &trigger_name); + if (strcmp(trigger_name, expected_trigger_name) != 0) { + /* Might match the end event trigger */ + if (end_trigger_name != NULL && + strcmp(trigger_name, end_trigger_name) != 0) { + continue; + } + } + if (!((action_type == LTTNG_ACTION_TYPE_LIST && + action_list_contains_notify(action)) || + action_type == LTTNG_ACTION_TYPE_NOTIFY)) { + /* "The action of trigger is not notify, skipping. */ + continue; + } + + channel_status = + lttng_notification_channel_subscribe(notification_channel, condition); + if (channel_status) { + fprintf(stderr, + "Failed to subscribe to notifications of trigger \"%s\"\n", + trigger_name); + ret = -1; + goto end; + } + + subcription_count++; + } + + if (subcription_count == 0) { + fprintf(stderr, "No matching trigger with a notify action found.\n"); + ret = -1; + goto end; + } + + if (end_trigger_name != NULL && subcription_count != 2) { + fprintf(stderr, "No matching end event trigger with a notify action found.\n"); + ret = -1; + goto end; + } + + /* + * We registered to the notification of our target trigger. We can now + * create the sync file to signify that we are ready. + */ + ret = create_file(after_notif_register_file_path); + if (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"); + ret = -1; + goto end; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED: + ret = -1; + 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; + } + + /* Early exit check. */ + if (end_trigger_name != NULL && is_trigger_name(end_trigger_name, notification)) { + /* Exit the loop immediately. */ + printf("Received end event notification from trigger %s\n", + end_trigger_name); + lttng_notification_destroy(notification); + goto evaluate_success; + } + + ret = is_trigger_name(expected_trigger_name, notification); + lttng_notification_destroy(notification); + if (!ret) { + ret = -1; + goto end; + } + + printf("Received event notification from trigger %s\n", expected_trigger_name); + notification_count++; + if (end_trigger_name == NULL && expected_notifications == notification_count) { + /* + * Here the loop exit is controlled by the number of + * notification and not by the reception of the end + * event trigger notification. This represent the + * default behavior. + * + */ + goto evaluate_success; + } + } + +evaluate_success: + if (expected_notifications == notification_count) { + /* Success */ + ret = 0; + } else { + fprintf(stderr, + "Expected %d notification got %d\n", + expected_notifications, + notification_count); + ret = 1; + } + +end: + lttng_triggers_destroy(triggers); + lttng_notification_channel_destroy(notification_channel); + free(after_notif_register_file_path); + free(end_trigger_name); + free(expected_trigger_name); + return !!ret; +}