From 6ba3189157b61924ae7fe08ff32fb02e81f56f24 Mon Sep 17 00:00:00 2001 From: Francis Deslauriers Date: Thu, 5 Mar 2020 12:23:45 -0500 Subject: [PATCH] Tests: triggers: `start-session` and `stop-session` actions MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Francis Deslauriers Signed-off-by: Jérémie Galarneau Change-Id: I1c740bcb2a7fa8a14fb1591893e8c7ea7655faac Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- configure.ac | 2 + tests/regression/Makefile.am | 1 + tests/regression/tools/trigger/Makefile.am | 2 + .../tools/trigger/start-stop/Makefile.am | 19 ++ .../tools/trigger/start-stop/test_start_stop | 198 ++++++++++++++ .../tools/trigger/utils/Makefile.am | 9 + .../tools/trigger/utils/notification-client.c | 242 ++++++++++++++++++ 7 files changed, 473 insertions(+) create mode 100644 tests/regression/tools/trigger/start-stop/Makefile.am create mode 100755 tests/regression/tools/trigger/start-stop/test_start_stop create mode 100644 tests/regression/tools/trigger/utils/Makefile.am create mode 100644 tests/regression/tools/trigger/utils/notification-client.c diff --git a/configure.ac b/configure.ac index e2a085853..954f74d99 100644 --- a/configure.ac +++ b/configure.ac @@ -1157,6 +1157,8 @@ AC_CONFIG_FILES([ tests/regression/tools/relayd-grouping/Makefile tests/regression/tools/clear/Makefile tests/regression/tools/trigger/Makefile + tests/regression/tools/trigger/start-stop/Makefile + tests/regression/tools/trigger/utils/Makefile tests/regression/ust/Makefile tests/regression/ust/nprocesses/Makefile tests/regression/ust/high-throughput/Makefile diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am index af2b793f1..c646cb1e3 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -46,6 +46,7 @@ TESTS = tools/filtering/test_invalid_filter \ tools/clear/test_ust \ tools/clear/test_kernel \ tools/tracker/test_event_tracker \ + tools/trigger/start-stop/test_start_stop \ tools/trigger/test_add_trigger_cli \ tools/trigger/test_list_triggers_cli \ tools/trigger/test_remove_trigger_cli diff --git a/tests/regression/tools/trigger/Makefile.am b/tests/regression/tools/trigger/Makefile.am index dcc5d98b0..378bf8b5d 100644 --- a/tests/regression/tools/trigger/Makefile.am +++ b/tests/regression/tools/trigger/Makefile.am @@ -1,3 +1,5 @@ +SUBDIRS=utils start-stop + noinst_SCRIPTS = test_add_trigger_cli \ test_list_triggers_cli \ test_remove_trigger_cli diff --git a/tests/regression/tools/trigger/start-stop/Makefile.am b/tests/regression/tools/trigger/start-stop/Makefile.am new file mode 100644 index 000000000..c28007d59 --- /dev/null +++ b/tests/regression/tools/trigger/start-stop/Makefile.am @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only + +noinst_SCRIPTS = test_start_stop + +EXTRA_DIST = test_start_stop + +all-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + cp -f $(srcdir)/$$script $(builddir); \ + done; \ + fi + +clean-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + rm -f $(builddir)/$$script; \ + done; \ + fi diff --git a/tests/regression/tools/trigger/start-stop/test_start_stop b/tests/regression/tools/trigger/start-stop/test_start_stop new file mode 100755 index 000000000..cecea3b19 --- /dev/null +++ b/tests/regression/tools/trigger/start-stop/test_start_stop @@ -0,0 +1,198 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +TEST_DESC="Triggers - Start and stop actions" + +CURDIR=$(dirname "$0")/ +TESTDIR=${CURDIR}/../../../.. + +# shellcheck source=../../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +TESTAPP_PATH="$TESTDIR/utils/testapp" +GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events" +GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME" +FULL_LTTNG_BIN="$TESTDIR/../src/bin/lttng/$LTTNG_BIN" +NOTIFICATION_CLIENT_BIN="$CURDIR/../utils/notification-client" +NUM_TESTS=18 + +NR_ITER=5 +NR_USEC_WAIT=5 + +function lttng_add_trigger_ust() +{ + local expected_to_fail="$1" + local trigger_name="$2" + shift 2 + + "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" "$@" 1> /dev/null 2> /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Add trigger $trigger_name failed as expected" + else + ok $ret "Add trigger $trigger_name" + fi +} + +function lttng_remove_trigger_ust() +{ + local expected_to_fail="$1" + local trigger_name="$2" + + "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" 1> /dev/null 2> /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Remove trigger $trigger_name failed as expected" + else + ok $ret "Remove trigger $trigger_name" + fi +} + +function lttng_add_trigger_ust_ok() +{ + lttng_add_trigger_ust 0 "$@" +} + +function lttng_remove_trigger_ust_ok() +{ + lttng_remove_trigger_ust 0 "$@" +} + +function lttng_session_is_active() +{ + local SESSION_NAME="$1" + "$FULL_LTTNG_BIN" list "$SESSION_NAME" | grep "Tracing session" | grep -q "\[active\]" + + ok $ret "Session \"$SESSION_NAME\" is active" +} + +function lttng_session_is_inactive() +{ + local SESSION_NAME="$1" + "$FULL_LTTNG_BIN" list "$SESSION_NAME" | grep "Tracing session" | grep -q "\[inactive\]" + + ok $ret "Session \"$SESSION_NAME\" is inactive" +} + +function test_start_session_action() +{ + local SESSION_NAME="my_triggered_session" + local TRIGGER_NAME="trigger1" + local TRACE_PATH=$(mktemp -d test-start-action-trace.XXXXXX) + local SYNC_AFTER_NOTIF_REGISTER_PATH=$(mktemp test-notif-register.XXXXXX) + + diag "Start session action" + + create_lttng_session_ok $SESSION_NAME "$TRACE_PATH" + + enable_ust_lttng_event_ok $SESSION_NAME "tp:tptest" + + lttng_session_is_inactive $SESSION_NAME + + # Add `start-session` action to an event-rule condition _followed_ by + # a `notify` action. + lttng_add_trigger_ust_ok \ + $TRIGGER_NAME \ + --condition on-event -u "tp:tptest" \ + --action start-session $SESSION_NAME \ + --action notify + + # Launch notification listener. + $NOTIFICATION_CLIENT_BIN \ + --trigger $TRIGGER_NAME \ + --sync-after-notif-register "$SYNC_AFTER_NOTIF_REGISTER_PATH" + notif_client_pid=$! + + while [ ! -f "${SYNC_AFTER_NOTIF_REGISTER_PATH}" ]; do + sleep 0.5 + done + + # Artificially produce the desired event-rule condition. + $GEN_UST_EVENTS_TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 + + # notification-client will exit once it receives a notification. + wait $notif_client_pid + test "$?" -eq "0" + ok $? "notification client exited successfully" + + # Test that the session has started. + lttng_session_is_active $SESSION_NAME + + # Tearing down. + lttng_remove_trigger_ust_ok $TRIGGER_NAME + stop_lttng_tracing_ok $SESSION_NAME + destroy_lttng_session_ok $SESSION_NAME + + rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH" + rm -rf "$TRACE_PATH" +} + +function test_stop_session_action() +{ + local SESSION_NAME="my_triggered_session" + local TRIGGER_NAME="trigger1" + local TRACE_PATH=$(mktemp -d test-stop-action-trace.XXXXXX) + local SYNC_AFTER_NOTIF_REGISTER_PATH=$(mktemp test-notif-register.XXXXXX) + + diag "Stop session action" + create_lttng_session_ok $SESSION_NAME "$TRACE_PATH" + + enable_ust_lttng_event_ok $SESSION_NAME "tp:tptest" + + start_lttng_tracing_ok $SESSION_NAME + + lttng_session_is_active $SESSION_NAME + + # Add `stop-session` action to an event-rule condition _followed_ by + # a `notify` action. + lttng_add_trigger_ust_ok \ + $TRIGGER_NAME \ + --condition on-event -u "tp:tptest" \ + --action stop-session $SESSION_NAME \ + --action notify + + # Launch notification listener. + $NOTIFICATION_CLIENT_BIN \ + --trigger $TRIGGER_NAME \ + --sync-after-notif-register "$SYNC_AFTER_NOTIF_REGISTER_PATH" + notif_client_pid=$! + + while [ ! -f "${SYNC_AFTER_NOTIF_REGISTER_PATH}" ]; do + sleep 0.5 + done + + # Artificially produce the desired event-rule condition. + $GEN_UST_EVENTS_TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 + + # notification-client will exit once it receives a notification. + wait $notif_client_pid + test "$?" -eq "0" + ok $? "notification client exited successfully" + + # Test that the session has been stopped. + lttng_session_is_inactive $SESSION_NAME + + # Tearing down. + lttng_remove_trigger_ust_ok $TRIGGER_NAME + destroy_lttng_session_ok $SESSION_NAME + + rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH" + rm -rf "$TRACE_PATH" +} + + # MUST set TESTDIR before calling those functions +plan_tests $NUM_TESTS + +print_test_banner "$TEST_DESC" + +start_lttng_sessiond_notap + +test_start_session_action +test_stop_session_action + +stop_lttng_sessiond_notap diff --git a/tests/regression/tools/trigger/utils/Makefile.am b/tests/regression/tools/trigger/utils/Makefile.am new file mode 100644 index 000000000..4b716cadb --- /dev/null +++ b/tests/regression/tools/trigger/utils/Makefile.am @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only + +AM_CFLAGS += -I$(srcdir) -I$(top_srcdir)/tests/utils +LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la + +noinst_PROGRAMS = notification-client +notification_client_SOURCES = notification-client.c +notification_client_LDADD = $(LIBLTTNG_CTL) \ + $(top_builddir)/tests/utils/libtestutils.la diff --git a/tests/regression/tools/trigger/utils/notification-client.c b/tests/regression/tools/trigger/utils/notification-client.c new file mode 100644 index 000000000..aecc478bd --- /dev/null +++ b/tests/regression/tools/trigger/utils/notification-client.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "utils.h" + +static struct option long_options[] = +{ + /* These options set a flag. */ + {"trigger", required_argument, 0, 'i'}, + {"sync-after-notif-register", required_argument, 0, 'a'}, + {0, 0, 0, 0} +}; + +static bool action_group_contains_notify( + const struct lttng_action *action_group) +{ + unsigned int i, count; + enum lttng_action_status status = + lttng_action_group_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_group_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 bool is_expected_trigger_name(const char *expected_trigger_name, + struct lttng_notification *notification) +{ + int ret = false; + 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: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + { + const char *trigger_name; + enum lttng_evaluation_status evaluation_status; + + evaluation_status = + lttng_evaluation_event_rule_get_trigger_name( + evaluation, &trigger_name); + if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { + fprintf(stderr, "Failed to get trigger name of event rule notification\n"); + ret = -1; + break; + } + + ret = true; + break; + } + default: + fprintf(stderr, "Unknown notification type (%d)\n", type); + } + + return ret; +} + +int main(int argc, char **argv) +{ + int ret; + int option; + int option_index; + const char *expected_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; + + while ((option = getopt_long(argc, argv, "a:t:", + long_options, &option_index)) != -1) { + switch (option) { + case 'a': + after_notif_register_file_path = 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"); + 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; + + lttng_trigger_get_name(trigger, &trigger_name); + if (strcmp(trigger_name, expected_trigger_name)) { + continue; + } + + if (!((action_type == LTTNG_ACTION_TYPE_GROUP && + action_group_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) { + printf("No matching trigger with a notify action found.\n"); + ret = 0; + 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"); + 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 = is_expected_trigger_name(expected_trigger_name, + notification); + lttng_notification_destroy(notification); + if (ret) { + ret = 0; + goto end; + } + } +end: + lttng_triggers_destroy(triggers); + lttng_notification_channel_destroy(notification_channel); + return !!ret; +} -- 2.34.1