From 434f80687923fbce645b5b31b44876229559808c Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Wed, 31 May 2017 13:25:29 -0400 Subject: [PATCH] Tests: regression testing for notification API MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This test suite includes tests for low and high buffer usage conditions, triggers, and multi application client scenarios. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau --- .gitignore | 2 + configure.ac | 1 + tests/regression/Makefile.am | 4 +- tests/regression/tools/Makefile.am | 2 +- .../regression/tools/notification/Makefile.am | 57 ++ .../tools/notification/base_client.c | 422 ++++++++++++ .../tools/notification/consumer_testpoints.c | 147 +++++ .../tools/notification/notification.c | 614 ++++++++++++++++++ .../tools/notification/test_notification | 86 +++ .../notification/test_notification_multi_app | 261 ++++++++ tests/utils/utils.sh | 86 ++- 11 files changed, 1655 insertions(+), 27 deletions(-) create mode 100644 tests/regression/tools/notification/Makefile.am create mode 100644 tests/regression/tools/notification/base_client.c create mode 100644 tests/regression/tools/notification/consumer_testpoints.c create mode 100644 tests/regression/tools/notification/notification.c create mode 100755 tests/regression/tools/notification/test_notification create mode 100755 tests/regression/tools/notification/test_notification_multi_app diff --git a/.gitignore b/.gitignore index e80c1890c..eb781239c 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,8 @@ gen-ust-events health_check /tests/regression/tools/mi/extract_xml /tests/regression/tools/mi/validate_xml +/tests/regression/tools/notification/base_client +/tests/regression/tools/notification/notification /tests/regression/ust/overlap/demo/demo /tests/regression/ust/linking/demo_builtin /tests/regression/ust/linking/demo_static diff --git a/configure.ac b/configure.ac index 9239e3b0b..2fa4e96a1 100644 --- a/configure.ac +++ b/configure.ac @@ -1054,6 +1054,7 @@ AC_CONFIG_FILES([ tests/regression/tools/crash/Makefile tests/regression/tools/regen-metadata/Makefile tests/regression/tools/regen-statedump/Makefile + tests/regression/tools/notification/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 86a55634b..c52c3c614 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -23,7 +23,9 @@ TESTS = tools/filtering/test_invalid_filter \ tools/wildcard/test_event_wildcard \ tools/crash/test_crash \ tools/regen-metadata/test_ust \ - tools/regen-statedump/test_ust + tools/regen-statedump/test_ust \ + tools/notification/test_notification \ + tools/notification/test_notification_multi_app if HAVE_LIBLTTNG_UST_CTL SUBDIRS += ust diff --git a/tests/regression/tools/Makefile.am b/tests/regression/tools/Makefile.am index 76374c754..fcbf90af2 100644 --- a/tests/regression/tools/Makefile.am +++ b/tests/regression/tools/Makefile.am @@ -1,2 +1,2 @@ SUBDIRS = streaming filtering health tracefile-limits snapshots live exclusion save-load mi \ - wildcard crash regen-metadata regen-statedump + wildcard crash regen-metadata regen-statedump notification diff --git a/tests/regression/tools/notification/Makefile.am b/tests/regression/tools/notification/Makefile.am new file mode 100644 index 000000000..540b81822 --- /dev/null +++ b/tests/regression/tools/notification/Makefile.am @@ -0,0 +1,57 @@ +AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_srcdir)/tests -I$(top_srcdir)/tests/utils/ -I$(srcdir) +AM_LDFLAGS = + + +LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la +LIB_LTTNG_CTL = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la + +noinst_PROGRAMS = base_client notification + +if NO_SHARED + +CLEANFILES = libpause_consumer.so libpause_consumer.so.debug +EXTRA_DIST = test_notification test_notification_multi_app base_client.c notification.c consumer_testpoints.c + +else + +# In order to test the health check feature, the helper library +# must be built as .so to be able to LD_PRELOAD it. +FORCE_SHARED_LIB_OPTIONS = -module -shared -avoid-version \ + -rpath $(abs_builddir) + +libpause_consumer_la_SOURCES = consumer_testpoints.c +libpause_consumer_la_LIBADD = $(top_builddir)/src/common/libcommon.la +libpause_consumer_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS) +noinst_LTLIBRARIES = libpause_consumer.la + +base_client_SOURCES = base_client.c +base_client_LDADD = $(LIB_LTTNG_CTL) + +notification_SOURCES = notification.c +notification_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm + +if LTTNG_TOOLS_BUILD_WITH_LIBDL +LIBS += -ldl +endif +if LTTNG_TOOLS_BUILD_WITH_LIBC_DL +LIBS += -lc +endif + +noinst_SCRIPTS = test_notification test_notification_multi_app +EXTRA_DIST = test_notification test_notification_multi_app + + +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 +endif diff --git a/tests/regression/tools/notification/base_client.c b/tests/regression/tools/notification/base_client.c new file mode 100644 index 000000000..21a8ac3fe --- /dev/null +++ b/tests/regression/tools/notification/base_client.c @@ -0,0 +1,422 @@ +/* + * base_client.c + * + * Base client application for testing of LTTng notification API + * + * Copyright 2017 Jonathan Rajotte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int nr_notifications = 0; +static unsigned int nr_expected_notifications = 0; +static const char *session_name = NULL; +static const char *channel_name = NULL; +static double threshold_ratio = 0.0; +static uint64_t threshold_bytes = 0; +static bool is_threshold_ratio = false; +static enum lttng_condition_type buffer_usage_type = LTTNG_CONDITION_TYPE_UNKNOWN; +static enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + +int handle_condition( + const struct lttng_condition *condition, + const struct lttng_evaluation *condition_evaluation); + +int parse_arguments(char **argv) { + const char *domain_type_string = NULL; + const char *buffer_usage_type_string = NULL; + const char *buffer_usage_threshold_type = NULL; + const char *buffer_usage_threshold_value = NULL; + const char *nr_expected_notifications_string = NULL; + + session_name = argv[1]; + channel_name = argv[2]; + domain_type_string = argv[3]; + buffer_usage_type_string = argv[4]; + buffer_usage_threshold_type = argv[5]; + buffer_usage_threshold_value = argv[6]; + nr_expected_notifications_string = argv[7]; + + /* Parse arguments */ + /* Domain type */ + if (!strcasecmp("LTTNG_DOMAIN_UST", domain_type_string)) { + domain_type = LTTNG_DOMAIN_UST; + } + if (!strcasecmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) { + domain_type = LTTNG_DOMAIN_KERNEL; + } + if (domain_type == LTTNG_DOMAIN_NONE) { + printf("error: Unknown domain type\n"); + goto error; + } + + /* Buffer usage condition type */ + if (!strcasecmp("low", buffer_usage_type_string)) { + buffer_usage_type = LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW; + } + if (!strcasecmp("high", buffer_usage_type_string)) { + buffer_usage_type = LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH; + } + if (buffer_usage_type == LTTNG_CONDITION_TYPE_UNKNOWN) { + printf("error: Unknown condition type\n"); + goto error; + } + + /* Ratio or bytes ? */ + if (!strcasecmp("bytes", buffer_usage_threshold_type)) { + is_threshold_ratio = false; + sscanf(buffer_usage_threshold_value, "%" SCNu64, &threshold_bytes); + } + + if (!strcasecmp("ratio", buffer_usage_threshold_type)) { + is_threshold_ratio = true; + sscanf(buffer_usage_threshold_value, "%lf", &threshold_ratio); + } + + /* Number of notification to expect */ + sscanf(nr_expected_notifications_string, "%d", &nr_expected_notifications); + + return 0; +error: + return 1; +} + +int main(int argc, char **argv) +{ + int ret = 0; + enum lttng_condition_status condition_status; + enum lttng_notification_channel_status nc_status; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + + /* + * Disable buffering on stdout. + * Safety measure to prevent hang on the validation side since + * stdout is used for outside synchronization. + */ + setbuf(stdout, NULL); + + if (argc < 8) { + printf("error: Missing arguments for tests\n"); + ret = 1; + goto end; + } + + ret = parse_arguments(argv); + if (ret) { + printf("error: Could not parse arguments\n"); + goto end; + } + + /* Setup */ + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + if (!notification_channel) { + printf("error: Could not create notification channel\n"); + ret = 1; + goto end; + } + + switch (buffer_usage_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: + printf("error: Invalid buffer_usage_type\n"); + ret = 1; + goto end; + } + + if (!condition) { + printf("error: Could not create condition object\n"); + ret = 1; + goto end; + } + + if (is_threshold_ratio) { + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + condition, threshold_ratio); + } else { + condition_status = lttng_condition_buffer_usage_set_threshold( + condition, threshold_bytes); + } + + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + printf("error: Could not set threshold\n"); + ret = 1; + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + printf("error: Could not set session name\n"); + ret = 1; + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + printf("error: Could not set channel name\n"); + ret = 1; + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + condition, LTTNG_DOMAIN_UST); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + printf("error: Could not set domain type\n"); + ret = 1; + goto end; + } + + action = lttng_action_notify_create(); + if (!action) { + printf("error: Could not create action notify\n"); + ret = 1; + goto end; + } + + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + printf("error: Could not create trigger\n"); + ret = 1; + goto end; + } + + ret = lttng_register_trigger(trigger); + + /* + * An equivalent trigger might already be registered if an other app + * registered an equivalent trigger. + */ + if (ret < 0 && ret != -LTTNG_ERR_TRIGGER_EXISTS) { + printf("error: %s\n", lttng_strerror(ret)); + ret = 1; + goto end; + } + + nc_status = lttng_notification_channel_subscribe(notification_channel, condition); + if (nc_status != LTTNG_NOTIFICATION_CHANNEL_STATUS_OK) { + printf("error: Could not subscribe\n"); + ret = 1; + goto end; + } + + /* Tell outside process that the client is ready */ + printf("sync: ready\n"); + + for (;;) { + struct lttng_notification *notification; + enum lttng_notification_channel_status status; + const struct lttng_evaluation *notification_evaluation; + const struct lttng_condition *notification_condition; + + if (nr_notifications == nr_expected_notifications) { + ret = 0; + goto end; + } + /* Receive the next notification. */ + status = lttng_notification_channel_get_next_notification( + notification_channel, + ¬ification); + + switch (status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + ret = 1; + printf("error: No drop should be observed during this test app\n"); + goto end; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: + /* + * The notification channel has been closed by the + * session daemon. This is typically caused by a session + * daemon shutting down (cleanly or because of a crash). + */ + printf("error: Notification channel was closed\n"); + ret = 1; + goto end; + default: + /* Unhandled conditions / errors. */ + printf("error: Unknown notification channel status\n"); + ret = 1; + goto end; + } + + notification_condition = lttng_notification_get_condition(notification); + notification_evaluation = lttng_notification_get_evaluation(notification); + + ret = handle_condition(notification_condition, notification_evaluation); + nr_notifications++; + + lttng_notification_destroy(notification); + if (ret != 0) { + goto end; + } + } +end: + if (trigger) { + lttng_unregister_trigger(trigger); + } + lttng_notification_channel_unsubscribe(notification_channel, condition); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + lttng_action_destroy(action); + lttng_notification_channel_destroy(notification_channel); + printf("exit: %d\n", ret); + return ret; +} + +int handle_condition( + const struct lttng_condition *condition, + const struct lttng_evaluation *evaluation) +{ + int ret = 0; + const char *string_low = "low"; + const char *string_high = "high"; + const char *string_condition_type = NULL; + const char *condition_session_name = NULL; + const char *condition_channel_name = NULL; + enum lttng_condition_type condition_type; + enum lttng_domain_type condition_domain_type; + double buffer_usage_ratio; + uint64_t buffer_usage_bytes; + + condition_type = lttng_condition_get_type(condition); + + if (condition_type != buffer_usage_type) { + ret = 1; + printf("error: condition type and buffer usage type are not the same\n"); + goto end; + } + + /* Fetch info to test */ + lttng_condition_buffer_usage_get_session_name(condition, + &condition_session_name); + lttng_condition_buffer_usage_get_channel_name(condition, + &condition_channel_name); + lttng_condition_buffer_usage_get_domain_type(condition, + &condition_domain_type); + + if (strcmp(condition_session_name, session_name) != 0) { + printf("error: session name differs\n"); + ret = 1; + goto end; + } + + if (strcmp(condition_channel_name, channel_name) != 0) { + printf("error: channel name differs\n"); + ret = 1; + goto end; + } + + if (condition_domain_type != domain_type) { + printf("error: domain type differs\n"); + ret = 1; + goto end; + } + + if (is_threshold_ratio) { + lttng_evaluation_buffer_usage_get_usage_ratio( + evaluation, &buffer_usage_ratio); + switch (condition_type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + if (buffer_usage_ratio > threshold_ratio) { + printf("error: buffer usage ratio is bigger than set threshold ratio\n"); + ret = 1; + goto end; + } + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + if (buffer_usage_ratio < threshold_ratio) { + printf("error: buffer usage ratio is lower than set threshold ratio\n"); + ret = 1; + goto end; + } + break; + default: + printf("error: Unknown condition type\n"); + ret = 1; + goto end; + } + } else { + lttng_evaluation_buffer_usage_get_usage( + evaluation, &buffer_usage_bytes); + switch (condition_type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + if (buffer_usage_bytes > threshold_bytes) { + printf("error: buffer usage ratio is bigger than set threshold bytes\n"); + ret = 1; + goto end; + } + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + if (buffer_usage_bytes < threshold_bytes) { + printf("error: buffer usage ratio is lower than set threshold bytes\n"); + ret = 1; + goto end; + } + break; + default: + printf("error: Unknown condition type\n"); + ret = 1; + goto end; + } + } + + switch (condition_type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + string_condition_type = string_low; + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + string_condition_type = string_high; + break; + default: + printf("error: Unknown condition type\n"); + ret = 1; + goto end; + } + + printf("notification: %s %d\n", string_condition_type, nr_notifications); +end: + return ret; +} diff --git a/tests/regression/tools/notification/consumer_testpoints.c b/tests/regression/tools/notification/consumer_testpoints.c new file mode 100644 index 000000000..1f5d83ecb --- /dev/null +++ b/tests/regression/tools/notification/consumer_testpoints.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); + +int lttng_opt_verbose; +int lttng_opt_mi; +int lttng_opt_quiet; + +static +void __attribute__((destructor)) pause_pipe_fini(void) +{ + int ret; + + if (pause_pipe_path) { + ret = unlink(pause_pipe_path); + if (ret) { + PERROR("unlink pause pipe"); + } + } + + free(pause_pipe_path); + lttng_pipe_destroy(pause_pipe); +} + +/* + * We use this testpoint, invoked at the start of the consumerd's data handling + * thread to create a named pipe/FIFO which a test application can use to either + * pause or resume the consumption of data. + */ +int __testpoint_consumerd_thread_data(void) +{ + int ret = 0; + const char *pause_pipe_path_prefix, *domain; + + pause_pipe_path_prefix = lttng_secure_getenv( + "CONSUMER_PAUSE_PIPE_PATH"); + if (!pause_pipe_path_prefix) { + ret = -1; + goto end; + } + + /* + * These symbols are exclusive to the consumerd process, hence we can't + * rely on their presence in the sessiond. Not looking-up these symbols + * dynamically would not allow this shared object to be LD_PRELOAD-ed + * when launching the session daemon. + */ + data_consumption_state = dlsym(NULL, "data_consumption_paused"); + assert(data_consumption_state); + lttng_consumer_get_type = dlsym(NULL, "lttng_consumer_get_type"); + assert(lttng_consumer_get_type); + + switch (lttng_consumer_get_type()) { + case LTTNG_CONSUMER_KERNEL: + domain = "kernel"; + break; + case LTTNG_CONSUMER32_UST: + domain = "ust32"; + break; + case LTTNG_CONSUMER64_UST: + domain = "ust64"; + break; + default: + abort(); + } + + ret = asprintf(&pause_pipe_path, "%s-%s", pause_pipe_path_prefix, + domain); + if (ret < 1) { + ERR("Failed to allocate pause pipe path"); + goto end; + } + + DBG("Creating pause pipe at %s", pause_pipe_path); + pause_pipe = lttng_pipe_named_open(pause_pipe_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, O_NONBLOCK); + if (!pause_pipe) { + ERR("Failed to create pause pipe at %s", pause_pipe_path); + ret = -1; + goto end; + } + + /* Only the read end of the pipe is useful to us. */ + ret = lttng_pipe_write_close(pause_pipe); +end: + return ret; +} + +int __testpoint_consumerd_thread_data_poll(void) +{ + int ret = 0; + uint8_t value; + bool value_read = false; + + if (!pause_pipe) { + ret = -1; + goto end; + } + + /* Purge pipe and only consider the freshest value. */ + do { + ret = lttng_pipe_read(pause_pipe, &value, sizeof(value)); + if (ret == sizeof(value)) { + value_read = true; + } + } while (ret == sizeof(value)); + + ret = (errno == EAGAIN) ? 0 : -errno; + + if (value_read) { + *data_consumption_state = !!value; + DBG("Message received on pause pipe: %s data consumption", + *data_consumption_state ? "paused" : "resumed"); + } +end: + return ret; +} diff --git a/tests/regression/tools/notification/notification.c b/tests/regression/tools/notification/notification.c new file mode 100644 index 000000000..f69e884ae --- /dev/null +++ b/tests/regression/tools/notification/notification.c @@ -0,0 +1,614 @@ +/* + * notification.c + * + * Tests suite for LTTng notification API + * + * Copyright (C) 2017 Jonathan Rajotte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NUM_TESTS 104 +int nb_args = 0; +int named_pipe_args_start = 0; + +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"); + ret = -1; + goto end; + } + + ret = close(fd); + if (ret < 0) { + perror("Name pipe closing failed"); + ret = -1; + goto end; + } +end: + return ret; +} + +int stop_consumer(const char **argv) +{ + int ret = 0; + for (int i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 49); + } + return ret; +} + +int resume_consumer(const char **argv) +{ + int ret = 0; + for (int i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 0); + } + return ret; +} + +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) +{ + 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 */ + unsigned int test_vector_size = 5; + for (unsigned int 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) { + 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-registerd 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); +} + +void test_notification_channel(const char *session_name, const char *channel_name, enum lttng_domain_type domain_type, const char **argv) +{ + int ret = 0; + enum lttng_condition_status condition_status; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *action = NULL; + struct lttng_notification *notification = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + + struct lttng_condition *low_condition = NULL; + struct lttng_condition *high_condition = NULL; + struct lttng_condition *dummy_invalid_condition = NULL; + struct lttng_condition *dummy_condition = NULL; + + double low_ratio = 0.0; + double high_ratio = 0.99; + + /* Set-up */ + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + /* Create a dummy, empty condition for later test */ + dummy_invalid_condition = lttng_condition_buffer_usage_low_create(); + if (!dummy_invalid_condition) { + fail("Setup error on condition creation"); + goto end; + } + + /* 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; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + dummy_condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on dummy_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + dummy_condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on dummy_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + dummy_condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on dummy_condition creation"); + goto end; + } + + /* Register a low condition with a ratio */ + low_condition = lttng_condition_buffer_usage_low_create(); + if (!low_condition) { + fail("Setup error on low_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + low_condition, low_ratio); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + low_condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + low_condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + low_condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + + } + + /* Register a high condition with a ratio */ + high_condition = lttng_condition_buffer_usage_high_create(); + if (!high_condition) { + fail("Setup error on high_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + high_condition, high_ratio); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + high_condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + high_condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + high_condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + + /* Register the triggers for low and high condition */ + trigger = lttng_trigger_create(low_condition, action); + if (!trigger) { + fail("Setup error on low trigger creation"); + goto end; + } + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on low trigger registration"); + goto end; + } + + lttng_trigger_destroy(trigger); + trigger = NULL; + + trigger = lttng_trigger_create(high_condition, action); + if (!trigger) { + fail("Setup error on high trigger creation"); + goto end; + } + + ret = lttng_register_trigger(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; + } + + /* Basic error path check */ + 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, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NON-NULL"); + + 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 to an invalid condition"); + + nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, "Unsubscribing to an valid unknown condition"); + + /* 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 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 condition"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); + + /* Wait for notification to happen */ + lttng_start_tracing(session_name); + stop_consumer(argv); + + /* Wait for high notification */ + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + 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; + + resume_consumer(argv); + lttng_stop_tracing(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"); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + 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 */ + lttng_start_tracing(session_name); + stop_consumer(argv); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + 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; + + /* Resume consumer to allow event consumption */ + resume_consumer(argv); + lttng_stop_tracing(session_name); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + 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 to force a high notification */ + lttng_start_tracing(session_name); + stop_consumer(argv); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + 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; + + /* Resume consumer to allow event consumption */ + resume_consumer(argv); + lttng_stop_tracing(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(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(low_condition); + lttng_condition_destroy(dummy_invalid_condition); + lttng_condition_destroy(dummy_condition); +} + +int main(int argc, const char *argv[]) +{ + const char *session_name = NULL; + const char *channel_name = NULL; + const char *domain_type_string = NULL; + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + + plan_tests(NUM_TESTS); + + /* Argument 4 and upward are named pipe location for consumerd control */ + named_pipe_args_start = 4; + + if (argc < 5) { + fail("Missing parameter for tests to run %d", argc); + goto error; + } + + nb_args = argc; + + session_name = argv[1]; + channel_name = argv[2]; + domain_type_string= argv[3]; + + 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; + } + + 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 notification channel api for domain %s", domain_type_string); + test_notification_channel(session_name, channel_name, domain_type, argv); +error: + return exit_status(); +} + diff --git a/tests/regression/tools/notification/test_notification b/tests/regression/tools/notification/test_notification new file mode 100755 index 000000000..48a5a16cb --- /dev/null +++ b/tests/regression/tools/notification/test_notification @@ -0,0 +1,86 @@ +#!/bin/bash +# +# Copyright (C) - 2017 Jonathan Rajotte-Julien +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$CURDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOIT_ARGS="CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LTTNG_TESTPOINT_ENABLE=1" +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" + +NR_ITER=-1 +NR_USEC_WAIT=5 + +SESSION_NAME="my_session" +CHANNEL_NAME="my_ust_channel" +EVENT_NAME="tp:tptest" + +TRACE_PATH=$(mktemp -d) + +DIR=$(readlink -f $TESTDIR) + +source $TESTDIR/utils/utils.sh + +consumerd_pipe=() + +file_sync_after_first_event=$(mktemp -u) + +LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}" +start_lttng_sessiond_notap + +create_lttng_session_notap $SESSION_NAME $TRACE_PATH + +enable_ust_lttng_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size=4096 +enable_ust_lttng_event_notap $SESSION_NAME $EVENT_NAME $CHANNEL_NAME + +#This is needed since the testpoint create a pipe with the consumer type suffixed +for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") +done + + +# Start app in infinite loop +$TESTAPP_BIN $NR_ITER $NR_USEC_WAIT $file_sync_after_first_event & +APP_PID=$! +# Pin to CPU zero to force specific sub buffer usage +taskset -p -c 0 $APP_PID > /dev/null 2>&1 + +while [ ! -f "${file_sync_after_first_event}" ]; do + sleep 0.5 +done +rm ${file_sync_after_first_event} + +# The actual test suite +# TODO: Add support for kernel domain +$CURDIR/notification $SESSION_NAME $CHANNEL_NAME LTTNG_DOMAIN_UST ${consumerd_pipe[@]} + +stop_lttng_sessiond_notap + +# On ungraceful kill the app is cleaned up via the full_cleanup call +# Suppress kill message +kill -9 $APP_PID +wait $APP_PID 2> /dev/null + +# Just in case cleanup +rm -rf $TRACE_PATH +rm ${consumerd_pipe[@]} 2> /dev/null diff --git a/tests/regression/tools/notification/test_notification_multi_app b/tests/regression/tools/notification/test_notification_multi_app new file mode 100755 index 000000000..a8f0de9f9 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_multi_app @@ -0,0 +1,261 @@ +#!/bin/bash +# +# Copyright (C) - 2017 Jonathan Rajotte > +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +TEST_DESC="Notification" + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +# This is needed since the testpoint creates a pipe with the consumerd domain +# suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$CURDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOIT_ARGS="CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LTTNG_TESTPOINT_ENABLE=1" +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" + +NR_ITER=-1 +NR_USEC_WAIT=5 + +SESSION_NAME="my_session" +UST_CHANNEL_NAME="my_ust_channel" +EVENT_NAME="tp:tptest" + +NR_NOTIFICATION_EXPECTED=5 +NR_CLIENT_APP=50 + +TRACE_PATH=$(mktemp -d) + +DIR=$(readlink -f $TESTDIR) + +NUM_TESTS=46 + +source $TESTDIR/utils/utils.sh + +consumerd_pipe=() +file_sync_after_first_event=$(mktemp -u) + +# MUST set TESTDIR before calling those functions +plan_tests $NUM_TESTS + +print_test_banner "$TEST_DESC" + +app_pids=() +function start_client { + local pid=-1 + local output_file=$1 + local session_name=$2 + local channel_name=$3 + local domain_type=$4 + local buffer_usage_type=$5 + local buffer_usage_threshold_type=$6 + local buffer_usage_threshold_value=$7 + local nr_expected_notification=$8 + + ${CURDIR}/base_client ${session_name} ${channel_name} ${domain_type} ${buffer_usage_type} ${buffer_usage_threshold_type} ${buffer_usage_threshold_value} ${nr_expected_notification} > ${output_file} & + pid=$! + + app_pids+=("$pid") +} + +function wait_for_message () +{ + local file_pattern=$1 + local message=$2 + + for file in $CURDIR/${file_pattern}*; do + while(true); do + # Check for "error" message + grep -q "error:" ${file} + app_error=$? + if [ $app_error -eq "0" ] ; then + # An error occurred + fail "Waiting message: error logged see file content: ${message}, ${file}" + return 1 + fi + + grep -q "${message}" ${file} + if [[ "$?" -ne "0" ]]; then + # Lookup failed restart loop + diag "Lookup failed sleep and retry grep for: ${message}, ${file}" + sleep 0.25 + continue + fi + break + done + done + pass "Message received: ${message}" + return 0 +} + +function print_errors () +{ + local file_pattern=$1 + + for file in $CURDIR/${file_pattern}*; do + # Check for "error" message + error_message=$(grep "error:" ${file}) + if [[ "${error_message}" -ne "" ]]; then + diag "Errors for application ${file}:" + diag "${error_message}" + fi + done +} + +function comm_consumerd () +{ + local message=$1 + local pipe=$2 + echo -ne "${message}" > "${pipe}" + return $? +} + +function stop_consumerd () +{ + local pipe=$1 + comm_consumerd "1" "$pipe" + ok $? "Stopping consumption consumerd" +} + +function resume_consumerd () +{ + local pipe=$1 + comm_consumerd "\0" "$pipe" + ok $? "Resuming consumerd" +} + +function test_multi_app () +{ + local app_pids=() + local low_output_file_pattern="low_app_output_file_" + local high_output_file_pattern="high_app_output_file_" + + # Cleanup + rm ${CURDIR}/${low_output_file_pattern}* 2> /dev/null + rm ${CURDIR}/${high_output_file_pattern}* 2> /dev/null + + # Setup + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}" + start_lttng_sessiond + + # Start app in infinite loop + $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT $file_sync_after_first_event & + app_pid=$! + # Pin to CPU zero to force specific sub buffer usage + taskset -p -c 0 $app_pid > /dev/null 2>&1 + + # Wait for sync with app + while [ ! -f "${file_sync_after_first_event}" ]; do + sleep 0.5 + done + rm ${file_sync_after_first_event} + + create_lttng_session_ok $SESSION_NAME $TRACE_PATH + enable_ust_lttng_channel_ok $SESSION_NAME $UST_CHANNEL_NAME --subbuf-size=4096 + enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME $UST_CHANNEL_NAME + + # Fetch consumerd testpoint pipe information + # This is needed since the testpoint create a pipe with the consumer type suffixed + for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") + done + + for (( i = 0; i < $NR_CLIENT_APP; i++ )); do + low_app_output_file=$CURDIR/${low_output_file_pattern}${i} + high_app_output_file=$CURDIR/${high_output_file_pattern}${i} + start_client $low_app_output_file $SESSION_NAME $UST_CHANNEL_NAME LTTNG_DOMAIN_UST LOW RATIO 0.0 $NR_NOTIFICATION_EXPECTED + start_client $high_app_output_file $SESSION_NAME $UST_CHANNEL_NAME LTTNG_DOMAIN_UST HIGH RATIO 0.420 $NR_NOTIFICATION_EXPECTED + done + + wait_for_message "${low_output_file_pattern}" "sync: ready" + wait_for_message "${high_output_file_pattern}" "sync: ready" + + # Test notification reception + for (( i = 0; i < $NR_NOTIFICATION_EXPECTED; i++ )); do + + # Stop consumerd consumption to force high notification + start_lttng_tracing_ok $SESSION_NAME + for pipe in "${consumerd_pipe[@]}"; do + stop_consumerd "${pipe}" + done + + wait_for_message "${high_output_file_pattern}" "notification: high $i" + + # Resume consumerd + for pipe in "${consumerd_pipe[@]}"; do + resume_consumerd "${pipe}" + done + # Stop tracing forcing full buffer consumption + stop_lttng_tracing $SESSION_NAME + + # Check for notifications reception + wait_for_message "${low_output_file_pattern}" "notification: low $i" + ret=$? + ok $ret "Notifications $i received" + if [[ $ret -ne "0" ]]; then + # Error occurred bail out + break; + fi + done + + wait_for_message "${low_output_file_pattern}" "exit: 0" + ret=$? + ok $ret "Application for low notification terminated normally" + if [[ $ret -eq "0" ]]; then + rm ${CURDIR}/${low_output_file_pattern}* 2> /dev/null + else + # Keep the file + print_errors "${low_output_file_pattern}" + fi + + wait_for_message "${high_output_file_pattern}" "exit: 0" + ret=$? + ok $ret "Application for high notification terminated normally" + if [[ $ret -eq "0" ]]; then + rm ${CURDIR}/${high_output_file_pattern}* 2> /dev/null + else + # Keep the file + print_errors "${high_output_file_pattern}" + fi + + kill -9 $app_pid + wait $app_pid 2> /dev/null + + stop_lttng_sessiond +} + + +TESTS=( + test_multi_app +) + +for fct_test in ${TESTS[@]}; +do + TRACE_PATH=$(mktemp -d) + + ${fct_test} + if [ $? -ne 0 ]; then + break; + fi + + # Only delete if successful + rm -rf $TRACE_PATH +done + diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh index 100f4fd2a..567929aa0 100644 --- a/tests/utils/utils.sh +++ b/tests/utils/utils.sh @@ -687,57 +687,81 @@ function create_lttng_session_no_output () function create_lttng_session () { - local expected_to_fail=$1 - local sess_name=$2 - local trace_path=$3 - local opt=$4 + local withtap=$1 + local expected_to_fail=$2 + local sess_name=$3 + local trace_path=$4 + local opt=$5 $TESTDIR/../src/bin/lttng/$LTTNG_BIN create $sess_name -o $trace_path $opt > $OUTPUT_DEST ret=$? - if [[ $expected_to_fail -eq "1" ]]; then + if [ $expected_to_fail -eq "1" ]; then test "$ret" -ne "0" - ok $? "Create session $sess_name in $trace_path failed as expected" + ret=$? + if [ $withtap -eq "1" ]; then + ok $ret "Create session $sess_name in $trace_path failed as expected" + fi else - ok $ret "Create session $sess_name in $trace_path" + if [ $withtap -eq "1" ]; then + ok $ret "Create session $sess_name in $trace_path" + fi fi + return $ret } function create_lttng_session_ok () { - create_lttng_session 0 "$@" + create_lttng_session 1 0 "$@" } function create_lttng_session_fail () { - create_lttng_session 1 "$@" + create_lttng_session 1 1 "$@" +} + +function create_lttng_session_notap () +{ + create_lttng_session 0 0 "$@" } function enable_ust_lttng_channel () { - local expected_to_fail=$1 - local sess_name=$2 - local channel_name=$3 - local opt=$4 + local withtap=$1 + local expected_to_fail=$2 + local sess_name=$3 + local channel_name=$4 + local opt=$5 $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-channel -u $channel_name -s $sess_name $opt 1> $OUTPUT_DEST 2> $ERROR_OUTPUT_DEST ret=$? if [[ $expected_to_fail -eq "1" ]]; then test "$ret" -ne "0" - ok $? "Enable channel $channel_name for session $sess_name failed as expected" + ret=$? + if [ $withtap -eq "1" ]; then + ok $ret "Enable channel $channel_name for session $sess_name failed as expected" + fi else - ok $ret "Enable channel $channel_name for session $sess_name" + if [ $withtap -eq "1" ]; then + ok $ret "Enable channel $channel_name for session $sess_name" + fi fi + return $ret } function enable_ust_lttng_channel_ok () { - enable_ust_lttng_channel 0 "$@" + enable_ust_lttng_channel 1 0 "$@" } function enable_ust_lttng_channel_fail () { - enable_ust_lttng_channel 1 "$@" + enable_ust_lttng_channel 1 1 "$@" +} + +function enable_ust_lttng_channel_notap () +{ + enable_ust_lttng_channel 0 0 "$@" } function disable_ust_lttng_channel() @@ -787,10 +811,11 @@ function enable_lttng_mmap_overwrite_ust_channel() function enable_ust_lttng_event () { - local expected_to_fail=$1 - local sess_name=$2 - local event_name="$3" - local channel_name=$4 + local withtap=$1 + local expected_to_fail=$2 + local sess_name=$3 + local event_name="$4" + local channel_name=$5 if [ -z $channel_name ]; then # default channel if none specified @@ -803,20 +828,31 @@ function enable_ust_lttng_event () ret=$? if [[ $expected_to_fail -eq "1" ]]; then test $ret -ne "0" - ok $? "Enable ust event $event_name for session $session_name failed as expected" + ret=$? + if [[ $withtap -eq "1" ]]; then + ok $ret "Enable ust event $event_name for session $session_name failed as expected" + fi else - ok $ret "Enable ust event $event_name for session $sess_name" + if [[ $withtap -eq "1" ]]; then + ok $ret "Enable ust event $event_name for session $sess_name" + fi fi + return $ret } function enable_ust_lttng_event_ok () { - enable_ust_lttng_event 0 "$@" + enable_ust_lttng_event 1 0 "$@" } function enable_ust_lttng_event_fail () { - enable_ust_lttng_event 1 "$@" + enable_ust_lttng_event 1 1 "$@" +} + +function enable_ust_lttng_event_notap () +{ + enable_ust_lttng_event 0 0 "$@" } function enable_jul_lttng_event() -- 2.34.1