Tests: regression testing for notification API
authorJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Wed, 31 May 2017 17:25:29 +0000 (13:25 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Thu, 1 Jun 2017 15:25:38 +0000 (11:25 -0400)
This test suite includes tests for low and high buffer usage conditions,
triggers, and multi application client scenarios.

Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
.gitignore
configure.ac
tests/regression/Makefile.am
tests/regression/tools/Makefile.am
tests/regression/tools/notification/Makefile.am [new file with mode: 0644]
tests/regression/tools/notification/base_client.c [new file with mode: 0644]
tests/regression/tools/notification/consumer_testpoints.c [new file with mode: 0644]
tests/regression/tools/notification/notification.c [new file with mode: 0644]
tests/regression/tools/notification/test_notification [new file with mode: 0755]
tests/regression/tools/notification/test_notification_multi_app [new file with mode: 0755]
tests/utils/utils.sh

index e80c1890c67d834048fbac691822aa0e2f9b2670..eb781239cd341cc9da1a58f0579eaad46acf07f7 100644 (file)
@@ -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
index 9239e3b0b0f8afcc34d080917fab4333ad6ed99b..2fa4e96a14fba24e03d54be85cc2eecc96a65ab2 100644 (file)
@@ -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
index 86a55634b4c67209fad078d172765cca2d69dd61..c52c3c61491aa1c17ddadde732e8a9df5dedabb1 100644 (file)
@@ -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
index 76374c75461ccfabe3268718605c4061c73c8656..fcbf90af2949e53d0d2da6615299109688ad15d2 100644 (file)
@@ -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 (file)
index 0000000..540b818
--- /dev/null
@@ -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 (file)
index 0000000..21a8ac3
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * 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,
+                               &notification);
+
+               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 (file)
index 0000000..1f5d83e
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * 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;
+}
diff --git a/tests/regression/tools/notification/notification.c b/tests/regression/tools/notification/notification.c
new file mode 100644 (file)
index 0000000..f69e884
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * 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, &notification);
+       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, &notification);
+       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, &notification);
+       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, &notification);
+       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, &notification);
+       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 (executable)
index 0000000..48a5a16
--- /dev/null
@@ -0,0 +1,86 @@
+#!/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
diff --git a/tests/regression/tools/notification/test_notification_multi_app b/tests/regression/tools/notification/test_notification_multi_app
new file mode 100755 (executable)
index 0000000..a8f0de9
--- /dev/null
@@ -0,0 +1,261 @@
+#!/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
+
index 100f4fd2a1fc02566518168a2d2cd57447d71061..567929aa08840f79e251855500b74f7584f36331 100644 (file)
@@ -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 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 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 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()
This page took 0.044968 seconds and 4 git commands to generate.