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
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
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
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
--- /dev/null
+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
--- /dev/null
+/*
+ * base_client.c
+ *
+ * Base client application for testing of LTTng notification API
+ *
+ * Copyright 2017 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <lttng/action/action.h>
+#include <lttng/action/notify.h>
+#include <lttng/condition/buffer-usage.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/evaluation.h>
+#include <lttng/domain.h>
+#include <lttng/endpoint.h>
+#include <lttng/notification/channel.h>
+#include <lttng/notification/notification.h>
+#include <lttng/trigger/trigger.h>
+
+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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2017 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * 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 <common/compat/getenv.h>
+#include <common/consumer/consumer.h>
+#include <common/pipe.h>
+#include <common/error.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <lttng/constant.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <assert.h>
+#include <stdio.h>
+
+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;
+}
--- /dev/null
+/*
+ * notification.c
+ *
+ * Tests suite for LTTng notification API
+ *
+ * Copyright (C) 2017 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * 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 <assert.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <lttng/action/action.h>
+#include <lttng/action/notify.h>
+#include <lttng/condition/buffer-usage.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/evaluation.h>
+#include <lttng/domain.h>
+#include <lttng/endpoint.h>
+#include <lttng/lttng-error.h>
+#include <lttng/notification/channel.h>
+#include <lttng/notification/notification.h>
+#include <lttng/trigger/trigger.h>
+
+#include <tap/tap.h>
+
+#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();
+}
+
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) - 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# 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
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) - 2017 Jonathan Rajotte <jonathan.rajotte-julien@efficiso.com>>
+#
+# 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
+
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()
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
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()