SoW-2019-0002: Dynamic Snapshot sow-2019-0002-rev1
authorFrancis Deslauriers <francis.deslauriers@efficios.com>
Tue, 11 Feb 2020 20:51:52 +0000 (15:51 -0500)
committerJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Sat, 15 Feb 2020 00:09:31 +0000 (19:09 -0500)
Revision 1

266 files changed:
.gitignore
configure.ac
doc/examples/trigger-on-event/README.md [new file with mode: 0644]
doc/examples/trigger-on-event/demo.sh [new file with mode: 0755]
doc/examples/trigger-on-event/instrumented-app.c [new file with mode: 0644]
doc/examples/trigger-on-event/notification-client.c [new file with mode: 0644]
doc/examples/trigger-on-event/tp.c [new file with mode: 0644]
doc/examples/trigger-on-event/tp.h [new file with mode: 0644]
doc/man/Makefile.am
doc/man/lttng-add-trigger.1.txt [new file with mode: 0644]
doc/man/lttng-list-triggers.1.txt [new file with mode: 0644]
doc/man/lttng-remove-trigger.1.txt [new file with mode: 0644]
extras/bindings/swig/python/lttng.i.in
include/Makefile.am
include/lttng/action/action-internal.h
include/lttng/action/action.h
include/lttng/action/group-internal.h [new file with mode: 0644]
include/lttng/action/group.h [new file with mode: 0644]
include/lttng/action/notify-internal.h
include/lttng/action/rotate-session-internal.h [new file with mode: 0644]
include/lttng/action/rotate-session.h [new file with mode: 0644]
include/lttng/action/snapshot-session-internal.h [new file with mode: 0644]
include/lttng/action/snapshot-session.h [new file with mode: 0644]
include/lttng/action/start-session-internal.h [new file with mode: 0644]
include/lttng/action/start-session.h [new file with mode: 0644]
include/lttng/action/stop-session-internal.h [new file with mode: 0644]
include/lttng/action/stop-session.h [new file with mode: 0644]
include/lttng/condition/condition-internal.h
include/lttng/condition/condition.h
include/lttng/condition/event-rule-internal.h [new file with mode: 0644]
include/lttng/condition/event-rule.h [new file with mode: 0644]
include/lttng/domain-internal.h [new file with mode: 0644]
include/lttng/event-internal.h
include/lttng/event-rule/event-rule-internal.h [new file with mode: 0644]
include/lttng/event-rule/event-rule.h [new file with mode: 0644]
include/lttng/event-rule/kprobe-internal.h [new file with mode: 0644]
include/lttng/event-rule/kprobe.h [new file with mode: 0644]
include/lttng/event-rule/kretprobe-internal.h [new file with mode: 0644]
include/lttng/event-rule/kretprobe.h [new file with mode: 0644]
include/lttng/event-rule/syscall-internal.h [new file with mode: 0644]
include/lttng/event-rule/syscall.h [new file with mode: 0644]
include/lttng/event-rule/tracepoint-internal.h [new file with mode: 0644]
include/lttng/event-rule/tracepoint.h [new file with mode: 0644]
include/lttng/event-rule/uprobe-internal.h [new file with mode: 0644]
include/lttng/event-rule/uprobe.h [new file with mode: 0644]
include/lttng/lttng.h
include/lttng/snapshot-internal.h
include/lttng/snapshot.h
include/lttng/trigger/trigger-internal.h
include/lttng/trigger/trigger.h
include/lttng/userspace-probe-internal.h
m4/ax_append_compile_flags.m4 [new file with mode: 0644]
m4/ax_append_flag.m4 [new file with mode: 0644]
m4/ax_check_compile_flag.m4 [new file with mode: 0644]
m4/ax_require_defined.m4 [new file with mode: 0644]
rename.sh [new file with mode: 0644]
src/bin/lttng-crash/lttng-crash.c
src/bin/lttng-relayd/cmd-2-2.c
src/bin/lttng-sessiond/Makefile.am
src/bin/lttng-sessiond/action-executor.c [new file with mode: 0644]
src/bin/lttng-sessiond/action-executor.h [new file with mode: 0644]
src/bin/lttng-sessiond/agent-thread.c
src/bin/lttng-sessiond/agent.c
src/bin/lttng-sessiond/agent.h
src/bin/lttng-sessiond/client.c
src/bin/lttng-sessiond/cmd.c
src/bin/lttng-sessiond/cmd.h
src/bin/lttng-sessiond/dispatch.c
src/bin/lttng-sessiond/event.c
src/bin/lttng-sessiond/event.h
src/bin/lttng-sessiond/globals.c
src/bin/lttng-sessiond/health-sessiond.h
src/bin/lttng-sessiond/kernel.c
src/bin/lttng-sessiond/kernel.h
src/bin/lttng-sessiond/lttng-sessiond.h
src/bin/lttng-sessiond/main.c
src/bin/lttng-sessiond/modprobe.c
src/bin/lttng-sessiond/notification-thread-commands.c
src/bin/lttng-sessiond/notification-thread-commands.h
src/bin/lttng-sessiond/notification-thread-events.c
src/bin/lttng-sessiond/notification-thread-events.h
src/bin/lttng-sessiond/notification-thread-internal.h
src/bin/lttng-sessiond/notification-thread.c
src/bin/lttng-sessiond/notification-thread.h
src/bin/lttng-sessiond/rotate.c
src/bin/lttng-sessiond/sessiond-config.c
src/bin/lttng-sessiond/thread.c
src/bin/lttng-sessiond/trace-kernel.c
src/bin/lttng-sessiond/trace-kernel.h
src/bin/lttng-sessiond/trace-ust.c
src/bin/lttng-sessiond/ust-abi-internal.h
src/bin/lttng-sessiond/ust-app.c
src/bin/lttng-sessiond/ust-app.h
src/bin/lttng-sessiond/ust-ctl-internal.h
src/bin/lttng-sessiond/utils.c
src/bin/lttng-sessiond/utils.h
src/bin/lttng/Makefile.am
src/bin/lttng/command.h
src/bin/lttng/commands/add_context.c
src/bin/lttng/commands/add_trigger.c [new file with mode: 0644]
src/bin/lttng/commands/create.c
src/bin/lttng/commands/disable_events.c
src/bin/lttng/commands/enable_events.c
src/bin/lttng/commands/list_triggers.c [new file with mode: 0644]
src/bin/lttng/commands/remove_trigger.c [new file with mode: 0644]
src/bin/lttng/commands/track-untrack.c
src/bin/lttng/commands/view.c
src/bin/lttng/conf.c
src/bin/lttng/lttng.c
src/bin/lttng/uprobe.c [new file with mode: 0644]
src/bin/lttng/uprobe.h [new file with mode: 0644]
src/bin/lttng/utils.c
src/bin/lttng/utils.h
src/common/Makefile.am
src/common/action.c [deleted file]
src/common/actions/action.c [new file with mode: 0644]
src/common/actions/group.c [new file with mode: 0644]
src/common/actions/notify.c [new file with mode: 0644]
src/common/actions/rotate-session.c [new file with mode: 0644]
src/common/actions/snapshot-session.c [new file with mode: 0644]
src/common/actions/start-session.c [new file with mode: 0644]
src/common/actions/stop-session.c [new file with mode: 0644]
src/common/argpar/Makefile.am [new file with mode: 0644]
src/common/argpar/argpar.c [new file with mode: 0644]
src/common/argpar/argpar.h [new file with mode: 0644]
src/common/buffer-usage.c [deleted file]
src/common/buffer-view.c
src/common/buffer-view.h
src/common/condition.c [deleted file]
src/common/conditions/buffer-usage.c [new file with mode: 0644]
src/common/conditions/condition.c [new file with mode: 0644]
src/common/conditions/event-rule.c [new file with mode: 0644]
src/common/conditions/session-consumed-size.c [new file with mode: 0644]
src/common/conditions/session-rotation.c [new file with mode: 0644]
src/common/credentials.c [new file with mode: 0644]
src/common/credentials.h
src/common/domain.c [new file with mode: 0644]
src/common/dynamic-array.h
src/common/error.c
src/common/error.h
src/common/evaluation.c
src/common/event-rule-kprobe.c [new file with mode: 0644]
src/common/event-rule-kretprobe.c [new file with mode: 0644]
src/common/event-rule-syscall.c [new file with mode: 0644]
src/common/event-rule-tracepoint.c [new file with mode: 0644]
src/common/event-rule-uprobe.c [new file with mode: 0644]
src/common/event-rule.c [new file with mode: 0644]
src/common/filter/Makefile.am [new file with mode: 0644]
src/common/filter/filter-ast.h [new file with mode: 0644]
src/common/filter/filter-bytecode.h [new file with mode: 0644]
src/common/filter/filter-grammar-test.c [new file with mode: 0644]
src/common/filter/filter-ir.h [new file with mode: 0644]
src/common/filter/filter-lexer.l [new file with mode: 0644]
src/common/filter/filter-parser.y [new file with mode: 0644]
src/common/filter/filter-symbols.h [new file with mode: 0644]
src/common/filter/filter-visitor-generate-bytecode.c [new file with mode: 0644]
src/common/filter/filter-visitor-generate-ir.c [new file with mode: 0644]
src/common/filter/filter-visitor-ir-check-binary-comparator.c [new file with mode: 0644]
src/common/filter/filter-visitor-ir-check-binary-op-nesting.c [new file with mode: 0644]
src/common/filter/filter-visitor-ir-normalize-glob-patterns.c [new file with mode: 0644]
src/common/filter/filter-visitor-ir-validate-globbing.c [new file with mode: 0644]
src/common/filter/filter-visitor-ir-validate-string.c [new file with mode: 0644]
src/common/filter/filter-visitor-xml.c [new file with mode: 0644]
src/common/filter/memstream.h [new file with mode: 0644]
src/common/kernel-ctl/kernel-ctl.c
src/common/kernel-ctl/kernel-ctl.h
src/common/kernel-ctl/kernel-ioctl.h
src/common/lttng-elf.c
src/common/lttng-kernel.h
src/common/notification.c
src/common/notify.c [deleted file]
src/common/relayd/relayd.c
src/common/runas.c
src/common/runas.h
src/common/session-consumed-size.c [deleted file]
src/common/session-rotation.c [deleted file]
src/common/sessiond-comm/sessiond-comm.h
src/common/snapshot.c [new file with mode: 0644]
src/common/snapshot.h [new file with mode: 0644]
src/common/trigger.c
src/common/unix.c
src/common/userspace-probe.c
src/common/utils.c
src/common/utils.h
src/lib/lttng-ctl/Makefile.am
src/lib/lttng-ctl/channel.c
src/lib/lttng-ctl/filter/Makefile.am [deleted file]
src/lib/lttng-ctl/filter/filter-ast.h [deleted file]
src/lib/lttng-ctl/filter/filter-bytecode.h [deleted file]
src/lib/lttng-ctl/filter/filter-grammar-test.c [deleted file]
src/lib/lttng-ctl/filter/filter-ir.h [deleted file]
src/lib/lttng-ctl/filter/filter-lexer.l [deleted file]
src/lib/lttng-ctl/filter/filter-parser.y [deleted file]
src/lib/lttng-ctl/filter/filter-symbols.h [deleted file]
src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c [deleted file]
src/lib/lttng-ctl/filter/filter-visitor-generate-ir.c [deleted file]
src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-comparator.c [deleted file]
src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-op-nesting.c [deleted file]
src/lib/lttng-ctl/filter/filter-visitor-ir-normalize-glob-patterns.c [deleted file]
src/lib/lttng-ctl/filter/filter-visitor-ir-validate-globbing.c [deleted file]
src/lib/lttng-ctl/filter/filter-visitor-ir-validate-string.c [deleted file]
src/lib/lttng-ctl/filter/filter-visitor-xml.c [deleted file]
src/lib/lttng-ctl/filter/memstream.h [deleted file]
src/lib/lttng-ctl/lttng-ctl-health.c
src/lib/lttng-ctl/lttng-ctl-helper.h
src/lib/lttng-ctl/lttng-ctl.c
src/lib/lttng-ctl/snapshot.c
tests/regression/Makefile.am
tests/regression/kernel/select_poll_epoll.c
tests/regression/tools/Makefile.am
tests/regression/tools/exclusion/test_exclusion
tests/regression/tools/health/health_exit.c
tests/regression/tools/health/health_fail.c
tests/regression/tools/health/health_stall.c
tests/regression/tools/live/live_test.c
tests/regression/tools/notification/Makefile.am
tests/regression/tools/notification/base_client.c
tests/regression/tools/notification/consumer_testpoints.c
tests/regression/tools/notification/notification.c
tests/regression/tools/notification/test_notification_kernel [deleted file]
tests/regression/tools/notification/test_notification_kernel_buffer_usage [new file with mode: 0755]
tests/regression/tools/notification/test_notification_kernel_error [new file with mode: 0755]
tests/regression/tools/notification/test_notification_kernel_instrumentation [new file with mode: 0755]
tests/regression/tools/notification/test_notification_kernel_syscall [new file with mode: 0755]
tests/regression/tools/notification/test_notification_kernel_userspace_probe [new file with mode: 0755]
tests/regression/tools/notification/test_notification_ust [deleted file]
tests/regression/tools/notification/test_notification_ust_buffer_usage [new file with mode: 0755]
tests/regression/tools/notification/test_notification_ust_error [new file with mode: 0755]
tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion [new file with mode: 0755]
tests/regression/tools/rotation/schedule_api.c
tests/regression/tools/snapshots/ust_test
tests/regression/tools/trigger/Makefile.am [new file with mode: 0644]
tests/regression/tools/trigger/base_client.c [new file with mode: 0644]
tests/regression/tools/trigger/consumer_testpoints.c [new file with mode: 0644]
tests/regression/tools/trigger/test_add_trigger_cli [new file with mode: 0755]
tests/regression/tools/trigger/test_list_triggers_cli [new file with mode: 0755]
tests/regression/tools/trigger/test_remove_trigger_cli [new file with mode: 0755]
tests/regression/tools/trigger/test_trigger_kernel [new file with mode: 0755]
tests/regression/tools/trigger/test_trigger_ust [new file with mode: 0755]
tests/regression/tools/trigger/trigger.c [new file with mode: 0644]
tests/regression/ust/clock-override/lttng-ust-clock-override-test.c
tests/regression/ust/fork/fork.c
tests/regression/ust/getcpu-override/lttng-ust-getcpu-override-test.c
tests/regression/ust/high-throughput/main.c
tests/regression/ust/low-throughput/tp.h
tests/regression/ust/multi-lib/callsites.c
tests/regression/ust/multi-lib/multi-lib-test.c
tests/unit/Makefile.am
tests/unit/test_buffer_view.c [new file with mode: 0644]
tests/unit/test_condition.c [new file with mode: 0644]
tests/unit/test_directory_handle.c
tests/unit/test_event_rule.c [new file with mode: 0644]
tests/unit/test_fd_tracker.c
tests/unit/test_relayd_backward_compat_group_by_session.c
tests/unit/test_session.c
tests/unit/test_utils_expand_path.c
tests/unit/test_utils_parse_size_suffix.c
tests/unit/test_utils_parse_time_suffix.c
tests/utils/tap/tap.c
tests/utils/tap/tap.h
tests/utils/testapp/gen-syscall-events-callstack/Makefile.am
tests/utils/testapp/gen-syscall-events/gen-syscall-events.c
tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c
tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.c
tests/utils/testapp/userspace-probe-elf-binary/Makefile.am
tests/utils/utils.sh

index 34f133a2574ee3b038c6fddd4a2a90f5ed9ac976..b92dac27c7631bfaec53e5e9982ae534ad32baf9 100644 (file)
@@ -61,11 +61,11 @@ compile_commands.json
 /src/bin/lttng-crash/lttng-crash
 /src/bin/lttng-relayd/lttng-relayd
 /src/lib/lttng-ctl/lttng-ctl.pc
-/src/lib/lttng-ctl/filter/filter-grammar-test
-/src/lib/lttng-ctl/filter/filter-lexer.c
-/src/lib/lttng-ctl/filter/filter-parser.c
-/src/lib/lttng-ctl/filter/filter-parser.h
-/src/lib/lttng-ctl/filter/filter-parser.output
+/src/common/filter/filter-grammar-test
+/src/common/filter/filter-lexer.c
+/src/common/filter/filter-parser.c
+/src/common/filter/filter-parser.h
+/src/common/filter/filter-parser.output
 
 /extras/bindings/swig/python/lttng.i
 /extras/bindings/swig/python/lttng.py
@@ -88,6 +88,8 @@ compile_commands.json
 /tests/unit/test_directory_handle
 /tests/unit/test_relayd_backward_compat_group_by_session
 /tests/unit/test_fd_tracker
+/tests/unit/test_event_rule
+/tests/unit/test_condition
 kernel_all_events_basic
 kernel_event_basic
 ust_global_event_wildcard
@@ -105,6 +107,8 @@ health_check
 /tests/regression/tools/notification/notification
 /tests/regression/tools/rotation/schedule_api
 /tests/regression/tools/notification/rotation
+/tests/regression/tools/trigger/base_client
+/tests/regression/tools/trigger/trigger
 /tests/regression/ust/overlap/demo/demo
 /tests/regression/ust/linking/demo_builtin
 /tests/regression/ust/linking/demo_static
index 852c61f0e246e025849d3a63256b34977cc01b10..2ea9571ff6cdd0854bcf79d8759f117b4cc7c821 100644 (file)
@@ -26,6 +26,31 @@ AC_PROG_CXX
 RW_PROG_CXX_WORKS
 AM_CONDITIONAL([CXX_WORKS], [test "x$rw_cv_prog_cxx_works" = "xyes"])
 
+# Detect warning flags supported by the compiler, append them to WARN_CFLAGS.
+#
+# Pass -Werror as an extra flag during the test: this is needed to make the
+# -Wunknown-warning-option diagnostic fatal with clang.
+AX_APPEND_COMPILE_FLAGS([ dnl
+               -Wall dnl
+               dnl We currently get this warning when building with Clang:
+               dnl
+               dnl /usr/include/setjmp.h:54:12: error: declaration of built-in function '__sigsetjmp' requires the declaration of the 'jmp_buf' type, commonly provided in the header <setjmp.h>. [-Werror,-Wincomplete-setjmp-declaration]
+               dnl extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __THROWNL;
+               dnl            ^
+               -Wno-incomplete-setjmp-declaration dnl
+               -Wdiscarded-qualifiers dnl
+       ],
+       [WARN_CFLAGS],
+       [-Werror])
+
+# When given, add -Werror to WARN_CFLAGS.
+AC_ARG_ENABLE([Werror],
+       [AS_HELP_STRING([--enable-Werror], [Treat compiler warnings as errors.])]
+)
+AS_IF([test "x$enable_Werror" = "xyes"],
+       [WARN_CFLAGS="${WARN_CFLAGS} -Werror"]
+)
+
 # Checks for programs.
 AC_PROG_GREP
 AC_PROG_MAKE_SET
@@ -1038,16 +1063,20 @@ AM_CONDITIONAL([BUILD_LIB_SESSIOND_COMM], [test x$build_lib_sessiond_comm = xyes
 AM_CONDITIONAL([BUILD_LIB_TESTPOINT], [test x$build_lib_testpoint = xyes])
 AM_CONDITIONAL([BUILD_LIB_UST_CONSUMER], [test x$build_lib_ust_consumer = xyes])
 
-AM_CFLAGS="-Wall -fno-strict-aliasing $PTHREAD_CFLAGS"
+AM_CFLAGS="${WARN_CFLAGS} -fno-strict-aliasing $PTHREAD_CFLAGS"
 AC_SUBST(AM_CFLAGS)
 
+# This is set even though it is empty, so Makefiles can do "AM_LDFLAGS += ...".
+AM_LDFLAGS=""
+AC_SUBST(AM_LDFLAGS)
+
 # The order in which the include folders are searched is important.
 # The top_builddir should always be searched first in the event that a build
 # time generated file is included. An example of this is the "version.i" file.
 # In a scenario where lttng-tools is built from a distribution tarball and in a
 # out-of-tree manner, the generated "version.i" has priority on the one from
 # the source (distribution tarball) and must be found first.
-AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -I\$(top_srcdir)/src -include config.h $AM_CPPFLAGS"
+AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -I\$(top_builddir)/src -I\$(top_srcdir)/src -include config.h $AM_CPPFLAGS"
 AC_SUBST(AM_CPPFLAGS)
 
 lttngincludedir="${includedir}/lttng"
@@ -1065,6 +1094,9 @@ AC_SUBST(lttngnotificationincludedir)
 lttngtriggerincludedir="${includedir}/lttng/trigger"
 AC_SUBST(lttngtriggerincludedir)
 
+lttngeventruleincludedir="${includedir}/lttng/event-rule"
+AC_SUBST(lttngeventruleincludedir)
+
 lttnglibexecdir="${libdir}/lttng/libexec"
 AC_SUBST(lttnglibexecdir)
 
@@ -1081,6 +1113,7 @@ AC_CONFIG_FILES([
        extras/core-handler/Makefile
        src/Makefile
        src/common/Makefile
+       src/common/argpar/Makefile
        src/common/kernel-ctl/Makefile
        src/common/kernel-consumer/Makefile
        src/common/consumer/Makefile
@@ -1095,9 +1128,9 @@ AC_CONFIG_FILES([
        src/common/config/Makefile
        src/common/string-utils/Makefile
        src/common/fd-tracker/Makefile
+       src/common/filter/Makefile
        src/lib/Makefile
        src/lib/lttng-ctl/Makefile
-       src/lib/lttng-ctl/filter/Makefile
        src/lib/lttng-ctl/lttng-ctl.pc
        src/bin/Makefile
        src/bin/lttng-consumerd/Makefile
@@ -1132,6 +1165,7 @@ AC_CONFIG_FILES([
        tests/regression/tools/working-directory/Makefile
        tests/regression/tools/relayd-grouping/Makefile
        tests/regression/tools/clear/Makefile
+       tests/regression/tools/trigger/Makefile
        tests/regression/ust/Makefile
        tests/regression/ust/nprocesses/Makefile
        tests/regression/ust/high-throughput/Makefile
diff --git a/doc/examples/trigger-on-event/README.md b/doc/examples/trigger-on-event/README.md
new file mode 100644 (file)
index 0000000..2c184f7
--- /dev/null
@@ -0,0 +1,56 @@
+# Trigger notification exemple
+
+## Description
+This exemple is made-up of three executables.
+
+### `notification-client`
+
+```
+Usage: notification-client TRIGGER_NAME
+```
+
+A simple client that subscribes to the notifications emitted by the `TRIGGER_NAME` trigger.
+
+### `instrumented-app`
+
+An application that emits the `trigger_exemple:my_event` event every 2 seconds.
+
+### `demo.sh`
+
+This script adds a trigger named `demo_trigger` which emits a notification when the user-space `trigger_exemple:my_event` event occurs.
+
+Once the trigger has been setup, the notification-client is launched to print all notifications emitted by the `demo_trigger` trigger.
+
+## Building
+
+Simply run the included Makefile.
+
+## Running the exemple
+
+1) Launch a session daemon using:
+        ```
+        $ lttng-sessiond
+        ```
+2) Launch the `demo.sh` script
+3) Launch the `instrumented-app`
+
+The following output should be produced:
+
+```
+$ ./demo.sh
+Registering a notification trigger named "demo_trigger" for the trigger_exemple:my_event user-space event
+Trigger registered successfully.
+Subscribed to notifications of trigger "demo_trigger"
+[02-14-2020] 18:13:34.779766 - Received notification of event rule trigger "demo_trigger"
+[02-14-2020] 18:13:36.779888 - Received notification of event rule trigger "demo_trigger"
+[02-14-2020] 18:13:38.780514 - Received notification of event rule trigger "demo_trigger"
+[02-14-2020] 18:13:40.780656 - Received notification of event rule trigger "demo_trigger"
+```
+
+```
+$ ./instrumented-app
+[02-14-2020] 18:13:34.779433 - Tracing event "trigger_exemple:my_event"
+[02-14-2020] 18:13:36.779693 - Tracing event "trigger_exemple:my_event"
+[02-14-2020] 18:13:38.780010 - Tracing event "trigger_exemple:my_event"
+[02-14-2020] 18:13:40.780286 - Tracing event "trigger_exemple:my_event"
+```
diff --git a/doc/examples/trigger-on-event/demo.sh b/doc/examples/trigger-on-event/demo.sh
new file mode 100755 (executable)
index 0000000..61623fb
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# SPDX-License-Identifier: MIT
+
+EVENT_NAME=trigger_exemple:my_event
+TRIGGER_NAME=demo_trigger
+
+lttng list > /dev/null 2>&1
+if [ $? -ne 0 ]; then
+    echo "Could not connect to session daemon, are you sure it is running?"
+    exit 1
+fi
+
+echo "Registering a notification trigger named \"$TRIGGER_NAME\" for the $EVENT_NAME user-space event"
+lttng add-trigger --id $TRIGGER_NAME --condition on-event --userspace $EVENT_NAME --action notify
+
+./notification-client $TRIGGER_NAME
+
diff --git a/doc/examples/trigger-on-event/instrumented-app.c b/doc/examples/trigger-on-event/instrumented-app.c
new file mode 100644 (file)
index 0000000..2490056
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#include "tp.h"
+
+#include <lttng/tracepoint.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+       uint64_t i;
+
+       for (i = 0; i < UINT64_MAX; i++) {
+               char time_str[64];
+               struct timeval tv;
+               time_t the_time;
+
+               gettimeofday(&tv, NULL);
+               the_time = tv.tv_sec;
+
+               strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T",
+                               localtime(&the_time));
+               printf("%s.%ld - Tracing event \"trigger_exemple:my_event\"\n", time_str, tv.tv_usec);
+
+               tracepoint(trigger_exemple, my_event, i);
+               sleep(2);
+       }
+       return 0;
+}
diff --git a/doc/examples/trigger-on-event/notification-client.c b/doc/examples/trigger-on-event/notification-client.c
new file mode 100644 (file)
index 0000000..217b5c7
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#include <lttng/condition/event-rule.h>
+#include <lttng/lttng.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+bool action_group_contains_notify(const struct lttng_action *action_group)
+{
+       unsigned int i, count;
+       enum lttng_action_status status =
+                       lttng_action_group_get_count(action_group, &count);
+
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               printf("Failed to get action count from action group\n");
+               exit(1);
+       }
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_action *action =
+                               lttng_action_group_get_at_index_const(
+                                               action_group, i);
+               const enum lttng_action_type action_type =
+                               lttng_action_get_type(action);
+
+               if (action_type == LTTNG_ACTION_TYPE_NOTIFY) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+int print_notification(struct lttng_notification *notification)
+{
+       int ret = 0;
+       const struct lttng_condition *condition =
+                       lttng_notification_get_condition(notification);
+       const struct lttng_evaluation *evaluation =
+                       lttng_notification_get_evaluation(notification);
+       const enum lttng_condition_type type =
+                       lttng_evaluation_get_type(evaluation);
+
+       switch (type) {
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+               printf("Received consumed size notification\n");
+               break;
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+               printf("Received buffer usage notification\n");
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               printf("Received session rotation ongoing notification\n");
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               printf("Received session rotation completed notification\n");
+               break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+       {
+               const char *trigger_name;
+               enum lttng_evaluation_status evaluation_status;
+               char time_str[64];
+               struct timeval tv;
+               time_t the_time;
+
+               gettimeofday(&tv, NULL);
+               the_time = tv.tv_sec;
+
+               strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T",
+                        localtime(&the_time));
+               printf("%s.%ld - ", time_str, tv.tv_usec);
+
+               evaluation_status =
+                               lttng_evaluation_event_rule_get_trigger_name(
+                                               evaluation, &trigger_name);
+               if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) {
+                       fprintf(stderr, "Failed to get trigger name of event rule notification\n");
+                       ret = -1;
+                       break;
+               }
+
+               printf("Received notification of event rule trigger \"%s\"\n",
+                               trigger_name);
+               break;
+       }
+       default:
+               fprintf(stderr, "Unknown notification type (%d)\n", type);
+       }
+
+       return ret;
+}
+
+int main(int argc, char **argv)
+{
+       int ret;
+       struct lttng_triggers *triggers = NULL;
+       unsigned int count, i, subcription_count = 0;
+       enum lttng_trigger_status trigger_status;
+       struct lttng_notification_channel *notification_channel = NULL;
+
+       if (argc != 2) {
+               fprintf(stderr, "Missing trigger name\n");
+               fprintf(stderr, "Usage: notification-client TRIGGER_NAME\n");
+               goto end;
+       }
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       if (!notification_channel) {
+               fprintf(stderr, "Failed to create notification channel\n");
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_list_triggers(&triggers);
+       if (ret) {
+               fprintf(stderr, "Failed to list triggers\n");
+               goto end;
+       }
+
+       trigger_status = lttng_triggers_get_count(triggers, &count);
+       if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+               fprintf(stderr, "Failed to get trigger count\n");
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_trigger *trigger =
+                               lttng_triggers_get_at_index(triggers, i);
+               const struct lttng_condition *condition =
+                               lttng_trigger_get_const_condition(trigger);
+               const struct lttng_action *action =
+                               lttng_trigger_get_const_action(trigger);
+               const enum lttng_action_type action_type =
+                               lttng_action_get_type(action);
+               enum lttng_notification_channel_status channel_status;
+               const char *trigger_name = NULL;
+               enum lttng_trigger_status trigger_status;
+
+               lttng_trigger_get_name(trigger, &trigger_name);
+               if (strcmp(trigger_name, argv[1])) {
+                       continue;
+               }
+
+               if (!((action_type == LTTNG_ACTION_TYPE_GROUP &&
+                                     action_group_contains_notify(action)) ||
+                                   action_type == LTTNG_ACTION_TYPE_NOTIFY)) {
+                       printf("The action of trigger \"%s\" is not \"notify\", skipping.\n",
+                                       trigger_name);
+                       continue;
+               }
+
+               channel_status = lttng_notification_channel_subscribe(
+                               notification_channel, condition);
+               if (channel_status) {
+                       fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n",
+                                       trigger_name);
+                       ret = -1;
+                       goto end;
+               }
+
+               printf("Subscribed to notifications of trigger \"%s\"\n",
+                               trigger_name);
+               subcription_count++;
+       }
+
+       if (subcription_count == 0) {
+               printf("No matching trigger with a notify action found.\n");
+               ret = 0;
+               goto end;
+       }
+
+       for (;;) {
+               struct lttng_notification *notification;
+               enum lttng_notification_channel_status channel_status;
+
+               channel_status =
+                               lttng_notification_channel_get_next_notification(
+                                               notification_channel,
+                                               &notification);
+               switch (channel_status) {
+               case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED:
+                       printf("Dropped notification\n");
+                       break;
+               case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED:
+                       ret = 0;
+                       goto end;
+               case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK:
+                       break;
+               case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED:
+                       printf("Notification channel was closed by peer.\n");
+                       break;
+               default:
+                       fprintf(stderr, "A communication error occurred on the notification channel.\n");
+                       ret = -1;
+                       goto end;
+               }
+
+               ret = print_notification(notification);
+               lttng_notification_destroy(notification);
+               if (ret) {
+                       goto end;
+               }
+       }
+end:
+       lttng_triggers_destroy(triggers);
+       lttng_notification_channel_destroy(notification_channel);
+       return !!ret;
+}
diff --git a/doc/examples/trigger-on-event/tp.c b/doc/examples/trigger-on-event/tp.c
new file mode 100644 (file)
index 0000000..e1745ed
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_CREATE_PROBES
+#include "tp.h"
diff --git a/doc/examples/trigger-on-event/tp.h b/doc/examples/trigger-on-event/tp.h
new file mode 100644 (file)
index 0000000..ae4593a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER trigger_exemple
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./tp.h"
+
+#if !defined(_TRACEPOINT_TRIGGER_EXEMPLE_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_TRIGGER_EXEMPLE_H
+
+#include <lttng/tracepoint.h>
+
+TRACEPOINT_EVENT(trigger_exemple, my_event,
+       TP_ARGS(int, iteration),
+       TP_FIELDS(
+               ctf_integer(uint64_t, iteration, iteration)
+       )
+)
+
+#endif /* _TRACEPOINT_TRIGGER_EXEMPLE_H */
+
+#include <lttng/tracepoint-event.h>
index 80bedbadfd3f6a7d3bcbaa9ff249b5792bffeb94..5ae6ffbe30da3d4755deed80d5ccfb9e591414f1 100644 (file)
@@ -36,7 +36,11 @@ MAN1_NAMES = \
        lttng-rotate \
        lttng-enable-rotation \
        lttng-disable-rotation \
-       lttng-clear
+       lttng-clear \
+       lttng-add-trigger \
+       lttng-remove-trigger \
+       lttng-list-triggers
+
 MAN3_NAMES =
 MAN8_NAMES = lttng-sessiond lttng-relayd
 MAN1_NO_ASCIIDOC_NAMES =
diff --git a/doc/man/lttng-add-trigger.1.txt b/doc/man/lttng-add-trigger.1.txt
new file mode 100644 (file)
index 0000000..393e8c9
--- /dev/null
@@ -0,0 +1,134 @@
+lttng-add-trigger(1)
+=====================
+:revdate: 17 January 2020
+
+
+NAME
+----
+lttng-add-trigger - Create LTTng triggers
+
+
+SYNOPSIS
+--------
+
+[verse]
+*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *add-trigger* [--id ID]
+      [--fire-every N] [--fire-once-after N]
+      --condition CONDITION-NAME CONDITION-ARGS
+      --action ACTION-NAME ACTION-ARGS
+      [--action ACTION-NAME ACTION-ARGS]...
+
+
+DESCRIPTION
+-----------
+
+The `lttng add-trigger` command is used to create triggers.  A
+trigger is an association between a *condition* and one or more
+*actions*.  When the condition associated to a trigger is met, the
+actions associated to that trigger are executed.  The tracing does not
+have to be active for the conditions to be met, and triggers are
+independent from tracing sessions.
+
+By default, a trigger fires every time its condition is met.  The
+'--fire-every' and '--fire-once-after' options can be used to change
+this mode.
+
+The syntax by which conditions and actions are specified is described
+below.
+
+[[conditions]]
+Conditions
+~~~~~~~~~~
+
+Conditions are specified with the `--condition` option, followed by a
+condition name, and possibly some more arguments, depending on the
+specific condition.  There must be exactly one condition given in the
+`lttng add-trigger` invocation.
+
+The available conditions are:
+
+Event rule: `on-event [event rule arguments]`::
+    This type of condition is met when the tracer encounters an event
+    matching the given even rule.  The arguments describing the event
+    rule are the same as those describing the event rules of the
+    man:lttng-enable-event(1) command, with these exceptions:
+
+    - It is not possible to use filter operands that use values from
+      the context.
+
+[[actions]]
+Actions
+~~~~~~~
+
+Actions are specified with the `--action` option, followed by an action
+name, and possibly some more arguments, depending on the specific
+action.  There must be at least one action given in the
+`lttng add-trigger` invocation.
+
+The available actions are:
+
+Notify: *notify*::
+    This action causes the LTTng session daemon to send a notification,
+    through its notification mechanism (see man:lttng-sessiond(8)).
+    Some details about the condition evaluation are sent along with the
+    notification.
+
+Start session: *start-session* session-name::
+    This action causes the LTTng session daemon to start tracing for the
+    session with the given name.  If no session with the given name exist
+    at the time the condition is met, nothing is done.
+
+Stop session: *stop-session* session-name::
+    This action causes the LTTng session daemon to stop tracing for the
+    session with the given name.  If no session with the given name exist
+    at the time the condition is met, nothing is done.
+
+Rotate session: *rotate-session* session-name::
+    This action causes the LTTng session daemon to rotate the session
+    with the given name.  See  man:lttng-rotate(1) for more information
+    about the session rotation concept.  If no session with the given
+    name exist at the time the condition is met, nothing is done.
+
+Snapshot session: *snapshot-session* session-name::
+    This action causes the LTTng session daemon to take a snapshot of the
+    session with the given name.  See man:lttng-snapshot(1) for more
+    information about the session snapshot concept.  If no session with
+    the given name exist at the time the condition is met, nothing is
+    done.
+
+OPTIONS
+-------
+
+option:--condition::
+    Define the condition for the trigger.  See the
+    <<conditions,CONDITIONS>> section for more details.
+
+option:--action::
+    Define an action for the trigger.  See the <<actions,ACTIONS>>
+    section for more details.
+
+option:--id='ID'::
+    Set the id of the trigger to 'ID'.  If omitted, an id will
+    automatically be assigned to the trigger by the session daemon.
++
+If a trigger with the specified 'ID' already exists, the trigger
+creation will fail.
+
+option:--fire-every 'N'::
+    Execute the trigger's actions every 'N' times the condition is met.
+
+option:--fire-once-after 'N'::
+    Execute the trigger's actions once after 'N' times the condition is
+    met, then never after that.
+
+include::common-cmd-help-options.txt[]
+
+
+include::common-cmd-footer.txt[]
+
+
+SEE ALSO
+--------
+man:lttng-list-triggers(1),
+man:lttng-remove-trigger(1),
+man:lttng(1)
diff --git a/doc/man/lttng-list-triggers.1.txt b/doc/man/lttng-list-triggers.1.txt
new file mode 100644 (file)
index 0000000..2ab3085
--- /dev/null
@@ -0,0 +1,37 @@
+lttng-list-triggers(1)
+======================
+:revdate: 20 January 2020
+
+
+NAME
+----
+lttng-list-triggers - List LTTng triggers
+
+
+SYNOPSIS
+--------
+
+[verse]
+*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *list-triggers*
+
+
+DESCRIPTION
+-----------
+
+The `lttng list-triggers` command list the existing triggers.
+
+
+OPTIONS
+-------
+
+include::common-cmd-help-options.txt[]
+
+
+include::common-cmd-footer.txt[]
+
+
+SEE ALSO
+--------
+man:lttng-add-trigger(1),
+man:lttng-remove-trigger(1),
+man:lttng(1)
diff --git a/doc/man/lttng-remove-trigger.1.txt b/doc/man/lttng-remove-trigger.1.txt
new file mode 100644 (file)
index 0000000..645ef15
--- /dev/null
@@ -0,0 +1,38 @@
+lttng-remove-trigger(1)
+========================
+:revdate: 20 January 2020
+
+
+NAME
+----
+lttng-remove-trigger - Remove LTTng triggers
+
+
+SYNOPSIS
+--------
+
+[verse]
+*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *remove-trigger* 'ID'
+
+
+DESCRIPTION
+-----------
+
+The `lttng remove-trigger` command removes the trigger with the given
+'ID'.
+
+
+OPTIONS
+-------
+
+include::common-cmd-help-options.txt[]
+
+
+include::common-cmd-footer.txt[]
+
+
+SEE ALSO
+--------
+man:lttng-add-trigger(1),
+man:lttng-list-trigger(1),
+man:lttng(1)
index e99c9aecf53750b1c31e54dc7db67d3c7a69990a..b6b6f960eba7720955611e3adbeceaa3679195e2 100644 (file)
@@ -29,6 +29,9 @@ multiple concurrent processes and threads. Tracing across multiple systems is al
 // This makes the typemap code useable with both Python 2 and 3.
 #define PyInt_AsSsize_t PyLong_AsSsize_t
 #endif
+
+// Avoid -Wmissing-declarations warning.
+PyObject *SWIG_init(void);
 %}
 
 typedef unsigned int uint32_t;
index ccd0d87d62dc6ff3ecd9bfe0393cf84baaf44167..4fdd78c7d0681080c4ca0e8e2b3ee5f1f8a1f25e 100644 (file)
@@ -121,11 +121,17 @@ lttnginclude_HEADERS = \
 
 lttngactioninclude_HEADERS= \
        lttng/action/action.h \
-       lttng/action/notify.h
+       lttng/action/group.h \
+       lttng/action/notify.h \
+       lttng/action/rotate-session.h \
+       lttng/action/snapshot-session.h \
+       lttng/action/start-session.h \
+       lttng/action/stop-session.h
 
 lttngconditioninclude_HEADERS= \
        lttng/condition/condition.h \
        lttng/condition/buffer-usage.h \
+       lttng/condition/event-rule.h \
        lttng/condition/session-consumed-size.h \
        lttng/condition/session-rotation.h \
        lttng/condition/evaluation.h
@@ -137,6 +143,14 @@ lttngnotificationinclude_HEADERS= \
 lttngtriggerinclude_HEADERS= \
        lttng/trigger/trigger.h
 
+lttngeventruleinclude_HEADERS= \
+       lttng/event-rule/event-rule.h \
+       lttng/event-rule/kprobe.h \
+       lttng/event-rule/kretprobe.h \
+       lttng/event-rule/syscall.h \
+       lttng/event-rule/tracepoint.h \
+       lttng/event-rule/uprobe.h
+
 noinst_HEADERS = \
        lttng/snapshot-internal.h \
        lttng/health-internal.h \
@@ -146,6 +160,7 @@ noinst_HEADERS = \
        lttng/action/notify-internal.h \
        lttng/condition/condition-internal.h \
        lttng/condition/buffer-usage-internal.h \
+       lttng/condition/event-rule-internal.h \
        lttng/condition/session-consumed-size-internal.h \
        lttng/condition/evaluation-internal.h \
        lttng/condition/session-rotation-internal.h \
@@ -162,5 +177,11 @@ noinst_HEADERS = \
        lttng/session-internal.h \
        lttng/session-descriptor-internal.h \
        lttng/tracker-internal.h \
+       lttng/event-rule/event-rule-internal.h \
+       lttng/event-rule/kprobe-internal.h \
+       lttng/event-rule/kretprobe-internal.h \
+       lttng/event-rule/uprobe-internal.h \
+       lttng/event-rule/syscall-internal.h \
+       lttng/event-rule/tracepoint-internal.h \
        version.h \
        version.i
index 787e9087a01e353c664c1876575dad2867a4fa5b..af52a07ff52a74bd8128537733deeb8ef2db7495 100644 (file)
 #include <common/dynamic-buffer.h>
 #include <stdbool.h>
 #include <sys/types.h>
+#include <urcu/ref.h>
 
 typedef bool (*action_validate_cb)(struct lttng_action *action);
 typedef void (*action_destroy_cb)(struct lttng_action *action);
 typedef int (*action_serialize_cb)(struct lttng_action *action,
                struct lttng_dynamic_buffer *buf);
+typedef bool (*action_equal_cb)(const struct lttng_action *a,
+               const struct lttng_action *b);
+typedef ssize_t (*action_create_from_buffer_cb)(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **action);
 
 struct lttng_action {
+       struct urcu_ref ref;
        enum lttng_action_type type;
        action_validate_cb validate;
        action_serialize_cb serialize;
+       action_equal_cb equal;
        action_destroy_cb destroy;
 };
 
@@ -32,6 +40,14 @@ struct lttng_action_comm {
        int8_t action_type;
 } LTTNG_PACKED;
 
+LTTNG_HIDDEN
+void lttng_action_init(struct lttng_action *action,
+               enum lttng_action_type type,
+               action_validate_cb validate,
+               action_serialize_cb serialize,
+               action_equal_cb equal,
+               action_destroy_cb destroy);
+
 LTTNG_HIDDEN
 bool lttng_action_validate(struct lttng_action *action);
 
@@ -47,4 +63,17 @@ LTTNG_HIDDEN
 enum lttng_action_type lttng_action_get_type_const(
                const struct lttng_action *action);
 
+LTTNG_HIDDEN
+bool lttng_action_is_equal(const struct lttng_action *a,
+               const struct lttng_action *b);
+
+LTTNG_HIDDEN
+void lttng_action_get(struct lttng_action *action);
+
+LTTNG_HIDDEN
+void lttng_action_put(struct lttng_action *action);
+
+LTTNG_HIDDEN
+const char* lttng_action_type_string(enum lttng_action_type action_type);
+
 #endif /* LTTNG_ACTION_INTERNAL_H */
index f3f8d9deef0f16722a668f6e109b24547076c69a..be7e397d0629e6f94091ac4a0eccaf1061366d2b 100644 (file)
@@ -17,6 +17,19 @@ extern "C" {
 enum lttng_action_type {
        LTTNG_ACTION_TYPE_UNKNOWN = -1,
        LTTNG_ACTION_TYPE_NOTIFY = 0,
+       LTTNG_ACTION_TYPE_START_SESSION = 1,
+       LTTNG_ACTION_TYPE_STOP_SESSION = 2,
+       LTTNG_ACTION_TYPE_ROTATE_SESSION = 3,
+       LTTNG_ACTION_TYPE_SNAPSHOT_SESSION = 4,
+       LTTNG_ACTION_TYPE_GROUP = 5,
+};
+
+enum lttng_action_status {
+       LTTNG_ACTION_STATUS_OK = 0,
+       LTTNG_ACTION_STATUS_ERROR = -1,
+       LTTNG_ACTION_STATUS_UNKNOWN = -2,
+       LTTNG_ACTION_STATUS_INVALID = -3,
+       LTTNG_ACTION_STATUS_UNSET = -4,
 };
 
 /*
@@ -25,7 +38,7 @@ enum lttng_action_type {
  * Returns the type of an action on success, LTTNG_ACTION_TYPE_UNKNOWN on error.
  */
 extern enum lttng_action_type lttng_action_get_type(
-               struct lttng_action *action);
+               const struct lttng_action *action);
 
 /*
  * Destroy (frees) an action object.
diff --git a/include/lttng/action/group-internal.h b/include/lttng/action/group-internal.h
new file mode 100644 (file)
index 0000000..4284795
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_GROUP_INTERNAL_H
+#define LTTNG_ACTION_GROUP_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create an action group from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * group in `*group`.  On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_group_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **group);
+
+#endif /* LTTNG_ACTION_GROUP_INTERNAL_H */
diff --git a/include/lttng/action/group.h b/include/lttng/action/group.h
new file mode 100644 (file)
index 0000000..a49660d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 EfficiOS, inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_GROUP_H
+#define LTTNG_ACTION_GROUP_H
+
+struct lttng_action;
+struct lttng_action_group;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated action group object.
+ *
+ * Returns a new action group on success, NULL on failure. This action group
+ * must be destroyed using lttng_action_group_destroy().
+ */
+extern struct lttng_action *lttng_action_group_create(void);
+
+/*
+ * Add an action to an lttng_action object of type LTTNG_ACTION_GROUP.
+ *
+ * The group takes ownership of the action.
+ */
+extern enum lttng_action_status lttng_action_group_add_action(
+               struct lttng_action *group, struct lttng_action *action);
+
+extern enum lttng_action_status lttng_action_group_get_count(
+               const struct lttng_action *group, unsigned int *count);
+
+extern const struct lttng_action *lttng_action_group_get_at_index_const(
+               const struct lttng_action *group,
+               unsigned int index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_GROUP_H */
index e6cb174ceea5de44705dc7b880259e5cbdfe8705..a9d50612dbceace92565d08deed4a85ed31902cb 100644 (file)
@@ -15,4 +15,9 @@ struct lttng_action_notify {
        struct lttng_action parent;
 };
 
+LTTNG_HIDDEN
+ssize_t lttng_action_notify_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **action);
+
 #endif /* LTTNG_ACTION_NOTIFY_INTERNAL_H */
diff --git a/include/lttng/action/rotate-session-internal.h b/include/lttng/action/rotate-session-internal.h
new file mode 100644 (file)
index 0000000..b3389f7
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_ROTATE_SESSION_INTERNAL_H
+#define LTTNG_ACTION_ROTATE_SESSION_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create a "rotate session" action from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * action in `*action`.  On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_rotate_session_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **action);
+
+#endif /* LTTNG_ACTION_ROTATE_SESSION_INTERNAL_H */
diff --git a/include/lttng/action/rotate-session.h b/include/lttng/action/rotate-session.h
new file mode 100644 (file)
index 0000000..c7112bb
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 EfficiOS, inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_ROTATE_SESSION_H
+#define LTTNG_ACTION_ROTATE_SESSION_H
+
+struct lttng_action;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated rotate-session action object.
+ *
+ * Returns a new action on success, NULL on failure. This action must be
+ * destroyed using lttng_action_destroy().
+ */
+extern struct lttng_action *lttng_action_rotate_session_create(void);
+
+/*
+ * Set the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_ROTATE_SESSION.
+ */
+extern enum lttng_action_status lttng_action_rotate_session_set_session_name(
+               struct lttng_action *action, const char *session_name);
+
+/*
+ * Get the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_ROTATE_SESSION.
+ */
+extern enum lttng_action_status lttng_action_rotate_session_get_session_name(
+               const struct lttng_action *action, const char **session_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_ROTATE_SESSION_H */
diff --git a/include/lttng/action/snapshot-session-internal.h b/include/lttng/action/snapshot-session-internal.h
new file mode 100644 (file)
index 0000000..0a3e0e9
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H
+#define LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create a "snapshot session" action from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * action in `*action`.  On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_snapshot_session_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **action);
+
+#endif /* LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H */
diff --git a/include/lttng/action/snapshot-session.h b/include/lttng/action/snapshot-session.h
new file mode 100644 (file)
index 0000000..eeb75e6
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 EfficiOS, inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_SNAPSHOT_SESSION_H
+#define LTTNG_ACTION_SNAPSHOT_SESSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lttng_action;
+struct lttng_snapshot_output;
+
+/*
+ * Create a newly allocated snapshot-session action object.
+ *
+ * Returns a new action on success, NULL on failure. This action must be
+ * destroyed using lttng_action_destroy().
+ */
+extern struct lttng_action *lttng_action_snapshot_session_create(void);
+
+/*
+ * Set the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_SNAPSHOT_SESSION.
+ */
+extern enum lttng_action_status lttng_action_snapshot_session_set_session_name(
+               struct lttng_action *action, const char *session_name);
+
+/*
+ * Get the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_SNAPSHOT_SESSION.
+ */
+extern enum lttng_action_status lttng_action_snapshot_session_get_session_name(
+               const struct lttng_action *action, const char **session_name);
+
+/*
+ * Set an explicit snapshot output for this snapshot session action.
+ *
+ * The given snapshot output will be used instead of the session's
+ * default snapshot output.
+ *
+ * This function takes ownership of the given snapshot output.
+ */
+extern enum lttng_action_status lttng_action_snapshot_session_set_output(
+               struct lttng_action *action,
+               struct lttng_snapshot_output *output);
+
+/*
+ * Get the explicit snapshot output for this snapshot session action.
+ */
+extern enum lttng_action_status lttng_action_snapshot_session_get_output_const(
+               const struct lttng_action *action,
+               const struct lttng_snapshot_output **output);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_SNAPSHOT_SESSION_H */
diff --git a/include/lttng/action/start-session-internal.h b/include/lttng/action/start-session-internal.h
new file mode 100644 (file)
index 0000000..496bee2
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_START_SESSION_INTERNAL_H
+#define LTTNG_ACTION_START_SESSION_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create a "start session" action from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * action in `*action`.  On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_start_session_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **action);
+
+#endif /* LTTNG_ACTION_START_SESSION_INTERNAL_H */
diff --git a/include/lttng/action/start-session.h b/include/lttng/action/start-session.h
new file mode 100644 (file)
index 0000000..b9eda70
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 EfficiOS, inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_START_SESSION_H
+#define LTTNG_ACTION_START_SESSION_H
+
+struct lttng_action;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated start-session action object.
+ *
+ * Returns a new action on success, NULL on failure. This action must be
+ * destroyed using lttng_action_destroy().
+ */
+extern struct lttng_action *lttng_action_start_session_create(void);
+
+/*
+ * Set the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_START_SESSION.
+ */
+extern enum lttng_action_status lttng_action_start_session_set_session_name(
+               struct lttng_action *action, const char *session_name);
+
+/*
+ * Get the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_START_SESSION.
+ */
+extern enum lttng_action_status lttng_action_start_session_get_session_name(
+               const struct lttng_action *action, const char **session_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_START_SESSION_H */
diff --git a/include/lttng/action/stop-session-internal.h b/include/lttng/action/stop-session-internal.h
new file mode 100644 (file)
index 0000000..7a453ea
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_STOP_SESSION_INTERNAL_H
+#define LTTNG_ACTION_STOP_SESSION_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create a "stop session" action from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * action in `*action`.  On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_stop_session_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **action);
+
+#endif /* LTTNG_ACTION_STOP_SESSION_INTERNAL_H */
diff --git a/include/lttng/action/stop-session.h b/include/lttng/action/stop-session.h
new file mode 100644 (file)
index 0000000..cf9eaab
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 EfficiOS, inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_ACTION_STOP_SESSION_H
+#define LTTNG_ACTION_STOP_SESSION_H
+
+struct lttng_action;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated stop-session action object.
+ *
+ * Returns a new action on success, NULL on failure. This action must be
+ * destroyed using lttng_action_destroy().
+ */
+extern struct lttng_action *lttng_action_stop_session_create(void);
+
+/*
+ * Set the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_STOP_SESSION.
+ */
+extern enum lttng_action_status lttng_action_stop_session_set_session_name(
+               struct lttng_action *action, const char *session_name);
+
+/*
+ * Get the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_STOP_SESSION.
+ */
+extern enum lttng_action_status lttng_action_stop_session_get_session_name(
+               const struct lttng_action *action, const char **session_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_STOP_SESSION_H */
index 60686d29f8a8c96b7c0209170690b20114409ef9..2eaf9292f5165fdbb34d9fef41e759db53e51b95 100644 (file)
@@ -21,7 +21,8 @@ typedef void (*condition_destroy_cb)(struct lttng_condition *condition);
 typedef bool (*condition_validate_cb)(const struct lttng_condition *condition);
 typedef int (*condition_serialize_cb)(
                const struct lttng_condition *condition,
-               struct lttng_dynamic_buffer *buf);
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send);
 typedef bool (*condition_equal_cb)(const struct lttng_condition *a,
                const struct lttng_condition *b);
 typedef ssize_t (*condition_create_from_buffer_cb)(
@@ -56,10 +57,14 @@ ssize_t lttng_condition_create_from_buffer(
 
 LTTNG_HIDDEN
 int lttng_condition_serialize(const struct lttng_condition *condition,
-               struct lttng_dynamic_buffer *buf);
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send);
 
 LTTNG_HIDDEN
 bool lttng_condition_is_equal(const struct lttng_condition *a,
                const struct lttng_condition *b);
 
+LTTNG_HIDDEN
+const char *lttng_condition_type_str(enum lttng_condition_type type);
+
 #endif /* LTTNG_CONDITION_INTERNAL_H */
index 877ccd1a39d0bcc9534c55e4fbc9b578b285cbd0..78a206df3180fd929d3c0b4225ac4f1810cf117f 100644 (file)
@@ -21,6 +21,7 @@ enum lttng_condition_type {
        LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW = 102,
        LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING = 103,
        LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED = 104,
+       LTTNG_CONDITION_TYPE_EVENT_RULE_HIT = 105,
 };
 
 enum lttng_condition_status {
diff --git a/include/lttng/condition/event-rule-internal.h b/include/lttng/condition/event-rule-internal.h
new file mode 100644 (file)
index 0000000..ce1004e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 - Jonathan Rajotte <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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_CONDITION_event_rule_INTERNAL_H
+#define LTTNG_CONDITION_event_rule_INTERNAL_H
+
+#include <lttng/condition/condition-internal.h>
+#include <common/buffer-view.h>
+#include <common/macros.h>
+#include <lttng/condition/evaluation-internal.h>
+
+struct lttng_condition_event_rule {
+       struct lttng_condition parent;
+       struct lttng_event_rule *rule;
+};
+
+struct lttng_condition_event_rule_comm {
+       /* length excludes its own length */
+       uint32_t length;
+       /* rule */
+       char payload[];
+} LTTNG_PACKED;
+
+struct lttng_evaluation_event_rule {
+       struct lttng_evaluation parent;
+       char *name;
+};
+
+struct lttng_evaluation_event_rule_comm {
+       uint32_t trigger_name_length;
+       /* Trigger name */
+       char payload[];
+} LTTNG_PACKED;
+
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_event_rule_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_condition **condition);
+
+LTTNG_HIDDEN
+enum lttng_condition_status
+lttng_condition_event_rule_get_rule_no_const(
+               const struct lttng_condition *condition,
+               struct lttng_event_rule **rule);
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_event_rule_create(const char* trigger_name);
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_event_rule_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_evaluation **_evaluation);
+
+#endif /* LTTNG_CONDITION_event_rule_INTERNAL_H */
diff --git a/include/lttng/condition/event-rule.h b/include/lttng/condition/event-rule.h
new file mode 100644 (file)
index 0000000..2a536d3
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 - Jonathan Rajotte <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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_CONDITION_EVENT_RULE_H
+#define LTTNG_CONDITION_EVENT_RULE_H
+
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/evaluation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * TODO: overall desciption of an event rule condition.
+ */
+
+/*
+ * Create a newly allocated event rule condition.
+ *
+ * Rule will be copied internally.
+ *
+ * Returns a new condition on success, NULL on failure. This condition must be
+ * destroyed using lttng_condition_destroy().
+ */
+extern struct lttng_condition *lttng_condition_event_rule_create(
+               struct lttng_event_rule *rule);
+
+/*
+ * Get the rule property of a event rule condition.
+ *
+ * The caller does not assume the ownership of the returned rule. The
+ * rule shall only be used for the duration of the condition's
+ * lifetime.
+ *
+ * Returns LTTNG_CONDITION_STATUS_OK and a pointer to the condition's rule
+ * on success, LTTNG_CONDITION_STATUS_INVALID if an invalid
+ * parameter is passed. */
+extern enum lttng_condition_status lttng_condition_event_rule_get_rule(
+               const struct lttng_condition *condition, const struct lttng_event_rule **rule);
+
+/**
+ * lttng_evaluation_event_rule_hit are specialised lttng_evaluations which
+ * allow users to query a number of properties resulting from the evaluation
+ * of a condition which evaluated to true.
+ *
+ * The evaluation of a event rule hit yields two different results:
+ *    TEMPORARY - The name of the triggers associated with the condition.
+ *    TODO - The captured event payload if any
+ */
+
+/*
+ * Get the trigger name property of a event rule hit evaluation.
+ *
+ * Returns LTTNG_EVALUATION_STATUS_OK on success and a trigger name
+ * or LTTNG_EVALUATION_STATUS_INVALID if
+ * an invalid parameter is passed.
+ */
+extern enum lttng_evaluation_status
+lttng_evaluation_event_rule_get_trigger_name(
+               const struct lttng_evaluation *evaluation,
+               const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_CONDITION_EVENT_RULE_H */
diff --git a/include/lttng/domain-internal.h b/include/lttng/domain-internal.h
new file mode 100644 (file)
index 0000000..f34a33b
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 - EfficiOS, inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_DOMAIN_INTERNAL_H
+#define LTTNG_DOMAIN_INTERNAL_H
+
+#include "lttng/domain.h"
+#include "common/macros.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LTTNG_HIDDEN
+const char *lttng_domain_type_str(enum lttng_domain_type domain_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_DOMAIN_INTERNAL_H */
index b3df4c9c2b9c3deea142bf9a2cb5388ef72b5b76..01e9b8088db991426b63294b7222efe911c00602 100644 (file)
@@ -36,4 +36,19 @@ struct lttng_event_extended {
 LTTNG_HIDDEN
 struct lttng_event *lttng_event_copy(const struct lttng_event *event);
 
+// FIXME: the implementation of these should be moved to some common file,
+// they should not be in the enable_events.c file.
+
+LTTNG_HIDDEN
+int loglevel_str_to_value(const char *inputstr);
+
+LTTNG_HIDDEN
+int loglevel_log4j_str_to_value(const char *inputstr);
+
+LTTNG_HIDDEN
+int loglevel_jul_str_to_value(const char *inputstr);
+
+LTTNG_HIDDEN
+int loglevel_python_str_to_value(const char *inputstr);
+
 #endif /* LTTNG_EVENT_INTERNAL_H */
diff --git a/include/lttng/event-rule/event-rule-internal.h b/include/lttng/event-rule/event-rule-internal.h
new file mode 100644 (file)
index 0000000..265286a
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_INTERNAL_H
+#define LTTNG_EVENT_RULE_INTERNAL_H
+
+#include <common/buffer-view.h>
+#include <common/dynamic-buffer.h>
+#include <common/macros.h>
+#include <lttng/event.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/lttng-error.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <urcu/ref.h>
+
+typedef void (*event_rule_destroy_cb)(struct lttng_event_rule *event_rule);
+typedef bool (*event_rule_validate_cb)(
+               const struct lttng_event_rule *event_rule);
+typedef int (*event_rule_serialize_cb)(
+               const struct lttng_event_rule *event_rule,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send);
+typedef bool (*event_rule_equal_cb)(const struct lttng_event_rule *a,
+               const struct lttng_event_rule *b);
+typedef ssize_t (*event_rule_create_from_buffer_cb)(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **event_rule);
+typedef enum lttng_error_code (*event_rule_populate_cb)(
+               struct lttng_event_rule *event_rule, uid_t uid, gid_t gid);
+typedef const char *(*event_rule_get_filter_cb)(
+               const struct lttng_event_rule *event_rule);
+typedef const struct lttng_filter_bytecode *(
+               *event_rule_get_filter_bytecode_cb)(
+               const struct lttng_event_rule *event_rule);
+typedef struct lttng_event_exclusion *(*event_rule_generate_exclusions_cb)(
+               struct lttng_event_rule *event_rule);
+typedef struct lttng_event *(*event_rule_generate_lttng_event_cb)(
+               const struct lttng_event_rule *event_rule);
+
+struct lttng_event_rule {
+       struct urcu_ref ref;
+       enum lttng_event_rule_type type;
+       event_rule_validate_cb validate;
+       event_rule_serialize_cb serialize;
+       event_rule_equal_cb equal;
+       event_rule_destroy_cb destroy;
+       event_rule_populate_cb populate;
+       /* Optional */
+       event_rule_get_filter_cb get_filter;
+       event_rule_get_filter_bytecode_cb get_filter_bytecode;
+       event_rule_generate_exclusions_cb generate_exclusions;
+       event_rule_generate_lttng_event_cb generate_lttng_event;
+};
+
+struct lttng_event_rule_comm {
+       /* enum lttng_event_rule_type */
+       int8_t event_rule_type;
+       char payload[];
+};
+
+LTTNG_HIDDEN
+void lttng_event_rule_init(struct lttng_event_rule *event_rule,
+               enum lttng_event_rule_type type);
+
+LTTNG_HIDDEN
+bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule);
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_create_from_buffer(
+               const struct lttng_buffer_view *buffer,
+               struct lttng_event_rule **event_rule);
+
+LTTNG_HIDDEN
+int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send);
+
+LTTNG_HIDDEN
+bool lttng_event_rule_is_equal(const struct lttng_event_rule *a,
+               const struct lttng_event_rule *b);
+
+LTTNG_HIDDEN
+bool lttng_event_rule_get(struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+void lttng_event_rule_put(struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+enum lttng_domain_type lttng_event_rule_get_domain_type(
+               const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+enum lttng_error_code lttng_event_rule_populate(
+               struct lttng_event_rule *rule, uid_t uid, gid_t gid);
+
+/* If not present return NULL
+ * Caller DO NOT own the returned object
+ */
+LTTNG_HIDDEN
+const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule);
+
+/* If not present return NULL
+ * Caller DO NOT own the returned object
+ */
+LTTNG_HIDDEN
+const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode(
+               const struct lttng_event_rule *rule);
+
+/*
+ * If not present return NULL
+ * Caller OWN the returned object
+ * TODO: should this be done another way?
+ */
+LTTNG_HIDDEN
+struct lttng_event_exclusion *lttng_event_rule_generate_exclusions(
+               struct lttng_event_rule *rule);
+
+/*
+ * This is compatibility helper, allowing us to generate a sessiond side (not
+ * communication) struct lttng_event object. This facilitate integration with
+ * current code.
+ * Caller OWN the returned object
+ */
+LTTNG_HIDDEN
+struct lttng_event *lttng_event_rule_generate_lttng_event(
+               const struct lttng_event_rule *rule);
+
+/* Quick helper to test if the event rule apply to an agent domain */
+LTTNG_HIDDEN
+bool lttng_event_rule_is_agent(const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_type_str(enum lttng_event_rule_type type);
+
+#endif /* LTTNG_EVENT_RULE_INTERNAL_H */
diff --git a/include/lttng/event-rule/event-rule.h b/include/lttng/event-rule/event-rule.h
new file mode 100644 (file)
index 0000000..71678b7
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_H
+#define LTTNG_EVENT_RULE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lttng_event_rule;
+
+enum lttng_event_rule_type {
+       LTTNG_EVENT_RULE_TYPE_UNKNOWN = -1,
+       LTTNG_EVENT_RULE_TYPE_TRACEPOINT = 100,
+       LTTNG_EVENT_RULE_TYPE_SYSCALL = 101,
+       LTTNG_EVENT_RULE_TYPE_KPROBE = 102,
+       LTTNG_EVENT_RULE_TYPE_KRETPROBE = 103,
+       LTTNG_EVENT_RULE_TYPE_UPROBE = 104,
+};
+
+
+enum lttng_event_rule_status {
+       LTTNG_EVENT_RULE_STATUS_OK = 0,
+       LTTNG_EVENT_RULE_STATUS_ERROR = -1,
+       LTTNG_EVENT_RULE_STATUS_UNKNOWN = -2,
+       LTTNG_EVENT_RULE_STATUS_INVALID = -3,
+       LTTNG_EVENT_RULE_STATUS_UNSET = -4,
+       LTTNG_EVENT_RULE_STATUS_UNSUPPORTED = -5,
+};
+
+/**
+ * Event rule describe a set of criteria to be used as a discriminant in regards
+ * to a set of events.
+ *
+ * Event rule have the following base properties:
+ *   - the instrumentation type for the event rule,
+ *       - tracepoint
+ *       - syscall
+ *       - probe
+ *       - userspace-probe
+ *   - the domain the event rule covers.
+ */
+
+/*
+ * Get the event rule type.
+ *
+ * Returns the type of an event rule on success, LTTNG_EVENT_RULE_UNKNOWN on
+ * error.
+ */
+extern enum lttng_event_rule_type lttng_event_rule_get_type(
+               const struct lttng_event_rule *event_rule);
+
+/*
+ * Destroy (release) a event_rule object.
+ */
+extern void lttng_event_rule_destroy(struct lttng_event_rule *rule);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_H */
diff --git a/include/lttng/event-rule/kprobe-internal.h b/include/lttng/event-rule/kprobe-internal.h
new file mode 100644 (file)
index 0000000..f3df8e4
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_KPROBE_INTERNAL_H
+#define LTTNG_EVENT_RULE_KPROBE_INTERNAL_H
+
+#include <lttng/event-rule/kprobe.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <common/buffer-view.h>
+#include <common/macros.h>
+
+struct lttng_event_rule_kprobe {
+       struct lttng_event_rule parent;
+       char *name;
+       struct {
+               uint64_t address;
+               uint64_t offset;
+               char *symbol_name;
+               bool set;
+       } probe;
+};
+
+struct lttng_event_rule_kprobe_comm {
+       uint32_t name_len;
+       uint32_t probe_symbol_name_len;
+       uint64_t probe_address;
+       uint64_t probe_offset;
+       /* name, source symbol_name */
+       char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_kprobe_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **rule);
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kprobe_get_address(
+               const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kprobe_get_offset(
+               const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_kprobe_get_symbol_name(
+               const struct lttng_event_rule *rule);
+
+#endif /* LTTNG_EVENT_RULE_KPROBE_INTERNAL_H */
diff --git a/include/lttng/event-rule/kprobe.h b/include/lttng/event-rule/kprobe.h
new file mode 100644 (file)
index 0000000..16f7488
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_KPROBE_H
+#define LTTNG_EVENT_RULE_KPROBE_H
+
+#include <lttng/event-rule/event-rule.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * TODO:
+ */
+extern struct lttng_event_rule *lttng_event_rule_kprobe_create(void);
+
+/*
+ * Set the source of a kprobe event rule.
+ *
+ * TODO: list possible format
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_source(
+               struct lttng_event_rule *rule, const char *source);
+
+/*
+ * Set the name of a kprobe event rule.
+ *
+ * The name is copied.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_name(
+               struct lttng_event_rule *rule, const char *name);
+
+/*
+ * Get the name of a kprobe event rule.
+ *
+ * The caller does not assume the ownership of the returned name.
+ * The name shall only only be used for the duration of the event
+ * rule's lifetime, or before a different name is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on
+ * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed,
+ * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kprobe_get_name(
+               const struct lttng_event_rule *rule, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_KPROBE_H */
diff --git a/include/lttng/event-rule/kretprobe-internal.h b/include/lttng/event-rule/kretprobe-internal.h
new file mode 100644 (file)
index 0000000..bf90906
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_KRETPROBE_INTERNAL_H
+#define LTTNG_EVENT_RULE_KRETPROBE_INTERNAL_H
+
+#include <lttng/event-rule/kretprobe.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <common/buffer-view.h>
+#include <common/macros.h>
+
+struct lttng_event_rule_kretprobe {
+       struct lttng_event_rule parent;
+       char *name;
+       struct {
+               uint64_t address;
+               uint64_t offset;
+               char *symbol_name;
+       } probe;
+};
+
+struct lttng_event_rule_kretprobe_comm {
+       uint32_t name_len;
+       uint32_t probe_symbol_len;
+       /*name, probe symbol_name */
+       char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_kretprobe_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **rule);
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kretprobe_get_address(
+               const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kretprobe_get_offset(
+               const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_kretprobe_get_symbol_name(
+               const struct lttng_event_rule *rule);
+
+#endif /* LTTNG_EVENT_RULE_KRETPROBE_INTERNAL_H */
diff --git a/include/lttng/event-rule/kretprobe.h b/include/lttng/event-rule/kretprobe.h
new file mode 100644 (file)
index 0000000..5ed0852
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_KRETPROBE_H
+#define LTTNG_EVENT_RULE_KRETPROBE_H
+
+#include <lttng/event-rule/event-rule.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * TODO:
+ */
+extern struct lttng_event_rule *lttng_event_rule_kretprobe_create(void);
+
+/*
+ * Set the source of a kretprobe event rule.
+ *
+ * TODO: list possible format
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kretprobe_set_source(
+               struct lttng_event_rule *rule, const char *source);
+
+/*
+ * Set the name of a kretprobe event rule.
+ *
+ * The name is copied.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kretprobe_set_name(
+               struct lttng_event_rule *rule, const char *name);
+
+/*
+ * Get the name of a kretprobe event rule.
+ *
+ * The caller does not assume the ownership of the returned name.
+ * The name shall only only be used for the duration of the event
+ * rule's lifetime, or before a different name is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on
+ * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed,
+ * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kretprobe_get_name(
+               const struct lttng_event_rule *rule, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_KRETPROBE_H */
diff --git a/include/lttng/event-rule/syscall-internal.h b/include/lttng/event-rule/syscall-internal.h
new file mode 100644 (file)
index 0000000..47e8e0f
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H
+#define LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H
+
+#include <lttng/event-rule/syscall.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <common/buffer-view.h>
+#include <common/macros.h>
+
+struct lttng_event_rule_syscall {
+       struct lttng_event_rule parent;
+       char *pattern;
+       char *filter_expression;
+
+       /* internal use only */
+       struct {
+               char *filter;
+               struct lttng_filter_bytecode *bytecode;
+       } internal_filter;
+};
+
+struct lttng_event_rule_syscall_comm {
+       uint32_t pattern_len;
+       uint32_t filter_expression_len;
+       /* pattern, filter expression */
+       char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_syscall_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **rule);
+
+#endif /* LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H */
diff --git a/include/lttng/event-rule/syscall.h b/include/lttng/event-rule/syscall.h
new file mode 100644 (file)
index 0000000..7d3bd17
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_SYSCALL_H
+#define LTTNG_EVENT_RULE_SYSCALL_H
+
+#include <lttng/event-rule/event-rule.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * TODO:
+ */
+extern struct lttng_event_rule *lttng_event_rule_syscall_create(void);
+
+/*
+ * Set the pattern of a syscall event rule.
+ *
+ * Pattern can contains wildcard '*'. See man lttng-enable-event.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_syscall_set_pattern(
+               struct lttng_event_rule *rule, const char *pattern);
+
+/*
+ * Get the pattern of a tracecpoint event rule.
+ *
+ * The caller does not assume the ownership of the returned pattern. The
+ * pattern shall only only be used for the duration of the event rule's
+ * lifetime, or before a different pattern is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's pattern
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid
+ * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern
+ * was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_syscall_get_pattern(
+               const struct lttng_event_rule *rule, const char **pattern);
+
+/*
+ * Set the filter expression of a syscall event rule.
+ *
+ * The expression is copied.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_syscall_set_filter(
+               struct lttng_event_rule *rule, const char *expression);
+
+/*
+ * Get the filter expression of a syscall event rule.
+ *
+ * The caller does not assume the ownership of the returned filter expression.
+ * The filter expression shall only only be used for the duration of the event
+ * rule's lifetime, or before a different filter expression is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's filter
+ * expression on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid
+ * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a filter expression
+ * was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_syscall_get_filter(
+               const struct lttng_event_rule *rule, const char **expression);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_H */
diff --git a/include/lttng/event-rule/tracepoint-internal.h b/include/lttng/event-rule/tracepoint-internal.h
new file mode 100644 (file)
index 0000000..e2432dd
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H
+#define LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H
+
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/domain.h>
+#include <lttng/event.h>
+#include <common/buffer-view.h>
+#include <common/macros.h>
+
+struct lttng_event_rule_tracepoint {
+       struct lttng_event_rule parent;
+
+       /* Domain */
+       enum lttng_domain_type domain;
+
+       /* Name pattern */
+       char *pattern;
+
+       /* Filter */
+       char *filter_expression;
+
+       /* Loglevel */
+       struct {
+               enum lttng_loglevel_type type;
+               int value;
+       } loglevel;
+
+       /* Exclusions */
+
+       struct {
+               char **values;
+               unsigned int count;
+       } exclusions;
+
+       /* internal use only */
+       struct {
+               char *filter;
+               struct lttng_filter_bytecode *bytecode;
+       } internal_filter;
+};
+
+struct lttng_event_rule_tracepoint_comm {
+       /* enum lttng_domain_type */
+       int8_t domain_type;
+       /* enum lttng_event_logleven_type */
+       int8_t loglevel_type;
+       int32_t loglevel_value;
+       uint32_t pattern_len;
+       uint32_t filter_expression_len;
+       uint32_t exclusions_count;
+       uint32_t exclusions_len;
+       /*
+        * pattern, filter expression and exclusions each terminating with '\0'
+        */
+       char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_tracepoint_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **rule);
+
+#endif /* LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H */
diff --git a/include/lttng/event-rule/tracepoint.h b/include/lttng/event-rule/tracepoint.h
new file mode 100644 (file)
index 0000000..8868828
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_TRACEPOINT_H
+#define LTTNG_EVENT_RULE_TRACEPOINT_H
+
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/domain.h>
+#include <lttng/event.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * TODO:
+ */
+extern struct lttng_event_rule *lttng_event_rule_tracepoint_create(
+               enum lttng_domain_type domain);
+
+/*
+ * Set the pattern of a tracepoint event rule.
+ *
+ * Pattern can contains wildcard '*'. See man lttng-enable-event.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_pattern(
+               struct lttng_event_rule *rule, const char *pattern);
+
+/*
+ * Get the pattern of a tracepoint event rule.
+ *
+ * The caller does not assume the ownership of the returned pattern. The
+ * pattern shall only only be used for the duration of the event rule's
+ * lifetime, or before a different pattern is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's pattern
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid
+ * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern
+ * was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_pattern(
+               const struct lttng_event_rule *rule, const char **pattern);
+
+/*
+ * Set the domain type of a tracepoint event rule.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter(
+               struct lttng_event_rule *rule, const char *expression);
+
+/*
+ * Get the domain type of a tracecpoint event rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the domain type output parameter
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is
+ * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern was not set prior to
+ * this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_domain_type(
+               const struct lttng_event_rule *rule,
+               enum lttng_domain_type *type);
+
+/*
+ * Set the filter expression of a tracepoint event rule.
+ *
+ * The expression is copied.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter(
+               struct lttng_event_rule *rule, const char *expression);
+
+/*
+ * Get the filter expression of a tracepoint event rule.
+ *
+ * The caller does not assume the ownership of the returned filter expression.
+ * The filter expression shall only only be used for the duration of the event
+ * rule's lifetime, or before a different filter expression is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's filter
+ * expression on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid
+ * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a filter expression
+ * was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter(
+               const struct lttng_event_rule *rule, const char **expression);
+
+/*
+ * Set the single loglevel of a tracepoint event rule.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel(
+               struct lttng_event_rule *rule, int level);
+
+/*
+ * Set the loglevel range of a tracepoint event rule.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_set_loglevel_range(
+               struct lttng_event_rule *rule, int level);
+
+/*
+ * Set the loglevel to all of a tracepoint event rule.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_set_loglevel_all(struct lttng_event_rule *rule);
+
+/*
+ * Get the loglevel type of a tracepoint event rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the loglevel type output
+ * parameter on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter
+ * is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a loglevel was not set prior
+ * to this call.
+ */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_get_loglevel_type(
+               const struct lttng_event_rule *rule, enum lttng_loglevel_type *type);
+
+/*
+ * Get the loglevel of a tracepoint event rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the loglevel output parameter
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is
+ * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a loglevel was not set prior to
+ * this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_loglevel(
+               const struct lttng_event_rule *rule, int *level);
+
+/*
+ * Set the exclusions property of a event rule.
+ *
+ * The passed exclusions will be copied to the event_rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK on success,
+ * LTTNG_EVENT_RULE_STATUS_INVALID if invalid parameters are passed, or
+ * LTTNG_EVENT_RULE_STATUS_UNSUPPORTED if this property is not supported by the
+ * domain.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_exclusions(
+               struct lttng_event_rule *rule,
+               unsigned int count,
+               const char **exclusions);
+
+/*
+ * Get the exclusions property count of a event rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the count output parameter
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is
+ * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a domain type was not set prior
+ * to this call.
+ */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_get_exclusions_count(
+               const struct lttng_event_rule *rule, unsigned int *count);
+
+/*
+ * TODO:
+ * */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_get_exclusion_at_index(
+               const struct lttng_event_rule *rule,
+               unsigned int index,
+               const char **exclusion);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_TRACEPOINT_H */
diff --git a/include/lttng/event-rule/uprobe-internal.h b/include/lttng/event-rule/uprobe-internal.h
new file mode 100644 (file)
index 0000000..c26c8c7
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_UPROBE_INTERNAL_H
+#define LTTNG_EVENT_RULE_UPROBE_INTERNAL_H
+
+#include <lttng/event-rule/uprobe.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <common/buffer-view.h>
+#include <common/macros.h>
+
+struct lttng_event_rule_uprobe {
+       struct lttng_event_rule parent;
+       char *name;
+       struct lttng_userspace_probe_location *location;
+};
+
+struct lttng_event_rule_uprobe_comm {
+       uint32_t name_len;
+       uint32_t location_len;
+       /*name, location object */
+       char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_uprobe_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **rule);
+
+LTTNG_HIDDEN
+struct lttng_userspace_probe_location *
+lttng_event_rule_uprobe_get_location_no_const(
+               const struct lttng_event_rule *rule);
+
+#endif /* LTTNG_EVENT_RULE_UPROBE_INTERNAL_H */
diff --git a/include/lttng/event-rule/uprobe.h b/include/lttng/event-rule/uprobe.h
new file mode 100644 (file)
index 0000000..dbe07bb
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef LTTNG_EVENT_RULE_UPROBE_H
+#define LTTNG_EVENT_RULE_UPROBE_H
+
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/userspace-probe.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * TODO:
+ */
+extern struct lttng_event_rule *lttng_event_rule_uprobe_create(void);
+
+/*
+ * Set the location of a uprobe event rule.
+ *
+ * The location is copied internally.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_location(
+               struct lttng_event_rule *rule, const struct lttng_userspace_probe_location *location);
+
+/*
+ * Get the location of a uprobe event rule.
+ *
+ * The caller does not assume the ownership of the returned location.
+ * The location shall only be used for the duration of the event
+ * rule's lifetime, or before a different location is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's location
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed,
+ * or LTTNG_EVENT_RULE_STATUS_UNSET if a location was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_location(
+               const struct lttng_event_rule *rule,
+               const struct lttng_userspace_probe_location **location);
+
+/*
+ * Set the name of a uprobe event rule.
+ *
+ * The name is copied.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_name(
+               struct lttng_event_rule *rule, const char *name);
+
+/*
+ * Get the name of a uprobe event rule.
+ *
+ * The caller does not assume the ownership of the returned name.
+ * The name shall only only be used for the duration of the event
+ * rule's lifetime, or before a different name is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on
+ * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed,
+ * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_name(
+               const struct lttng_event_rule *rule, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_UPROBE_H */
index 4557cf0d72a0a39c870b12076671157ced8d4cf3..a3d51894498d3a66da505d58bd454f97c4a79f1b 100644 (file)
 #include <lttng/session-descriptor.h>
 #include <lttng/destruction-handle.h>
 #include <lttng/action/action.h>
+#include <lttng/action/group.h>
 #include <lttng/action/notify.h>
+#include <lttng/action/rotate-session.h>
+#include <lttng/action/snapshot-session.h>
+#include <lttng/action/start-session.h>
+#include <lttng/action/stop-session.h>
 #include <lttng/condition/condition.h>
 #include <lttng/condition/buffer-usage.h>
 #include <lttng/condition/session-consumed-size.h>
 #include <lttng/condition/session-rotation.h>
 #include <lttng/condition/evaluation.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/kprobe.h>
+#include <lttng/event-rule/kretprobe.h>
+#include <lttng/event-rule/syscall.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event-rule/uprobe.h>
 #include <lttng/notification/channel.h>
 #include <lttng/notification/notification.h>
 #include <lttng/trigger/trigger.h>
index 72d4922375c1c029420184aeceeb291ee77bae3d..8012a1e4ea58303456f4d6c23fb0e624e3f7278f 100644 (file)
@@ -50,7 +50,7 @@ struct lttng_snapshot_output_list {
        size_t count;
 
        /*
-        * Containes snapshot output object.
+        * Contains snapshot output object.
         */
        struct lttng_snapshot_output *array;
 };
index 9bdf775a97c8e36dc7fe3846ec01189e6120e95c..33e9a53f9f8dfbcbd97eeb0084a25979eab8e9d8 100644 (file)
@@ -39,15 +39,15 @@ void lttng_snapshot_output_destroy(struct lttng_snapshot_output *output);
  */
 
 /* Return snapshot ID. */
-uint32_t lttng_snapshot_output_get_id(struct lttng_snapshot_output *output);
+uint32_t lttng_snapshot_output_get_id(const struct lttng_snapshot_output *output);
 /* Return maximum size of a snapshot. */
-uint64_t lttng_snapshot_output_get_maxsize(struct lttng_snapshot_output *output);
+uint64_t lttng_snapshot_output_get_maxsize(const struct lttng_snapshot_output *output);
 /* Return snapshot name. */
-const char *lttng_snapshot_output_get_name(struct lttng_snapshot_output *output);
+const char *lttng_snapshot_output_get_name(const struct lttng_snapshot_output *output);
 /* Return snapshot control URL in a text format. */
-const char *lttng_snapshot_output_get_ctrl_url(struct lttng_snapshot_output *output);
+const char *lttng_snapshot_output_get_ctrl_url(const struct lttng_snapshot_output *output);
 /* Return snapshot data URL in a text format. */
-const char *lttng_snapshot_output_get_data_url(struct lttng_snapshot_output *output);
+const char *lttng_snapshot_output_get_data_url(const struct lttng_snapshot_output *output);
 
 /*
  * Snapshot output setter family functions.
@@ -65,6 +65,36 @@ int lttng_snapshot_output_set_size(uint64_t size,
 /* Set the snapshot name. */
 int lttng_snapshot_output_set_name(const char *name,
                struct lttng_snapshot_output *output);
+
+/*
+ * Set the output destination to be a path on the local filesystem.
+ *
+ * The path must be absolute.  It can optionally begin with `file://`.
+ */
+int lttng_snapshot_output_set_local_path(const char *path,
+               struct lttng_snapshot_output *output);
+
+/*
+ * Set the output destination to be the network from a combined control/data
+ * URL.
+ *
+ * `url` must start with `net://` or `net6://`.
+ */
+int lttng_snapshot_output_set_network_url(const char *url,
+               struct lttng_snapshot_output *output);
+
+/*
+ * Set the output destination to be the network using separate URLs for control
+ * and data.
+ *
+ * `ctrl_url` and `data_url` must start with `tcp://` or `tcp6://`.
+ */
+int lttng_snapshot_output_set_network_urls(
+               const char *ctrl_url, const char *data_url,
+               struct lttng_snapshot_output *output);
+
+// Deprecated?
+
 /* Set the control URL. Local and remote URL are supported. */
 int lttng_snapshot_output_set_ctrl_url(const char *url,
                struct lttng_snapshot_output *output);
index cff4504a7d71e3b656bce857d6307125a338ff94..64937661f5f79f85e07c5d4ff970dd2023a17355 100644 (file)
 #include <common/macros.h>
 #include <common/buffer-view.h>
 #include <common/dynamic-buffer.h>
+#include <common/dynamic-array.h>
+#include <common/credentials.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <sys/types.h>
+#include <urcu/ref.h>
 
 struct lttng_trigger {
+       struct urcu_ref ref; /* internal use only */
+       bool owns_internal_objects; /* internal use only */
+
        struct lttng_condition *condition;
        struct lttng_action *action;
+       char *name;
+       struct { /* Internal use only */
+               bool set;
+               uint64_t value;
+       } key;
+       struct { /* internal use only */
+               struct lttng_credentials credentials;
+               bool set;
+       } creds;
+       struct {
+               enum lttng_trigger_firing_policy_type type;
+               uint64_t threshold;
+               uint64_t current_count;
+       } firing_policy;
+};
+
+struct lttng_triggers {
+       struct lttng_dynamic_pointer_array array;
 };
 
 struct lttng_trigger_comm {
        /* length excludes its own length. */
+       uint32_t name_length /* Includes '\0' */;
        uint32_t length;
-       /* A condition and action object follow. */
+       uint8_t policy_type;
+       uint64_t policy_threshold;
+       /* A name, condition and action object follow. */
        char payload[];
 } LTTNG_PACKED;
 
+struct lttng_triggers_comm {
+       uint32_t count;
+       uint32_t length;
+       /* Count * lttng_trigger_comm structure */
+       char payload[];
+};
+
 LTTNG_HIDDEN
 ssize_t lttng_trigger_create_from_buffer(const struct lttng_buffer_view *view,
                struct lttng_trigger **trigger);
 
 LTTNG_HIDDEN
-int lttng_trigger_serialize(struct lttng_trigger *trigger,
-               struct lttng_dynamic_buffer *buf);
+int lttng_trigger_serialize(const struct lttng_trigger *trigger,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send);
 
 LTTNG_HIDDEN
-const struct lttng_condition *lttng_trigger_get_const_condition(
-               const struct lttng_trigger *trigger);
+bool lttng_trigger_validate(const struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+int lttng_trigger_assign(struct lttng_trigger *dst,
+               const struct lttng_trigger *src);
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_equal(const struct lttng_trigger *a,
+               const struct lttng_trigger *b);
+
+LTTNG_HIDDEN
+void lttng_trigger_set_key(struct lttng_trigger *trigger, uint64_t key);
+
+LTTNG_HIDDEN
+uint64_t lttng_trigger_get_key(const struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset);
+
+/*
+ * Allocate a new list of lttng_trigger.
+ * The returned object must be freed via lttng_triggers_destroy.
+ */
+LTTNG_HIDDEN
+struct lttng_triggers *lttng_triggers_create(void);
+
+/*
+ * Return the non-const pointer of an element at index "index" of a
+ * lttng_triggers.
+ *
+ * The ownership of the lttng_triggers element is NOT transfered.
+ * The returned object can NOT be freed via lttng_trigger_destroy.
+ */
+LTTNG_HIDDEN
+struct lttng_trigger *lttng_triggers_get_pointer_of_index(
+               const struct lttng_triggers *triggers, unsigned int index);
+
+/*
+ * TODO:
+ */
+LTTNG_HIDDEN
+int lttng_triggers_add(
+               struct lttng_triggers *triggers, struct lttng_trigger *trigger);
+
+/*
+ * Serialize a trigger collection to a lttng_dynamic_buffer.
+ * Return LTTNG_OK on success, negative lttng error code on error.
+ */
+LTTNG_HIDDEN
+int lttng_triggers_serialize(const struct lttng_triggers *triggers,
+               struct lttng_dynamic_buffer *buffer);
+
+LTTNG_HIDDEN
+ssize_t lttng_triggers_create_from_buffer(const struct lttng_buffer_view *view,
+               struct lttng_triggers **triggers);
 
 LTTNG_HIDDEN
-const struct lttng_action *lttng_trigger_get_const_action(
+const struct lttng_credentials *lttng_trigger_get_credentials(
                const struct lttng_trigger *trigger);
 
 LTTNG_HIDDEN
-bool lttng_trigger_validate(struct lttng_trigger *trigger);
+void lttng_trigger_set_credentials(
+               struct lttng_trigger *trigger, uid_t uid, gid_t git);
+
+LTTNG_HIDDEN
+void lttng_trigger_get(struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+void lttng_trigger_put(struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_ready_to_fire(
+               struct lttng_trigger *trigger);
+
+/*
+ * Return the type of any uderlying domain requirement. If no particular
+ * requirement is needed return LTTNG_DOMAIN_NONE.
+ */
+LTTNG_HIDDEN
+enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction(
+               const struct lttng_trigger *trigger);
 
 #endif /* LTTNG_TRIGGER_INTERNAL_H */
index feffc6a8f9fb0f1c05c8b1f19b9cb1f308988a7e..823a026518ecfc7d42a7462415576a3190667df9 100644 (file)
@@ -11,6 +11,8 @@
 struct lttng_action;
 struct lttng_condition;
 struct lttng_trigger;
+/* A collection of trigger */
+struct lttng_triggers;
 
 #ifdef __cplusplus
 extern "C" {
@@ -21,6 +23,20 @@ enum lttng_register_trigger_status {
        LTTNG_REGISTER_TRIGGER_STATUS_INVALID = -1,
 };
 
+enum lttng_trigger_status {
+       LTTNG_TRIGGER_STATUS_OK = 0,
+       LTTNG_TRIGGER_STATUS_ERROR = -1,
+       LTTNG_TRIGGER_STATUS_UNKNOWN = -2,
+       LTTNG_TRIGGER_STATUS_INVALID = -3,
+       LTTNG_TRIGGER_STATUS_UNSET = -4,
+       LTTNG_TRIGGER_STATUS_UNSUPPORTED = -5,
+};
+
+enum lttng_trigger_firing_policy_type {
+       LTTNG_TRIGGER_FIRE_EVERY_N = 0,
+       LTTNG_TRIGGER_FIRE_ONCE_AFTER_N = 1,
+};
+
 /*
  * Create a trigger object associating a condition and an action.
  *
@@ -52,6 +68,9 @@ extern struct lttng_trigger *lttng_trigger_create(
 extern struct lttng_condition *lttng_trigger_get_condition(
                struct lttng_trigger *trigger);
 
+const struct lttng_condition *lttng_trigger_get_const_condition(
+               const struct lttng_trigger *trigger);
+
 /*
  * Get the action of a trigger.
  *
@@ -62,6 +81,58 @@ extern struct lttng_condition *lttng_trigger_get_condition(
 extern struct lttng_action *lttng_trigger_get_action(
                struct lttng_trigger *trigger);
 
+const struct lttng_action *lttng_trigger_get_const_action(
+               const struct lttng_trigger *trigger);
+
+/*
+ * Get the name of a trigger.
+ *
+ * The caller does not assume the ownership of the returned name.
+ * The name shall only only be used for the duration of the trigger's
+ * lifetime, or before a different name is set.
+ *
+ * Returns LTTNG_TRIGGER_STATUS_OK and a pointer to the trigger's name on
+ * success, LTTNG_TRIGGER_STATUS_INVALID if an invalid parameter is passed,
+ * or LTTNG_TRIGGER_STATUS_UNSET if a name was not set prior to this call.
+ */
+extern enum lttng_trigger_status lttng_trigger_get_name(
+               const struct lttng_trigger *trigger, const char **name);
+
+/*
+ * Set the trigger name.
+ *
+ * A name is optional.
+ * A name will be assigned on trigger registration if no name is set.
+ *
+ * The name is copied.
+ *
+ * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_trigger_status lttng_trigger_set_name(
+               struct lttng_trigger *trigger, const char *name);
+
+/*
+ * Set the trigger firing policy.
+ *
+ * This is optional. By default a trigger is set to fire each time the
+ * associated condition occurs.
+ *
+ * Threshold is the number of time the condition must be hit before the policy is
+ * enacted.
+ *
+ * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_trigger_status lttng_trigger_set_firing_policy(
+               struct lttng_trigger *trigger,
+               enum lttng_trigger_firing_policy_type policy_type,
+               unsigned long long threshold);
+extern enum lttng_trigger_status lttng_trigger_get_firing_policy(
+               const struct lttng_trigger *trigger,
+               enum lttng_trigger_firing_policy_type *policy_type,
+               unsigned long long *threshold);
+
 /*
  * Destroy (frees) a trigger object.
  */
@@ -83,7 +154,44 @@ extern int lttng_register_trigger(struct lttng_trigger *trigger);
  *
  * Return 0 on success, a negative LTTng error code on error.
  */
-extern int lttng_unregister_trigger(struct lttng_trigger *trigger);
+extern int lttng_unregister_trigger(const struct lttng_trigger *trigger);
+
+/*
+ * List current triggers.
+ * 
+ * On success, triggers is allocated.
+ * The trigger collection must be free by the caller with lttng_destroy_triggers
+ *
+ * Returns 0 on success, else a negative LTTng error code.
+ */
+extern int lttng_list_triggers(struct lttng_triggers **triggers);
+
+/*
+ * Get a trigger from the collection at a given index.
+ *
+ * Note that the collection maintains the ownership of the returned trigger.
+ * It must not be destroyed by the user, nor should it be held beyond the
+ * lifetime of the trigger collection.
+ *
+ * Returns a trigger, or NULL on error.
+ */
+extern const struct lttng_trigger *lttng_triggers_get_at_index(
+               const struct lttng_triggers *triggers, unsigned int index);
+
+/*
+ * Get the number of trigger in a tracker id list.
+ *
+ * Return LTTNG_TRIGGER_STATUS_OK on success,
+ * LTTNG_TRIGGER_STATUS_INVALID when passed invalid parameters.
+ */
+extern enum lttng_trigger_status lttng_triggers_get_count(
+               const struct lttng_triggers *triggers, unsigned int *count);
+
+/*
+ * Destroy a trigger collection.
+ */
+extern void lttng_triggers_destroy(struct lttng_triggers *ids);
+
 
 #ifdef __cplusplus
 }
index 8287954931791e21bf3b7e0e5f595c91e46f605d..ef6a5726e1edf64710dea6826ea8472e5aa9555a 100644 (file)
 #include <common/dynamic-buffer.h>
 #include <common/buffer-view.h>
 
+typedef bool (*userspace_probe_location_equal_cb)(
+               const struct lttng_userspace_probe_location *a,
+               const struct lttng_userspace_probe_location *b);
+
 /*
  * No elf-specific comm structure is defined since no elf-specific payload is
  * currently needed.
@@ -79,6 +83,7 @@ struct lttng_userspace_probe_location_tracepoint_comm {
 struct lttng_userspace_probe_location {
        enum lttng_userspace_probe_location_type type;
        struct lttng_userspace_probe_location_lookup_method *lookup_method;
+       userspace_probe_location_equal_cb equal;
 };
 
 struct lttng_userspace_probe_location_function {
@@ -142,4 +147,13 @@ LTTNG_HIDDEN
 struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy(
                const struct lttng_userspace_probe_location *location);
 
+LTTNG_HIDDEN
+bool lttng_userspace_probe_location_is_equal(
+               const struct lttng_userspace_probe_location *a,
+               const struct lttng_userspace_probe_location *b);
+
+LTTNG_HIDDEN
+int lttng_userspace_probe_location_set_binary_fd(
+               struct lttng_userspace_probe_location *location, int fd);
+
 #endif /* LTTNG_USERSPACE_PROBE_INTERNAL_H */
diff --git a/m4/ax_append_compile_flags.m4 b/m4/ax_append_compile_flags.m4
new file mode 100644 (file)
index 0000000..9c85635
--- /dev/null
@@ -0,0 +1,46 @@
+# ============================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+#   AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+#   For every FLAG1, FLAG2 it is checked whether the compiler works with the
+#   flag.  If it does, the flag is added FLAGS-VARIABLE
+#
+#   If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
+#   CFLAGS) is used.  During the check the flag is always added to the
+#   current language's flags.
+#
+#   If EXTRA-FLAGS is defined, it is added to the current language's default
+#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
+#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
+#   force the compiler to issue an error when a bad flag is given.
+#
+#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+#   NOTE: This macro depends on the AX_APPEND_FLAG and
+#   AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with
+#   AX_APPEND_LINK_FLAGS.
+#
+# LICENSE
+#
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 7
+
+AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
+[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
+AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
+for flag in $1; do
+  AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4])
+done
+])dnl AX_APPEND_COMPILE_FLAGS
diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4
new file mode 100644 (file)
index 0000000..dd6d8b6
--- /dev/null
@@ -0,0 +1,50 @@
+# ===========================================================================
+#      https://www.gnu.org/software/autoconf-archive/ax_append_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
+#
+# DESCRIPTION
+#
+#   FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
+#   added in between.
+#
+#   If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
+#   CFLAGS) is used.  FLAGS-VARIABLE is not changed if it already contains
+#   FLAG.  If FLAGS-VARIABLE is unset in the shell, it is set to exactly
+#   FLAG.
+#
+#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 8
+
+AC_DEFUN([AX_APPEND_FLAG],
+[dnl
+AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
+AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
+AS_VAR_SET_IF(FLAGS,[
+  AS_CASE([" AS_VAR_GET(FLAGS) "],
+    [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
+    [
+     AS_VAR_APPEND(FLAGS,[" $1"])
+     AC_RUN_LOG([: FLAGS="$FLAGS"])
+    ])
+  ],
+  [
+  AS_VAR_SET(FLAGS,[$1])
+  AC_RUN_LOG([: FLAGS="$FLAGS"])
+  ])
+AS_VAR_POPDEF([FLAGS])dnl
+])dnl AX_APPEND_FLAG
diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
new file mode 100644 (file)
index 0000000..bd753b3
--- /dev/null
@@ -0,0 +1,53 @@
+# ===========================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+#   Check whether the given FLAG works with the current language's compiler
+#   or gives an error.  (Warnings, however, are ignored)
+#
+#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+#   success/failure.
+#
+#   If EXTRA-FLAGS is defined, it is added to the current language's default
+#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
+#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
+#   force the compiler to issue an error when a bad flag is given.
+#
+#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 6
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+  AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+    [AS_VAR_SET(CACHEVAR,[yes])],
+    [AS_VAR_SET(CACHEVAR,[no])])
+  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+  [m4_default([$2], :)],
+  [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/m4/ax_require_defined.m4 b/m4/ax_require_defined.m4
new file mode 100644 (file)
index 0000000..17c3eab
--- /dev/null
@@ -0,0 +1,37 @@
+# ===========================================================================
+#    https://www.gnu.org/software/autoconf-archive/ax_require_defined.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_REQUIRE_DEFINED(MACRO)
+#
+# DESCRIPTION
+#
+#   AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
+#   been defined and thus are available for use.  This avoids random issues
+#   where a macro isn't expanded.  Instead the configure script emits a
+#   non-fatal:
+#
+#     ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
+#
+#   It's like AC_REQUIRE except it doesn't expand the required macro.
+#
+#   Here's an example:
+#
+#     AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
+#
+# LICENSE
+#
+#   Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 2
+
+AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
+  m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
+])dnl AX_REQUIRE_DEFINED
diff --git a/rename.sh b/rename.sh
new file mode 100644 (file)
index 0000000..ded105d
--- /dev/null
+++ b/rename.sh
@@ -0,0 +1,13 @@
+ag --ignore rename.sh -l "event-rule-uprobe-internal\.h" | xargs sed -i 's/event-rule-uprobe-internal\.h/uprobe-internal.h/g'
+ag --ignore rename.sh -l "event-rule-kprobe-internal\.h" | xargs sed -i 's/event-rule-kprobe-internal\.h/kprobe-internal.h/g'
+ag --ignore rename.sh -l "event-rule-kretprobe-internal\.h" | xargs sed -i 's/event-rule-kretprobe-internal\.h/kretprobe-internal.h/g'
+ag --ignore rename.sh -l "event-rule-syscall-internal\.h" | xargs sed -i 's/event-rule-syscall-internal\.h/syscall-internal.h/g'
+ag --ignore rename.sh -l "event-rule-tracepoint-internal\.h" | xargs sed -i 's/event-rule-tracepoint-internal\.h/tracepoint-internal.h/g'
+
+ag --ignore rename.sh -l "event-rule-uprobe\.h" | xargs sed -i 's/event-rule-uprobe\.h/uprobe.h/g'
+ag --ignore rename.sh -l "event-rule-kprobe\.h" | xargs sed -i 's/event-rule-kprobe\.h/kprobe.h/g'
+ag --ignore rename.sh -l "event-rule-kretprobe\.h" | xargs sed -i 's/event-rule-kretprobe\.h/kretprobe.h/g'
+ag --ignore rename.sh -l "event-rule-syscall\.h" | xargs sed -i 's/event-rule-syscall\.h/syscall.h/g'
+ag --ignore rename.sh -l "event-rule-tracepoint\.h" | xargs sed -i 's/event-rule-tracepoint\.h/tracepoint.h/g'
+git add .
+git commit --amend --no-edit
index c0bf42c5a58076b407eb46c50a567a4825f61894..98c52c59dcd835a2c48cde3def2543d025f13132 100644 (file)
@@ -180,9 +180,9 @@ struct lttng_crash_layout {
 };
 
 /* Variables */
-static char *progname,
-       *opt_viewer_path = NULL,
-       *opt_output_path = NULL;
+static const char *progname;
+static char *opt_viewer_path = NULL;
+static char *opt_output_path = NULL;
 
 static char *input_path;
 
@@ -296,7 +296,7 @@ static int parse_args(int argc, char **argv)
        }
 
        if (!opt_viewer_path) {
-               opt_viewer_path = DEFAULT_VIEWER;
+               opt_viewer_path = (char *) DEFAULT_VIEWER;
        }
 
        /* No leftovers, or more than one input path, print usage and quit */
index 09014be0f112959bc07e28b07081c940f339633a..8566ecfa8298c197f6c87f755c747d8f57296d3a 100644 (file)
@@ -10,6 +10,8 @@
 #define _LGPL_SOURCE
 #include <assert.h>
 
+#include "cmd-2-2.h"
+
 #include <common/common.h>
 #include <common/sessiond-comm/relayd.h>
 
index ee53655be18f8390934c01269c537a9a339d0117..dd807125b6342e243c4720f835ba01ba94c37f27 100644 (file)
@@ -54,7 +54,8 @@ lttng_sessiond_SOURCES = utils.c utils.h \
                        manage-kernel.c manage-kernel.h \
                        manage-consumer.c manage-consumer.h \
                        clear.c clear.h \
-                       tracker.c tracker.h
+                       tracker.c tracker.h \
+                       action-executor.c action-executor.h
 
 if HAVE_LIBLTTNG_UST_CTL
 lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \
diff --git a/src/bin/lttng-sessiond/action-executor.c b/src/bin/lttng-sessiond/action-executor.c
new file mode 100644 (file)
index 0000000..40686eb
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "action-executor.h"
+#include "cmd.h"
+#include "health-sessiond.h"
+#include "lttng-sessiond.h"
+#include "notification-thread-internal.h"
+#include "session.h"
+#include "thread.h"
+#include <common/macros.h>
+#include <lttng/action/group.h>
+#include <lttng/action/notify.h>
+#include <lttng/action/rotate-session.h>
+#include <lttng/action/snapshot-session.h>
+#include <lttng/action/start-session.h>
+#include <lttng/action/stop-session.h>
+#include <lttng/condition/evaluation.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/lttng-error.h>
+#include <lttng/trigger/trigger-internal.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <urcu/list.h>
+
+#define THREAD_NAME "Action Executor"
+#define MAX_QUEUED_WORK_COUNT 8192
+
+struct action_work_item {
+       uint64_t id;
+       struct lttng_trigger *trigger;
+       struct notification_client_list *client_list;
+       struct cds_list_head list_node;
+};
+
+struct action_executor {
+       struct lttng_thread *thread;
+       struct notification_thread_handle *notification_thread_handle;
+       struct {
+               uint64_t pending_count;
+               struct cds_list_head list;
+               pthread_cond_t cond;
+               pthread_mutex_t lock;
+       } work;
+       bool should_quit;
+       uint64_t next_work_item_id;
+};
+
+typedef int (*action_executor_handler)(struct action_executor *executor,
+               const struct action_work_item *,
+               const struct lttng_action *action);
+
+static int action_executor_notify_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               const struct lttng_action *);
+static int action_executor_start_session_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               const struct lttng_action *);
+static int action_executor_stop_session_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               const struct lttng_action *);
+static int action_executor_rotate_session_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               const struct lttng_action *);
+static int action_executor_snapshot_session_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               const struct lttng_action *);
+static int action_executor_group_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               const struct lttng_action *);
+static int action_executor_generic_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               const struct lttng_action *);
+
+static const action_executor_handler action_executors[] = {
+       [LTTNG_ACTION_TYPE_NOTIFY] = action_executor_notify_handler,
+       [LTTNG_ACTION_TYPE_START_SESSION] = action_executor_start_session_handler,
+       [LTTNG_ACTION_TYPE_STOP_SESSION] = action_executor_stop_session_handler,
+       [LTTNG_ACTION_TYPE_ROTATE_SESSION] = action_executor_rotate_session_handler,
+       [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = action_executor_snapshot_session_handler,
+       [LTTNG_ACTION_TYPE_GROUP] = action_executor_group_handler,
+};
+
+static const char *get_action_name(const struct lttng_action *action)
+{
+       const char *action_type_names[] = {
+               [LTTNG_ACTION_TYPE_NOTIFY] = "Notify",
+               [LTTNG_ACTION_TYPE_START_SESSION] = "Start session",
+               [LTTNG_ACTION_TYPE_STOP_SESSION] = "Stop session",
+               [LTTNG_ACTION_TYPE_ROTATE_SESSION] = "Rotate session",
+               [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = "Snapshot session",
+               [LTTNG_ACTION_TYPE_GROUP] = "Group",
+       };
+
+       return action_type_names[lttng_action_get_type(action)];
+}
+
+static const char *get_trigger_name(const struct lttng_trigger *trigger)
+{
+       const char *trigger_name;
+       enum lttng_trigger_status trigger_status;
+
+       trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
+       assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       return trigger_name;
+}
+
+static int client_handle_transmission_status(
+               struct notification_client *client,
+               enum client_transmission_status status,
+               void *user_data)
+{
+       struct action_executor *executor = user_data;
+
+       switch (status) {
+       case CLIENT_TRANSMISSION_STATUS_COMPLETE:
+               DBG("Sent notification to client");
+               break;
+       default:
+               ERR("Could not send notification to client");
+       }
+
+       return 0;
+}
+
+static int action_executor_notify_handler(struct action_executor *executor,
+               const struct action_work_item *work_item,
+               const struct lttng_action *action)
+{
+       int ret = 0;
+       struct lttng_evaluation *evaluation = NULL;
+
+       assert(work_item->client_list);
+
+       evaluation = lttng_evaluation_event_rule_create(
+                       get_trigger_name(work_item->trigger));
+       if (!evaluation) {
+               ERR("Failed to create event rule hit evaluation");
+               ret = -1;
+               goto end;
+       }
+
+       ret = notification_client_list_send_evaluation(work_item->client_list,
+                       lttng_trigger_get_const_condition(work_item->trigger),
+                       evaluation,
+                       lttng_trigger_get_credentials(work_item->trigger), NULL,
+                       client_handle_transmission_status, executor);
+end:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+static int action_executor_start_session_handler(struct action_executor *executor,
+               const struct action_work_item *work_item,
+               const struct lttng_action *action)
+{
+       int ret = 0;
+       const char *session_name;
+       enum lttng_action_status action_status;
+       struct ltt_session *session;
+
+       action_status = lttng_action_start_session_get_session_name(
+                       action, &session_name);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR("Failed to get session name from \"%s\" action",
+                               get_action_name(action));
+               ret = -1;
+               goto end;
+       }
+
+       session_lock_list();
+       session = session_find_by_name(session_name);
+       if (session) {
+               enum lttng_error_code cmd_ret;
+
+               session_lock(session);
+               cmd_ret = cmd_start_trace(session);
+               session_unlock(session);
+
+               switch (cmd_ret) {
+               case LTTNG_OK:
+                       DBG("Successfully started session \"%s\" on behalf of trigger \"%s\"",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger));
+                       break;
+               case LTTNG_ERR_TRACE_ALREADY_STARTED:
+                       DBG("Attempted to start session \"%s\" on behalf of trigger \"%s\" but it was already started",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger));
+                       break;
+               default:
+                       WARN("Failed to start session \"%s\" on behalf of trigger \"%s\": %s",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger),
+                                       lttng_strerror(-cmd_ret));
+                       break;
+               }
+               session_put(session);
+       } else {
+               DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"",
+                               session_name, get_action_name(action),
+                               get_trigger_name(work_item->trigger));
+       }
+       session_unlock_list();
+end:
+       return ret;
+}
+
+static int action_executor_stop_session_handler(struct action_executor *executor,
+               const struct action_work_item *work_item,
+               const struct lttng_action *action)
+{
+       int ret = 0;
+       const char *session_name;
+       enum lttng_action_status action_status;
+       struct ltt_session *session;
+
+       action_status = lttng_action_stop_session_get_session_name(
+                       action, &session_name);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR("Failed to get session name from \"%s\" action",
+                               get_action_name(action));
+               ret = -1;
+               goto end;
+       }
+
+       session_lock_list();
+       session = session_find_by_name(session_name);
+       if (session) {
+               enum lttng_error_code cmd_ret;
+
+               session_lock(session);
+               cmd_ret = cmd_stop_trace(session);
+               session_unlock(session);
+
+               switch (cmd_ret) {
+               case LTTNG_OK:
+                       DBG("Successfully stopped session \"%s\" on behalf of trigger \"%s\"",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger));
+                       break;
+               case LTTNG_ERR_TRACE_ALREADY_STOPPED:
+                       DBG("Attempted to stop session \"%s\" on behalf of trigger \"%s\" but it was already stopped",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger));
+                       break;
+               default:
+                       WARN("Failed to stop session \"%s\" on behalf of trigger \"%s\": %s",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger),
+                                       lttng_strerror(-cmd_ret));
+                       break;
+               }
+               session_put(session);
+       } else {
+               DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"",
+                               session_name, get_action_name(action),
+                               get_trigger_name(work_item->trigger));
+       }
+       session_unlock_list();
+end:
+       return ret;
+}
+
+static int action_executor_rotate_session_handler(struct action_executor *executor,
+               const struct action_work_item *work_item,
+               const struct lttng_action *action)
+{
+       int ret = 0;
+       const char *session_name;
+       enum lttng_action_status action_status;
+       struct ltt_session *session;
+
+       action_status = lttng_action_rotate_session_get_session_name(
+                       action, &session_name);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR("Failed to get session name from \"%s\" action",
+                               get_action_name(action));
+               ret = -1;
+               goto end;
+       }
+
+       session_lock_list();
+       session = session_find_by_name(session_name);
+       if (session) {
+               enum lttng_error_code cmd_ret;
+
+               session_lock(session);
+               cmd_ret = cmd_rotate_session(session, NULL, false,
+                               LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED);
+               session_unlock(session);
+
+               switch (cmd_ret) {
+               case LTTNG_OK:
+                       DBG("Successfully started rotation of session \"%s\" on behalf of trigger \"%s\"",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger));
+                       break;
+               case LTTNG_ERR_ROTATION_PENDING:
+                       DBG("Attempted to start a rotation of session \"%s\" on behalf of trigger \"%s\" but a rotation is already ongoing",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger));
+                       break;
+               case LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP:
+               case LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR:
+                       DBG("Attempted to start a rotation of session \"%s\" on behalf of trigger \"%s\" but a rotation has already been completed since the last stop or clear",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger));
+                       break;
+               default:
+                       WARN("Failed to start a rotation of session \"%s\" on behalf of trigger \"%s\": %s",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger),
+                                       lttng_strerror(-cmd_ret));
+                       break;
+               }
+               session_put(session);
+       } else {
+               DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"",
+                               session_name, get_action_name(action),
+                               get_trigger_name(work_item->trigger));
+       }
+       session_unlock_list();
+end:
+       return ret;
+}
+
+static int action_executor_snapshot_session_handler(struct action_executor *executor,
+               const struct action_work_item *work_item,
+               const struct lttng_action *action)
+{
+       int ret = 0;
+       const char *session_name;
+       enum lttng_action_status action_status;
+       struct ltt_session *session;
+       const struct lttng_snapshot_output default_snapshot_output = {
+               .max_size = UINT64_MAX,
+       };
+       const struct lttng_snapshot_output *snapshot_output =
+                       &default_snapshot_output;
+
+       action_status = lttng_action_snapshot_session_get_session_name(
+                       action, &session_name);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR("Failed to get session name from \"%s\" action",
+                               get_action_name(action));
+               ret = -1;
+               goto end;
+       }
+
+       action_status = lttng_action_snapshot_session_get_output_const(
+                       action, &snapshot_output);
+       if (action_status != LTTNG_ACTION_STATUS_OK &&
+                       action_status != LTTNG_ACTION_STATUS_UNSET) {
+               ERR("Failed to get output from \"%s\" action",
+                               get_action_name(action));
+               ret = -1;
+               goto end;
+       }
+
+       session_lock_list();
+       session = session_find_by_name(session_name);
+       if (session) {
+               enum lttng_error_code cmd_ret;
+
+               session_lock(session);
+               cmd_ret = cmd_snapshot_record(session, snapshot_output, 0);
+               session_unlock(session);
+
+               switch (cmd_ret) {
+               case LTTNG_OK:
+                       DBG("Successfully recorded snapshot of session \"%s\" on behalf of trigger \"%s\"",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger));
+                       break;
+               default:
+                       WARN("Failed to record snapshot of session \"%s\" on behalf of trigger \"%s\": %s",
+                                       session_name,
+                                       get_trigger_name(work_item->trigger),
+                                       lttng_strerror(-cmd_ret));
+                       break;
+               }
+               session_put(session);
+       } else {
+               DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"",
+                               session_name, get_action_name(action),
+                               get_trigger_name(work_item->trigger));
+       }
+       session_unlock_list();
+end:
+       return ret;
+}
+
+static int action_executor_group_handler(struct action_executor *executor,
+               const struct action_work_item *work_item,
+               const struct lttng_action *action_group)
+{
+       int ret = 0;
+       unsigned int i, count;
+       enum lttng_action_status action_status;
+
+       action_status = lttng_action_group_get_count(action_group, &count);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               /* Fatal error. */
+               ERR("Failed to get count of action in action group");
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Action group has %u action%s", count, count != 1 ? "s" : "");
+       for (i = 0; i < count; i++) {
+               const struct lttng_action *action =
+                               lttng_action_group_get_at_index_const(
+                                               action_group, i);
+
+               ret = action_executor_generic_handler(
+                               executor, work_item, action);
+               if (ret) {
+                       ERR("Stopping the execution of the action group of trigger \"%s\" following a fatal error",
+                                       get_trigger_name(work_item->trigger));
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+static int action_executor_generic_handler(struct action_executor *executor,
+               const struct action_work_item *work_item,
+               const struct lttng_action *action)
+{
+       DBG("Executing action \"%s\" of trigger \"%s\" action work item %" PRIu64,
+                       get_action_name(action),
+                       get_trigger_name(work_item->trigger),
+                       work_item->id);
+
+       return action_executors[lttng_action_get_type(action)](
+                       executor, work_item, action);
+}
+
+static int action_work_item_execute(struct action_executor *executor,
+               struct action_work_item *work_item)
+{
+       int ret;
+       const struct lttng_action *action =
+                       lttng_trigger_get_const_action(work_item->trigger);
+
+       DBG("Starting execution of action work item %" PRIu64 " of trigger \"%s\"",
+                       work_item->id, get_trigger_name(work_item->trigger));
+       ret = action_executor_generic_handler(executor, work_item, action);
+       DBG("Completed execution of action work item %" PRIu64 " of trigger \"%s\"",
+                       work_item->id, get_trigger_name(work_item->trigger));
+       return ret;
+}
+
+static void action_work_item_destroy(struct action_work_item *work_item)
+{
+       lttng_trigger_put(work_item->trigger);
+       notification_client_list_put(work_item->client_list);
+       free(work_item);
+}
+
+static void *action_executor_thread(void *_data)
+{
+       struct action_executor *executor = _data;
+
+       assert(executor);
+
+       health_register(health_sessiond, HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR);
+
+       rcu_register_thread();
+       rcu_thread_online();
+
+       DBG("Entering work execution loop");
+       pthread_mutex_lock(&executor->work.lock);
+       while (!executor->should_quit) {
+               int ret;
+               struct action_work_item *work_item;
+
+               health_code_update();
+               if (executor->work.pending_count == 0) {
+                       health_poll_entry();
+                       DBG("No work items enqueued, entering wait");
+                       pthread_cond_wait(&executor->work.cond,
+                                       &executor->work.lock);
+                       DBG("Woke-up from wait");
+                       health_poll_exit();
+                       continue;
+               }
+
+               /* Pop item from front of the listwith work lock held. */
+               work_item = cds_list_first_entry(&executor->work.list,
+                               struct action_work_item, list_node);
+               cds_list_del(&work_item->list_node);
+               executor->work.pending_count--;
+
+               /*
+                * Work can be performed without holding the work lock,
+                * allowing new items to be queued.
+                */
+               pthread_mutex_unlock(&executor->work.lock);
+               ret = action_work_item_execute(executor, work_item);
+               action_work_item_destroy(work_item);
+               if (ret) {
+                       /* Fatal error. */
+                       break;
+               }
+               health_code_update();
+               pthread_mutex_lock(&executor->work.lock);
+       }
+       pthread_mutex_unlock(&executor->work.lock);
+       DBG("Left work execution loop");
+
+       health_code_update();
+
+       rcu_thread_offline();
+       rcu_unregister_thread();
+       health_unregister(health_sessiond);
+
+       return NULL;
+}
+
+static bool shutdown_action_executor_thread(void *_data)
+{
+       struct action_executor *executor = _data;
+
+       /* TODO. */
+       executor->should_quit = true;
+       pthread_cond_signal(&executor->work.cond);
+       return true;
+}
+
+static void clean_up_action_executor_thread(void *_data)
+{
+       struct action_executor *executor = _data;
+
+       assert(cds_list_empty(&executor->work.list));
+
+       pthread_mutex_destroy(&executor->work.lock);
+       pthread_cond_destroy(&executor->work.cond);
+       free(executor);
+}
+
+struct action_executor *action_executor_create(
+               struct notification_thread_handle *handle)
+{
+       struct action_executor *executor = zmalloc(sizeof(*executor));
+
+       if (!executor) {
+               goto end;
+       }
+
+       CDS_INIT_LIST_HEAD(&executor->work.list);
+       pthread_cond_init(&executor->work.cond, NULL);
+       pthread_mutex_init(&executor->work.lock, NULL);
+       executor->notification_thread_handle = handle;
+
+       executor->thread = lttng_thread_create(THREAD_NAME,
+                       action_executor_thread, shutdown_action_executor_thread,
+                       clean_up_action_executor_thread, executor);
+end:
+       return executor;
+}
+
+void action_executor_destroy(struct action_executor *executor)
+{
+       struct action_work_item *work_item, *tmp;
+
+       /* TODO Wait for work list to drain? */
+       lttng_thread_shutdown(executor->thread);
+       pthread_mutex_lock(&executor->work.lock);
+       if (executor->work.pending_count != 0) {
+               WARN("%" PRIu64
+                       " trigger action%s still queued for execution and will be discarded",
+                               executor->work.pending_count,
+                               executor->work.pending_count == 1 ? " is" :
+                                                                   "s are");
+       }
+
+       cds_list_for_each_entry_safe (
+                       work_item, tmp, &executor->work.list, list_node) {
+               WARN("Discarding action work item %" PRIu64
+                               " associated to trigger \"%s\"",
+                               work_item->id, get_trigger_name(work_item->trigger));
+               cds_list_del(&work_item->list_node);
+               action_work_item_destroy(work_item);
+       }
+       pthread_mutex_unlock(&executor->work.lock);
+       lttng_thread_put(executor->thread);
+}
+
+/* RCU read-lock must be held by the caller. */
+enum action_executor_status action_executor_enqueue(
+               struct action_executor *executor,
+               struct lttng_trigger *trigger,
+               struct notification_client_list *client_list)
+{
+       enum action_executor_status executor_status = ACTION_EXECUTOR_STATUS_OK;
+       const uint64_t work_item_id = executor->next_work_item_id++;
+       struct action_work_item *work_item;
+       bool signal = false;
+
+       pthread_mutex_lock(&executor->work.lock);
+       /* Check for queue overflow. */
+       if (executor->work.pending_count >= MAX_QUEUED_WORK_COUNT) {
+               /* Most likely spammy, remove if it is the case. */
+               DBG("Refusing to enqueue action for trigger \"%s\" as work item %" PRIu64
+                   " (overflow)",
+                               get_trigger_name(trigger), work_item_id);
+               executor_status = ACTION_EXECUTOR_STATUS_OVERFLOW;
+               goto error_unlock;
+       }
+
+       work_item = zmalloc(sizeof(*work_item));
+       if (!work_item) {
+               PERROR("Failed to allocate action executor work item on behalf of trigger \"%s\"",
+                               get_trigger_name(trigger));
+               executor_status = ACTION_EXECUTOR_STATUS_ERROR;
+               goto error_unlock;
+       }
+
+       lttng_trigger_get(trigger);
+       if (client_list) {
+               const bool reference_acquired =
+                               notification_client_list_get(client_list);
+
+               assert(reference_acquired);
+       }
+
+       *work_item = (typeof(*work_item)){
+                       .id = work_item_id,
+                       .trigger = trigger,
+                       .client_list = client_list,
+                       .list_node = CDS_LIST_HEAD_INIT(work_item->list_node),
+       };
+       cds_list_add_tail(&work_item->list_node, &executor->work.list);
+       executor->work.pending_count++;
+       DBG("Enqueued action for trigger \"%s\" as work item %" PRIu64,
+                       get_trigger_name(trigger), work_item_id);
+       signal = true;
+
+error_unlock:
+       pthread_mutex_unlock(&executor->work.lock);
+       if (signal) {
+               pthread_cond_signal(&executor->work.cond);
+       }
+       return executor_status;
+}
diff --git a/src/bin/lttng-sessiond/action-executor.h b/src/bin/lttng-sessiond/action-executor.h
new file mode 100644 (file)
index 0000000..60a740d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef ACTION_EXECUTOR_H
+#define ACTION_EXECUTOR_H
+
+struct action_executor;
+struct notification_thread_handle;
+struct lttng_trigger;
+struct notification_client_list;
+
+enum action_executor_status {
+       ACTION_EXECUTOR_STATUS_OK,
+       ACTION_EXECUTOR_STATUS_OVERFLOW,
+       ACTION_EXECUTOR_STATUS_ERROR,
+       ACTION_EXECUTOR_STATUS_INVALID,
+};
+
+struct action_executor *action_executor_create(
+               struct notification_thread_handle *handle);
+
+void action_executor_destroy(struct action_executor *executor);
+
+enum action_executor_status action_executor_enqueue(
+       struct action_executor *executor,
+       struct lttng_trigger *trigger,
+       struct notification_client_list *list);
+
+#endif /* ACTION_EXECUTOR_H */
index c278ce95e506daabf779636b2544dc799181b38e..3587fd4713312a7f85d0c577adaadc6852cff418 100644 (file)
@@ -49,6 +49,8 @@ static void update_agent_app(struct agent_app *app)
 {
        struct ltt_session *session, *stmp;
        struct ltt_session_list *list;
+       struct agent *trigger_agent;
+       struct lttng_ht_iter iter;
 
        list = session_get_list();
        assert(list);
@@ -74,6 +76,14 @@ static void update_agent_app(struct agent_app *app)
                session_put(session);
        }
        session_unlock_list();
+
+       /* Do we need more locking here? maybe against trigger add? */
+       rcu_read_lock();
+       cds_lfht_for_each_entry (trigger_agents_ht_by_domain->ht, &iter.iter,
+                       trigger_agent, node.node) {
+               agent_update(trigger_agent, app->sock->fd);
+       }
+       rcu_read_unlock();
 }
 
 /*
index 1926b0820fc808f4076e12b8110a4ce5fe34cbae..0b95c4d1d1a1fc5ac283d2a66cbba2624b94d2e6 100644 (file)
 #include <urcu/uatomic.h>
 #include <urcu/rculist.h>
 
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule.h>
+
 #include <common/common.h>
 #include <common/sessiond-comm/agent.h>
 
@@ -101,7 +107,8 @@ no_match:
 }
 
 /*
- * Match function for the events hash table lookup by name and loglevel.
+ * Match function for the events hash table lookup by name, loglevel and
+ * filter_expression.
  */
 static int ht_match_event(struct cds_lfht_node *node,
                const void *_key)
@@ -682,6 +689,7 @@ int agent_enable_event(struct agent_event *event,
        }
 
        event->enabled = 1;
+       event->user_refcount++;
        ret = LTTNG_OK;
 
 error:
@@ -791,6 +799,17 @@ int agent_disable_event(struct agent_event *event,
                goto end;
        }
 
+       if (event->user_refcount - 1 != 0) {
+               /*
+                * Disable the agent event only when all users (trigger etc.)
+                * have disabled it.
+                */
+
+               event->user_refcount--;
+               ret = LTTNG_OK;
+               goto end;
+       }
+
        rcu_read_lock();
 
        cds_lfht_for_each_entry(agent_apps_ht_by_sock->ht, &iter.iter, app,
@@ -806,6 +825,7 @@ int agent_disable_event(struct agent_event *event,
                }
        }
 
+       event->user_refcount = 0;
        event->enabled = 0;
 
 error:
@@ -1206,6 +1226,66 @@ void agent_find_events_by_name(const char *name, struct agent *agt,
                        ht_match_event_by_name, &key, &iter->iter);
 }
 
+/*
+ * Find the agent event matching the trigger.
+ *
+ * RCU read side lock MUST be acquired. It must be kept for as long as
+ * the returned agent_event is used.
+ *
+ * Return object if found else NULL.
+ */
+struct agent_event *agent_find_event_by_trigger(
+               const struct lttng_trigger *trigger, struct agent *agt)
+{
+       enum lttng_condition_status c_status;
+       enum lttng_event_rule_status er_status;
+       enum lttng_domain_type d_type;
+       const struct lttng_condition *condition;
+       const struct lttng_event_rule *rule;
+       const char *name;
+       const char *filter_expression;
+       /* TODO validate if this is the unset value or no */
+       int loglevel_value = 0;
+       enum lttng_loglevel_type loglevel_type;
+
+       assert(agt);
+       assert(agt->events);
+
+       condition = lttng_trigger_get_const_condition(trigger);
+
+       assert(lttng_condition_get_type(condition) ==
+                       LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+       c_status = lttng_condition_event_rule_get_rule(condition, &rule);
+       assert(c_status == LTTNG_CONDITION_STATUS_OK);
+
+       assert(lttng_event_rule_get_type(rule) ==
+                       LTTNG_EVENT_RULE_TYPE_TRACEPOINT);
+
+       d_type = lttng_event_rule_get_domain_type(rule);
+       assert(d_type == LTTNG_DOMAIN_JUL || d_type == LTTNG_DOMAIN_LOG4J ||
+                       d_type == LTTNG_DOMAIN_PYTHON);
+
+       /* Get the name (aka pattern) */
+       er_status = lttng_event_rule_tracepoint_get_pattern(rule, &name);
+       assert(er_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       /* Get the internal filter_expression */
+       filter_expression = lttng_event_rule_get_filter(rule);
+
+       er_status = lttng_event_rule_tracepoint_get_loglevel_type(
+                       rule, &loglevel_type);
+       assert(er_status == LTTNG_EVENT_RULE_STATUS_OK);
+       if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) {
+               er_status = lttng_event_rule_tracepoint_get_loglevel(
+                               rule, &loglevel_value);
+               assert(er_status == LTTNG_EVENT_RULE_STATUS_OK);
+       }
+
+       return agent_find_event(name, loglevel_type, loglevel_value,
+                       filter_expression, agt);
+}
+
 /*
  * Get the next agent event duplicate by name. This should be called
  * after a call to agent_find_events_by_name() to iterate on events.
@@ -1233,8 +1313,10 @@ void agent_event_next_duplicate(const char *name,
  * Return object if found else NULL.
  */
 struct agent_event *agent_find_event(const char *name,
-               enum lttng_loglevel_type loglevel_type, int loglevel_value,
-               char *filter_expression, struct agent *agt)
+               enum lttng_loglevel_type loglevel_type,
+               int loglevel_value,
+               const char *filter_expression,
+               struct agent *agt)
 {
        struct lttng_ht_node_str *node;
        struct lttng_ht_iter iter;
@@ -1347,6 +1429,21 @@ int agent_app_ht_alloc(void)
        return ret;
 }
 
+/*
+ * Allocate agent_apps_ht_by_sock.
+ */
+int trigger_agent_ht_alloc(void)
+{
+       int ret = 0;
+
+       trigger_agents_ht_by_domain = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+       if (!trigger_agents_ht_by_domain) {
+               ret = -1;
+       }
+
+       return ret;
+}
+
 /*
  * Destroy a agent application by socket.
  */
@@ -1396,6 +1493,32 @@ void agent_app_ht_clean(void)
        lttng_ht_destroy(agent_apps_ht_by_sock);
 }
 
+/*
+ * Clean-up the trigger agent hash table and destroy it.
+ */
+void trigger_agent_ht_clean(void)
+{
+       struct lttng_ht_node_u64 *node;
+       struct lttng_ht_iter iter;
+
+       if (!trigger_agents_ht_by_domain) {
+               return;
+       }
+       rcu_read_lock();
+       cds_lfht_for_each_entry (trigger_agents_ht_by_domain->ht, &iter.iter,
+                       node, node) {
+               struct agent *agent;
+
+               (void) lttng_ht_del(trigger_agents_ht_by_domain, &iter);
+
+               agent = caa_container_of(node, struct agent, node);
+               agent_destroy(agent);
+       }
+       rcu_read_unlock();
+
+       lttng_ht_destroy(trigger_agents_ht_by_domain);
+}
+
 /*
  * Update a agent application (given socket) using the given agent.
  *
@@ -1449,3 +1572,27 @@ void agent_update(struct agent *agt, int sock)
 
        rcu_read_unlock();
 }
+
+struct agent *trigger_find_agent(enum lttng_domain_type domain_type)
+{
+       struct agent *agt = NULL;
+       struct lttng_ht_node_u64 *node;
+       struct lttng_ht_iter iter;
+       uint64_t key;
+
+       assert(trigger_agents_ht_by_domain);
+
+       DBG3("Trigger agent lookup for domain %d", domain_type);
+
+       key = domain_type;
+
+       lttng_ht_lookup(trigger_agents_ht_by_domain, &key, &iter);
+       node = lttng_ht_iter_get_node_u64(&iter);
+       if (!node) {
+               goto end;
+       }
+       agt = caa_container_of(node, struct agent, node);
+
+end:
+       return agt;
+}
index 2d2d6425559d2330904bd9d6c83eb24dbcba7872..ebb12628e15ebd08cae0981021991a3a193bd527 100644 (file)
  */
 extern struct lttng_ht *agent_apps_ht_by_sock;
 
+/*
+ * Hash table that contains the trigger agents by domain */
+extern struct lttng_ht *trigger_agents_ht_by_domain;
+
 struct agent_ht_key {
        const char *name;
        int loglevel_value;
        enum lttng_loglevel_type loglevel_type;
-       char *filter_expression;
+       const char *filter_expression;
 };
 
 /*
@@ -87,6 +91,12 @@ struct agent_event {
        struct lttng_filter_bytecode *filter;
        char *filter_expression;
        struct lttng_event_exclusion *exclusion;
+
+       /*
+        * Multiple triggers and events can use this agent event.
+        * The event can only be disabled when the count is zero.
+        */
+       unsigned int user_refcount;
 };
 
 /*
@@ -133,8 +143,10 @@ struct agent_event *agent_create_event(const char *name,
 void agent_add_event(struct agent_event *event, struct agent *agt);
 
 struct agent_event *agent_find_event(const char *name,
-               enum lttng_loglevel_type loglevel_type, int loglevel_value,
-               char *filter_expression, struct agent *agt);
+               enum lttng_loglevel_type loglevel_type,
+               int loglevel_value,
+               const char *filter_expression,
+               struct agent *agt);
 void agent_find_events_by_name(const char *name, struct agent *agt,
                struct lttng_ht_iter* iter);
 void agent_event_next_duplicate(const char *name,
@@ -167,4 +179,12 @@ void agent_update(struct agent *agt, int sock);
 int agent_list_events(struct lttng_event **events,
                enum lttng_domain_type domain);
 
+struct agent_event *agent_find_event_by_trigger(
+               const struct lttng_trigger *trigger, struct agent *agt);
+
+/* todo: find a better place for this */
+struct agent *trigger_find_agent(enum lttng_domain_type domain_type);
+void trigger_agent_ht_clean(void);
+int trigger_agent_ht_alloc(void);
+
 #endif /* LTTNG_SESSIOND_AGENT_H */
index ae0e020924b38b1929d97a1857c27eb382d5826a..e381514e063d39e6b2a75abe9762bc95248af62a 100644 (file)
@@ -30,6 +30,7 @@
 #include "utils.h"
 #include "manage-consumer.h"
 #include "clear.h"
+#include "agent-thread.h"
 
 static bool is_root;
 
@@ -182,7 +183,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                case LTTNG_CONSUMER64_UST:
                {
                        if (config.consumerd64_lib_dir.value) {
-                               char *tmp;
+                               const char *tmp;
                                size_t tmplen;
                                char *tmpnew;
 
@@ -219,7 +220,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                case LTTNG_CONSUMER32_UST:
                {
                        if (config.consumerd32_lib_dir.value) {
-                               char *tmp;
+                               const char *tmp;
                                size_t tmplen;
                                char *tmpnew;
 
@@ -532,7 +533,6 @@ static int receive_userspace_probe(struct command_ctx *cmd_ctx, int sock,
 {
        int fd, ret;
        struct lttng_userspace_probe_location *probe_location;
-       const struct lttng_userspace_probe_location_lookup_method *lookup = NULL;
        struct lttng_dynamic_buffer probe_location_buffer;
        struct lttng_buffer_view buffer_view;
 
@@ -592,30 +592,7 @@ static int receive_userspace_probe(struct command_ctx *cmd_ctx, int sock,
         * Set the file descriptor received from the client through the unix
         * socket in the probe location.
         */
-       lookup = lttng_userspace_probe_location_get_lookup_method(probe_location);
-       if (!lookup) {
-               ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
-               goto error;
-       }
-
-       /*
-        * From the kernel tracer's perspective, all userspace probe event types
-        * are all the same: a file and an offset.
-        */
-       switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
-               ret = lttng_userspace_probe_location_function_set_binary_fd(
-                               probe_location, fd);
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
-               ret = lttng_userspace_probe_location_tracepoint_set_binary_fd(
-                               probe_location, fd);
-               break;
-       default:
-               ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
-               goto error;
-       }
-
+       ret = lttng_userspace_probe_location_set_binary_fd(probe_location, fd);
        if (ret) {
                ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
                goto error;
@@ -710,6 +687,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock,
        int ret = LTTNG_OK;
        int need_tracing_session = 1;
        int need_domain;
+       int need_consumerd = 1;
 
        DBG("Processing client command %d", cmd_ctx->lsm->cmd_type);
 
@@ -733,19 +711,29 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock,
        case LTTNG_SET_SESSION_SHM_PATH:
        case LTTNG_REGENERATE_METADATA:
        case LTTNG_REGENERATE_STATEDUMP:
-       case LTTNG_REGISTER_TRIGGER:
-       case LTTNG_UNREGISTER_TRIGGER:
        case LTTNG_ROTATE_SESSION:
        case LTTNG_ROTATION_GET_INFO:
        case LTTNG_ROTATION_SET_SCHEDULE:
        case LTTNG_SESSION_LIST_ROTATION_SCHEDULES:
        case LTTNG_CLEAR_SESSION:
+       case LTTNG_LIST_TRIGGERS:
                need_domain = 0;
                break;
        default:
                need_domain = 1;
        }
 
+       /* Needs a functioning consumerd */
+       switch (cmd_ctx->lsm->cmd_type) {
+       case LTTNG_REGISTER_TRIGGER:
+       case LTTNG_UNREGISTER_TRIGGER:
+               need_consumerd = 0;
+               break;
+       default:
+               need_consumerd = 1;
+               break;
+       }
+
        if (config.no_kernel && need_domain
                        && cmd_ctx->lsm->domain.type == LTTNG_DOMAIN_KERNEL) {
                if (!is_root) {
@@ -785,6 +773,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock,
        case LTTNG_ROTATE_SESSION:
        case LTTNG_ROTATION_GET_INFO:
        case LTTNG_SESSION_LIST_ROTATION_SCHEDULES:
+       case LTTNG_REGISTER_TRIGGER:
+       case LTTNG_LIST_TRIGGERS:
                break;
        default:
                /* Setup lttng message with no payload */
@@ -805,6 +795,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock,
        case LTTNG_SAVE_SESSION:
        case LTTNG_REGISTER_TRIGGER:
        case LTTNG_UNREGISTER_TRIGGER:
+       case LTTNG_LIST_TRIGGERS:
                need_tracing_session = 0;
                break;
        default:
@@ -883,7 +874,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock,
                }
 
                /* Consumer is in an ERROR state. Report back to client */
-               if (uatomic_read(&kernel_consumerd_state) == CONSUMER_ERROR) {
+               if (need_consumerd && uatomic_read(&kernel_consumerd_state) ==
+                                                     CONSUMER_ERROR) {
                        ret = LTTNG_ERR_NO_KERNCONSUMERD;
                        goto error;
                }
@@ -928,14 +920,21 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock,
        case LTTNG_DOMAIN_JUL:
        case LTTNG_DOMAIN_LOG4J:
        case LTTNG_DOMAIN_PYTHON:
+               if (!agent_tracing_is_enabled()) {
+                       ret = LTTNG_ERR_AGENT_TRACING_DISABLED;
+                       goto error;
+               }
+               /* Fallthrough */
        case LTTNG_DOMAIN_UST:
        {
                if (!ust_app_supported()) {
                        ret = LTTNG_ERR_NO_UST;
                        goto error;
                }
+
                /* Consumer is in an ERROR state. Report back to client */
-               if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) {
+               if (need_consumerd && uatomic_read(&ust_consumerd_state) ==
+                                                     CONSUMER_ERROR) {
                        ret = LTTNG_ERR_NO_USTCONSUMERD;
                        goto error;
                }
@@ -1957,8 +1956,33 @@ error_add_context:
        }
        case LTTNG_REGISTER_TRIGGER:
        {
+               struct lttng_dynamic_buffer payload;
+               struct lttng_trigger *return_trigger;
+
+               lttng_dynamic_buffer_init(&payload);
                ret = cmd_register_trigger(cmd_ctx, *sock,
-                               notification_thread_handle);
+                               notification_thread_handle, &return_trigger);
+               if (ret != LTTNG_OK) {
+                       goto error;
+               }
+
+               ret = lttng_trigger_serialize(return_trigger, &payload, NULL);
+               if (ret) {
+                       ERR("Failed to serialize trigger in reply to \"register trigger\" command");
+                       ret = LTTNG_ERR_NOMEM;
+                       lttng_trigger_destroy(return_trigger);
+                       goto error;
+               }
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, payload.data,
+                               payload.size);
+               if (ret) {
+                       lttng_trigger_destroy(return_trigger);
+                       ret = LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+               lttng_trigger_destroy(return_trigger);
+               lttng_dynamic_buffer_reset(&payload);
+               ret = LTTNG_OK;
                break;
        }
        case LTTNG_UNREGISTER_TRIGGER:
@@ -2071,6 +2095,37 @@ error_add_context:
                ret = cmd_clear_session(cmd_ctx->session, sock);
                break;
        }
+       case LTTNG_LIST_TRIGGERS:
+       {
+               struct lttng_dynamic_buffer payload;
+               struct lttng_triggers *return_triggers;
+
+               lttng_dynamic_buffer_init(&payload);
+               ret = cmd_list_triggers(cmd_ctx, *sock,
+                               notification_thread_handle, &return_triggers);
+               if (ret != LTTNG_OK) {
+                       goto error;
+               }
+
+               ret = lttng_triggers_serialize(return_triggers, &payload);
+               if (ret) {
+                       ERR("Failed to serialize triggers in reply to \"list triggers\" command");
+                       ret = LTTNG_ERR_NOMEM;
+                       lttng_triggers_destroy(return_triggers);
+                       goto error;
+               }
+               ret = setup_lttng_msg_no_cmd_header(cmd_ctx, payload.data,
+                               payload.size);
+               if (ret) {
+                       ret = LTTNG_ERR_NOMEM;
+                       lttng_triggers_destroy(return_triggers);
+                       goto error;
+               }
+               lttng_dynamic_buffer_reset(&payload);
+               lttng_triggers_destroy(return_triggers);
+               ret = LTTNG_OK;
+               break;
+       }
        default:
                ret = LTTNG_ERR_UND;
                break;
index 5e522d6fe0886108b87df756e930a6519a5a8a09..f4c7390ef784d52d9371d982e78a68dab966e358 100644 (file)
 #include <lttng/location-internal.h>
 #include <lttng/trigger/trigger-internal.h>
 #include <lttng/condition/condition.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
+#include <lttng/event-rule/tracepoint.h>
 #include <lttng/action/action.h>
 #include <lttng/channel.h>
 #include <lttng/channel-internal.h>
@@ -4283,8 +4290,94 @@ end:
        return ret;
 }
 
+/* TODO: is this the best place to perform this? (code wise) */
+/*
+ * Set sock to -1 if reception of more information is not necessary e.g on
+ * unregister. TODO find a better way.
+ *
+ * On success LTTNG_OK. On error, returns lttng_error code.
+ * */
+static enum lttng_error_code prepare_trigger_object(struct lttng_trigger *trigger, int sock)
+{
+       enum lttng_error_code ret;
+       /* Internal object of the trigger might have to "generate" and
+        * "populate" internal field e.g filter bytecode
+        */
+       struct lttng_condition *condition = NULL;
+       condition = lttng_trigger_get_condition(trigger);
+       if (!condition) {
+               ret = LTTNG_ERR_INVALID_TRIGGER;
+               goto end;
+       }
+
+       switch (lttng_condition_get_type(condition)) {
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+       {
+               struct lttng_event_rule *event_rule;
+               const struct lttng_credentials *credential = lttng_trigger_get_credentials(trigger);
+               lttng_condition_event_rule_get_rule_no_const(
+                               condition, &event_rule);
+               ret = lttng_event_rule_populate(event_rule, credential->uid, credential->gid);
+               if (ret != LTTNG_OK) {
+                       goto end;
+               }
+
+               switch (lttng_event_rule_get_type(event_rule)) {
+               case LTTNG_EVENT_RULE_TYPE_UPROBE:
+               {
+                       int fd;
+                       struct lttng_userspace_probe_location *location = lttng_event_rule_uprobe_get_location_no_const(event_rule);
+
+                       if (sock < 0) {
+                               /* Nothing to receive */
+                               break;
+                       }
+                       /*
+                        * Receive the file descriptor to the target binary from
+                        * the client.
+                        */
+                       DBG("Receiving userspace probe target FD from client ...");
+                       ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1);
+                       if (ret <= 0) {
+                               DBG("Nothing recv() from client userspace probe fd... continuing");
+                               ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+                               goto end;
+                       }
+
+                       /*
+                        * Set the file descriptor received from the client
+                        * through the unix socket in the probe location.
+                        */
+                       ret = lttng_userspace_probe_location_set_binary_fd(
+                                       location, fd);
+                       if (ret) {
+                               ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+                               goto end;
+                       }
+
+                       break;
+               }
+               default:
+                       /* Nothing to do */
+                       break;
+               }
+               ret = LTTNG_OK;
+               break;
+       }
+       default:
+       {
+               ret = LTTNG_OK;
+               break;
+       }
+       }
+end:
+       return ret;
+}
+
+/* Caller must call lttng_destroy_trigger on the returned trigger object */
 int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock,
-               struct notification_thread_handle *notification_thread)
+               struct notification_thread_handle *notification_thread,
+               struct lttng_trigger **return_trigger)
 {
        int ret;
        size_t trigger_len;
@@ -4318,9 +4411,95 @@ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock,
                goto end;
        }
 
+       /* Set the trigger credential */
+       lttng_trigger_set_credentials(trigger, cmd_ctx->creds.uid, cmd_ctx->creds.gid);
+
+       /* Prepare internal trigger object if needed on reception.
+        * Handles also special treatment for certain internal object of the
+        * trigger (e.g uprobe event rule binary fd.
+        */
+       ret = prepare_trigger_object(trigger, sock);
+       if (ret != LTTNG_OK) {
+               goto end;
+       }
+
+       /*
+        * Since we return the trigger object, take a reference to it
+        * Caller is responsible for calling lttng_destroy_trigger on it.
+        * This thread does not OWN the trigger.
+        */
+       lttng_trigger_get(trigger);
+
+       /* Inform the notification thread */
        ret = notification_thread_command_register_trigger(notification_thread,
                        trigger);
-       /* Ownership of trigger was transferred. */
+       if (ret != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Synchronize tracers, only if needed */
+       /* TODO: maybe extract somewhere else */
+       {
+               struct lttng_condition *condition = NULL;
+               condition = lttng_trigger_get_condition(trigger);
+               if (!condition) {
+                       ret = LTTNG_ERR_INVALID_TRIGGER;
+                       goto end;
+               }
+
+               if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+                       const struct lttng_event_rule *rule = NULL;
+                       (void) lttng_condition_event_rule_get_rule(condition, &rule);
+                       if (!rule) {
+                               ret = LTTNG_ERR_INVALID_TRIGGER;
+                               goto end;
+                       }
+                       if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) {
+                               /* TODO: get the token value from the
+                                * notification thread and only perform an
+                                * enable and a disable.... This is NOT
+                                * OPTIMIZED AT ALL
+                                */
+                               kernel_update_tokens();
+                       } else {
+                               /* TODO: get the token value from the
+                                * notification thread and only perform an
+                                * enable and a disable.... This is NOT
+                                * OPTIMIZED AT ALL
+                                */
+                               ust_app_global_update_all_tokens();
+                               /* Agent handling */
+                               if (lttng_event_rule_is_agent(rule)) {
+                                       struct agent *agt;
+                                       const char *pattern;
+                                       enum lttng_domain_type domain_type;
+                                       domain_type = lttng_event_rule_get_domain_type(
+                                                       rule);
+                                       (void) lttng_event_rule_tracepoint_get_pattern(
+                                                       rule, &pattern);
+                                       agt = trigger_find_agent(domain_type);
+                                       if (!agt) {
+                                               agt = agent_create(domain_type);
+                                               if (!agt) {
+                                                       ret = LTTNG_ERR_NOMEM;
+                                                       goto end;
+                                               }
+                                               agent_add(agt, trigger_agents_ht_by_domain);
+                                       }
+
+                                       ret = trigger_agent_enable(
+                                                       trigger, agt);
+                                       if (ret != LTTNG_OK) {
+                                               goto end;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* Return an image of the updated object to the client */
+       *return_trigger = trigger;
+       /* Ownership of trigger was transferred to caller. */
        trigger = NULL;
 end:
        lttng_trigger_destroy(trigger);
@@ -4363,14 +4542,110 @@ int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock,
                goto end;
        }
 
+       lttng_trigger_set_credentials(trigger, cmd_ctx->creds.uid, cmd_ctx->creds.gid);
+
+       /* TODO: forgotting this bited me for agent since the filter is
+        * genereted on this side. Wonder if we could find a way to detect when
+        * do it or not.
+        */
+
+       /* Prepare internal trigger object if needed on reception.
+        * Handles also special treatment for certain internal object of the
+        * trigger (e.g uprobe event rule binary fd.
+        */
+       ret = prepare_trigger_object(trigger, -1);
+       if (ret != LTTNG_OK) {
+               goto end;
+       }
+
        ret = notification_thread_command_unregister_trigger(notification_thread,
                        trigger);
+
+       /* Synchronize tracers, only if needed */
+       /* TODO: maybe extract somewhere else */
+       {
+               struct lttng_condition *condition = NULL;
+               condition = lttng_trigger_get_condition(trigger);
+               if (!condition) {
+                       ret = LTTNG_ERR_INVALID_TRIGGER;
+                       goto end;
+               }
+
+               if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+                       const struct lttng_event_rule *rule = NULL;
+                       (void) lttng_condition_event_rule_get_rule(condition, &rule);
+                       if (!rule) {
+                               ret = LTTNG_ERR_INVALID_TRIGGER;
+                               goto end;
+                       }
+                       if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) {
+                               /* TODO: get the token value from the
+                                * notification thread and only perform an
+                                * enable and a disable.... This is NOT
+                                * OPTIMIZED AT ALL
+                                */
+                               kernel_update_tokens();
+                       } else {
+                               /* TODO: get the token value from the
+                                * notification thread and only perform an
+                                * enable and a disable.... This is NOT
+                                * OPTIMIZED AT ALL
+                                */
+                               ust_app_global_update_all_tokens();
+                               if (lttng_event_rule_is_agent(rule)) {
+                                       struct agent *agt;
+                                       const char *pattern;
+                                       enum lttng_domain_type domain_type;
+
+                                       domain_type = lttng_event_rule_get_domain_type(
+                                                       rule);
+                                       (void) lttng_event_rule_tracepoint_get_pattern(
+                                                       rule, &pattern);
+
+                                       agt = trigger_find_agent(domain_type);
+                                       if (!agt) {
+                                               ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
+                                               goto end;
+                                       }
+                                       ret = trigger_agent_disable(
+                                                       trigger, agt);
+                                       if (ret != LTTNG_OK) {
+                                               goto end;
+                                       }
+                               }
+                       }
+               }
+       }
+
 end:
        lttng_trigger_destroy(trigger);
        lttng_dynamic_buffer_reset(&trigger_buffer);
        return ret;
 }
 
+int cmd_list_triggers(struct command_ctx *cmd_ctx, int sock,
+               struct notification_thread_handle *notification_thread,
+               struct lttng_triggers **return_triggers)
+{
+       int ret = 0;
+       enum lttng_error_code ret_code;
+       struct lttng_triggers *triggers = NULL;
+
+       /* Get list of token trigger from the notification thread here */
+       ret_code = notification_thread_command_list_triggers(notification_thread_handle, cmd_ctx->creds.uid, cmd_ctx->creds.gid, &triggers);
+       if (ret_code != LTTNG_OK) {
+               ret = ret_code;
+               goto end;
+       }
+
+       /* Return a "view" of the current triggers */
+       *return_triggers = triggers;
+       triggers = NULL;
+       ret = LTTNG_OK;
+end:
+       lttng_triggers_destroy(triggers);
+       return ret;
+}
 /*
  * Send relayd sockets from snapshot output to consumer. Ignore request if the
  * snapshot output is *not* set with a remote destination.
index 23d9d5e026e0496ac31591f637e495873d7a8f31..76052100d32e05c388479d54e8dd283751a9268d 100644 (file)
@@ -125,10 +125,15 @@ int cmd_regenerate_metadata(struct ltt_session *session);
 int cmd_regenerate_statedump(struct ltt_session *session);
 
 int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock,
-               struct notification_thread_handle *notification_thread_handle);
+               struct notification_thread_handle *notification_thread_handle,
+               struct lttng_trigger **return_trigger);
 int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock,
                struct notification_thread_handle *notification_thread_handle);
 
+int cmd_list_triggers(struct command_ctx *cmd_ctx, int sock,
+               struct notification_thread_handle *notification_thread_handle,
+               struct lttng_triggers **return_triggers);
+
 int cmd_rotate_session(struct ltt_session *session,
                struct lttng_rotate_session_return *rotate_return,
                bool quiet_rotation,
index 4fe3dfce70e40d22a05d361e3071e8966e610258..ea147ddf82f81c170a3789f930c6945ec875dd02 100644 (file)
@@ -36,6 +36,7 @@ static void update_ust_app(int app_sock)
 {
        struct ltt_session *sess, *stmp;
        const struct ltt_session_list *session_list = session_get_list();
+       struct ust_app *app;
 
        /* Consumer is in an ERROR state. Stop any application update. */
        if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) {
@@ -43,10 +44,25 @@ static void update_ust_app(int app_sock)
                return;
        }
 
+       rcu_read_lock();
+       assert(app_sock >= 0);
+       app = ust_app_find_by_sock(app_sock);
+       if (app == NULL) {
+               /*
+                * Application can be unregistered before so
+                * this is possible hence simply stopping the
+                * update.
+                */
+               DBG3("UST app update failed to find app sock %d",
+                       app_sock);
+               goto unlock_rcu;
+       }
+
+       /* Update all tokens for the app */
+       ust_app_global_update_tokens(app);
+
        /* For all tracing session(s) */
        cds_list_for_each_entry_safe(sess, stmp, &session_list->head, list) {
-               struct ust_app *app;
-
                if (!session_get(sess)) {
                        continue;
                }
@@ -55,26 +71,14 @@ static void update_ust_app(int app_sock)
                        goto unlock_session;
                }
 
-               rcu_read_lock();
-               assert(app_sock >= 0);
-               app = ust_app_find_by_sock(app_sock);
-               if (app == NULL) {
-                       /*
-                        * Application can be unregistered before so
-                        * this is possible hence simply stopping the
-                        * update.
-                        */
-                       DBG3("UST app update failed to find app sock %d",
-                               app_sock);
-                       goto unlock_rcu;
-               }
                ust_app_global_update(sess->ust_session, app);
-       unlock_rcu:
-               rcu_read_unlock();
        unlock_session:
                session_unlock(sess);
                session_put(sess);
        }
+
+unlock_rcu:
+       rcu_read_unlock();
 }
 
 /*
@@ -386,6 +390,8 @@ static void *thread_dispatch_ust_registration(void *data)
                                /* Set app version. This call will print an error if needed. */
                                (void) ust_app_version(app);
 
+                               (void) ust_app_setup_trigger_group(app);
+
                                /* Send notify socket through the notify pipe. */
                                ret = send_socket_to_thread(
                                                notifiers->apps_cmd_notify_pipe_write_fd,
index 189236beb1171a05ba21e89b3ecb51216a7b5e47..6a7545dc0d982d84c361f8307e016ae5765ed2e3 100644 (file)
 #include <string.h>
 
 #include <lttng/lttng.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
 #include <common/error.h>
 #include <common/sessiond-comm/sessiond-comm.h>
 #include <common/filter.h>
@@ -27,6 +31,7 @@
 #include "trace-kernel.h"
 #include "trace-ust.h"
 #include "agent.h"
+#include "utils.h"
 
 /*
  * Add unique UST event based on the event name, filter bytecode and loglevel.
@@ -366,6 +371,20 @@ error:
        return ret;
 }
 
+static void agent_enable_all(struct agent *agt)
+{
+       struct agent_event *aevent;
+       struct lttng_ht_iter iter;
+
+       /* Flag every event that they are now enabled. */
+       rcu_read_lock();
+       cds_lfht_for_each_entry (
+                       agt->events->ht, &iter.iter, aevent, node.node) {
+               aevent->enabled = 1;
+       }
+       rcu_read_unlock();
+}
+
 /*
  * Enable all agent event for a given UST session.
  *
@@ -376,8 +395,6 @@ int event_agent_enable_all(struct ltt_ust_session *usess,
                struct lttng_filter_bytecode *filter ,char *filter_expression)
 {
        int ret;
-       struct agent_event *aevent;
-       struct lttng_ht_iter iter;
 
        assert(usess);
 
@@ -389,13 +406,7 @@ int event_agent_enable_all(struct ltt_ust_session *usess,
                goto error;
        }
 
-       /* Flag every event that they are now enabled. */
-       rcu_read_lock();
-       cds_lfht_for_each_entry(agt->events->ht, &iter.iter, aevent,
-                       node.node) {
-               aevent->enabled = 1;
-       }
-       rcu_read_unlock();
+       agent_enable_all(agt);
 
        ret = LTTNG_OK;
 
@@ -467,28 +478,17 @@ end:
        return ret;
 }
 
-/*
- * Enable a single agent event for a given UST session.
- *
- * Return LTTNG_OK on success or else a LTTNG_ERR* code.
- */
-int event_agent_enable(struct ltt_ust_session *usess,
-               struct agent *agt, struct lttng_event *event,
+static int agent_enable(struct agent *agt,
+               struct lttng_event *event,
                struct lttng_filter_bytecode *filter,
                char *filter_expression)
 {
        int ret, created = 0;
        struct agent_event *aevent;
 
-       assert(usess);
        assert(event);
        assert(agt);
 
-       DBG("Event agent enabling %s for session %" PRIu64 " with loglevel type %d "
-                       ", loglevel %d and filter \"%s\"", event->name,
-                       usess->id, event->loglevel_type, event->loglevel,
-                       filter_expression ? filter_expression : "NULL");
-
        aevent = agent_find_event(event->name, event->loglevel_type,
                        event->loglevel, filter_expression, agt);
        if (!aevent) {
@@ -542,6 +542,113 @@ end:
        return ret;
 }
 
+/*
+ * Enable a single agent event for a given UST session.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int event_agent_enable(struct ltt_ust_session *usess,
+               struct agent *agt,
+               struct lttng_event *event,
+               struct lttng_filter_bytecode *filter,
+               char *filter_expression)
+{
+       assert(usess);
+       assert(event);
+       assert(agt);
+
+       DBG("Event agent enabling %s for session %" PRIu64
+           " with loglevel type %d "
+           ", loglevel %d and filter \"%s\"",
+                       event->name, usess->id, event->loglevel_type,
+                       event->loglevel,
+                       filter_expression ? filter_expression : "NULL");
+
+       return agent_enable(agt, event, filter, filter_expression);
+}
+
+/*
+ * Enable a single agent event for a trigger.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int trigger_agent_enable(const struct lttng_trigger *trigger, struct agent *agt)
+{
+       int ret;
+       enum lttng_condition_status c_status;
+       enum lttng_domain_type d_type;
+       const struct lttng_condition *condition;
+       const struct lttng_event_rule *rule;
+       const char *filter_expression;
+       char *filter_expression_copy = NULL;
+       const struct lttng_filter_bytecode *filter_bytecode;
+       struct lttng_filter_bytecode *filter_bytecode_copy = NULL;
+       struct lttng_event *event = NULL;
+
+       assert(trigger);
+       assert(agt);
+
+       condition = lttng_trigger_get_const_condition(trigger);
+
+       assert(lttng_condition_get_type(condition) ==
+                       LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+       c_status = lttng_condition_event_rule_get_rule(condition, &rule);
+       assert(c_status == LTTNG_CONDITION_STATUS_OK);
+
+       assert(lttng_event_rule_get_type(rule) ==
+                       LTTNG_EVENT_RULE_TYPE_TRACEPOINT);
+
+       d_type = lttng_event_rule_get_domain_type(rule);
+       assert(d_type == agt->domain);
+
+       event = lttng_event_rule_generate_lttng_event(rule);
+       if (!event) {
+               ret = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       /* Get the internal filter_expression and bytecode */
+       filter_expression = lttng_event_rule_get_filter(rule);
+       if (filter_expression) {
+               filter_expression_copy = strdup(filter_expression);
+               if (!filter_expression_copy) {
+                       ret = LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+
+               /* Get the filter bytecode */
+               filter_bytecode = lttng_event_rule_get_filter_bytecode(rule);
+               if (filter_bytecode) {
+                       filter_bytecode_copy =
+                                       copy_filter_bytecode(filter_bytecode);
+                       if (!filter_bytecode_copy) {
+                               ret = LTTNG_ERR_NOMEM;
+                               goto end;
+                       }
+               }
+       }
+
+       DBG("Event agent enabling %s for trigger %" PRIu64
+           " with loglevel type %d "
+           ", loglevel %d and filter \"%s\"",
+                       event->name, lttng_trigger_get_key(trigger),
+                       event->loglevel_type, event->loglevel,
+                       filter_expression ? filter_expression : "NULL");
+
+       ret = agent_enable(agt, event, filter_bytecode_copy,
+                       filter_expression_copy);
+       /* Ownership was passed even in case of error */
+       filter_expression_copy = NULL;
+       filter_bytecode_copy = NULL;
+
+end:
+       free(filter_expression_copy);
+       free(filter_bytecode_copy);
+       free(event);
+       return ret;
+}
+
 /*
  * Return the default event name associated with the provided UST domain. Return
  * NULL on error.
@@ -567,6 +674,43 @@ const char *event_get_default_agent_ust_name(enum lttng_domain_type domain)
        return default_event_name;
 }
 
+static int trigger_agent_disable_one(const struct lttng_trigger *trigger,
+               struct agent *agt,
+               struct agent_event *aevent)
+
+{
+       int ret;
+
+       assert(agt);
+       assert(trigger);
+       assert(aevent);
+
+       /*
+        * Actual ust event un-registration happens on the trigger
+        * un-registration at that point.
+        */
+
+       DBG("Event agent disabling %s (loglevel type %d, loglevel value %d) for trigger %" PRIu64,
+                       aevent->name, aevent->loglevel_type,
+                       aevent->loglevel_value, lttng_trigger_get_key(trigger));
+
+       /* Already disabled? */
+       if (!aevent->enabled) {
+               goto end;
+       }
+
+       ret = agent_disable_event(aevent, agt->domain);
+       if (ret != LTTNG_OK) {
+               goto error;
+       }
+
+end:
+       return LTTNG_OK;
+
+error:
+       return ret;
+}
+
 /*
  * Disable a given agent event for a given UST session.
  *
@@ -659,6 +803,44 @@ error:
        return ret;
 }
 
+/*
+ * Disable agent event matching a given trigger.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int trigger_agent_disable(
+               const struct lttng_trigger *trigger, struct agent *agt)
+{
+       int ret = LTTNG_OK;
+       struct agent_event *aevent;
+
+       assert(trigger);
+       assert(agt);
+
+       DBG("Event agent disabling for trigger %" PRIu64,
+                       lttng_trigger_get_key(trigger));
+
+       rcu_read_lock();
+       aevent = agent_find_event_by_trigger(trigger, agt);
+
+       if (aevent == NULL) {
+               DBG2("Event agent NOT found by trigger %" PRIu64,
+                               lttng_trigger_get_key(trigger));
+               ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
+               goto end;
+       }
+
+       ret = trigger_agent_disable_one(trigger, agt, aevent);
+
+       if (ret != LTTNG_OK) {
+               goto end;
+       }
+
+end:
+       rcu_read_unlock();
+       return ret;
+}
+
 /*
  * Disable all agent events matching a given name for a given UST session.
  *
index 1c646db37b7d4d7956d1bc50303ed1032415ac3f..c7a849c0b3d46017d4660ed217a2fcd6e348eafb 100644 (file)
@@ -42,6 +42,11 @@ int event_agent_disable(struct ltt_ust_session *usess, struct agent *agt,
                const char *event_name);
 int event_agent_disable_all(struct ltt_ust_session *usess, struct agent *agt);
 
+int trigger_agent_enable(
+               const struct lttng_trigger *trigger, struct agent *agt);
+int trigger_agent_disable(
+               const struct lttng_trigger *trigger, struct agent *agt);
+
 const char *event_get_default_agent_ust_name(enum lttng_domain_type domain);
 
 #endif /* _LTT_EVENT_H */
index 20aa790a7540d4dcfd0adbb8897a7da81ff47582..6e111b63b2755fb72c63f6f022f7e72b2d36d37e 100644 (file)
@@ -20,8 +20,12 @@ long page_size;
 struct health_app *health_sessiond;
 
 struct notification_thread_handle *notification_thread_handle;
+pthread_mutex_t notification_trigger_tokens_ht_lock = PTHREAD_MUTEX_INITIALIZER;
 
 struct lttng_ht *agent_apps_ht_by_sock = NULL;
+struct lttng_ht *trigger_agents_ht_by_domain = NULL;
+
+struct lttng_ht *registered_ust_event_rule = NULL;
 
 struct lttng_kernel_tracer_version kernel_tracer_version;
 struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version;
index 7c9dbd0b3218d6c0f3cbc7c9f24d9060ed106390..b541822f87081ed47b8d7d551cabdb728d516992 100644 (file)
@@ -23,6 +23,7 @@ enum health_type_sessiond {
        HEALTH_SESSIOND_TYPE_NOTIFICATION       = 8,
        HEALTH_SESSIOND_TYPE_ROTATION           = 9,
        HEALTH_SESSIOND_TYPE_TIMER              = 10,
+       HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR    = 11,
 
        NR_HEALTH_SESSIOND_TYPES,
 };
index 4ee4bea64ff630abae9617624f86ce21cc87950d..646da3ba83affd3463a30dd7e2efb7a43b05535c 100644 (file)
 #include <sys/types.h>
 
 #include <common/common.h>
+#include <common/hashtable/utils.h>
 #include <common/trace-chunk.h>
 #include <common/kernel-ctl/kernel-ctl.h>
 #include <common/kernel-ctl/kernel-ioctl.h>
 #include <common/sessiond-comm/sessiond-comm.h>
 
+#include <lttng/userspace-probe.h>
+#include <lttng/userspace-probe-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
+
 #include "lttng-sessiond.h"
 #include "lttng-syscall.h"
 #include "consumer.h"
@@ -29,6 +38,7 @@
 #include "utils.h"
 #include "rotate.h"
 #include "modprobe.h"
+#include "notification-thread-commands.h"
 
 /*
  * Key used to reference a channel between the sessiond and the consumer. This
@@ -39,9 +49,10 @@ static uint64_t next_kernel_channel_key;
 static const char *module_proc_lttng = "/proc/lttng";
 
 static int kernel_tracer_fd = -1;
+static int kernel_tracer_trigger_group_fd = -1;
+static int kernel_tracer_trigger_group_notification_fd = -1;
+static struct ltt_kernel_token_event_rule_list kernel_tracer_token_list;
 
-#include <lttng/userspace-probe.h>
-#include <lttng/userspace-probe-internal.h>
 /*
  * Add context on a kernel channel.
  *
@@ -213,6 +224,44 @@ error:
        return -1;
 }
 
+/*
+ * Create a kernel channel, register it to the kernel tracer and add it to the
+ * kernel session.
+ */
+static
+int kernel_create_trigger_group(int *trigger_group_fd)
+{
+       int ret;
+       int local_fd = -1;
+
+       assert(trigger_group_fd);
+
+       /* Kernel tracer channel creation */
+       ret = kernctl_create_trigger_group(kernel_tracer_fd);
+       if (ret < 0) {
+               PERROR("ioctl kernel create trigger group");
+               ret = -1;
+               goto error;
+       }
+
+       /* Store locally */
+       local_fd = ret;
+
+       /* Prevent fd duplication after execlp() */
+       ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC);
+       if (ret < 0) {
+               PERROR("fcntl session fd");
+       }
+
+       DBG("Kernel trigger group created (fd: %d)",
+                       local_fd);
+       ret = 0;
+
+error:
+       *trigger_group_fd = local_fd;
+       return ret;
+}
+
 /*
  * Compute the offset of the instrumentation byte in the binary based on the
  * function probe location using the ELF lookup method.
@@ -224,7 +273,7 @@ error:
 static
 int extract_userspace_probe_offset_function_elf(
                const struct lttng_userspace_probe_location *probe_location,
-               struct ltt_kernel_session *session, uint64_t *offset)
+               uid_t uid, gid_t gid, uint64_t *offset)
 {
        int fd;
        int ret = 0;
@@ -261,8 +310,7 @@ int extract_userspace_probe_offset_function_elf(
                goto end;
        }
 
-       ret = run_as_extract_elf_symbol_offset(fd, symbol, session->uid,
-                       session->gid, offset);
+       ret = run_as_extract_elf_symbol_offset(fd, symbol, uid, gid, offset);
        if (ret < 0) {
                DBG("userspace probe offset calculation failed for "
                                "function %s", symbol);
@@ -286,7 +334,7 @@ end:
 static
 int extract_userspace_probe_offset_tracepoint_sdt(
                const struct lttng_userspace_probe_location *probe_location,
-               struct ltt_kernel_session *session, uint64_t **offsets,
+               uid_t uid, gid_t gid, uint64_t **offsets,
                uint32_t *offsets_count)
 {
        enum lttng_userspace_probe_location_lookup_method_type lookup_method_type;
@@ -332,7 +380,7 @@ int extract_userspace_probe_offset_tracepoint_sdt(
        }
 
        ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name,
-                       session->uid, session->gid, offsets, offsets_count);
+                       uid, gid, offsets, offsets_count);
        if (ret < 0) {
                DBG("userspace probe offset calculation failed for sdt "
                                "probe %s:%s", provider_name, probe_name);
@@ -353,29 +401,16 @@ end:
        return ret;
 }
 
-/*
- * Extract the offsets of the instrumentation point for the different lookup
- * methods.
- */
 static
-int userspace_probe_add_callsites(struct lttng_event *ev,
-                       struct ltt_kernel_session *session, int fd)
+int userspace_probe_add_callsite(
+               const struct lttng_userspace_probe_location *location,
+               uid_t uid, gid_t gid, int fd)
 {
        const struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
        enum lttng_userspace_probe_location_lookup_method_type type;
-       const struct lttng_userspace_probe_location *location = NULL;
        int ret;
 
-       assert(ev);
-       assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE);
-
-       location = lttng_event_get_userspace_probe_location(ev);
-       if (!location) {
-               ret = -1;
-               goto end;
-       }
-       lookup_method =
-                       lttng_userspace_probe_location_get_lookup_method(location);
+       lookup_method = lttng_userspace_probe_location_get_lookup_method(location);
        if (!lookup_method) {
                ret = -1;
                goto end;
@@ -388,7 +423,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev,
                struct lttng_kernel_event_callsite callsite;
                uint64_t offset;
 
-               ret = extract_userspace_probe_offset_function_elf(location, session, &offset);
+               ret = extract_userspace_probe_offset_function_elf(location,
+                               uid, gid, &offset);
                if (ret) {
                        ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
                        goto end;
@@ -397,8 +433,7 @@ int userspace_probe_add_callsites(struct lttng_event *ev,
                callsite.u.uprobe.offset = offset;
                ret = kernctl_add_callsite(fd, &callsite);
                if (ret) {
-                       WARN("Adding callsite to userspace probe "
-                                       "event %s failed.", ev->name);
+                       WARN("Adding callsite to ELF userspace probe failed.");
                        ret = LTTNG_ERR_KERN_ENABLE_FAIL;
                        goto end;
                }
@@ -415,8 +450,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev,
                 * This call allocates the offsets buffer. This buffer must be freed
                 * by the caller
                 */
-               ret = extract_userspace_probe_offset_tracepoint_sdt(location, session,
-                               &offsets, &offsets_count);
+               ret = extract_userspace_probe_offset_tracepoint_sdt(location,
+                               uid, gid, &offsets, &offsets_count);
                if (ret) {
                        ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
                        goto end;
@@ -425,8 +460,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev,
                        callsite.u.uprobe.offset = offsets[i];
                        ret = kernctl_add_callsite(fd, &callsite);
                        if (ret) {
-                               WARN("Adding callsite to userspace probe "
-                                               "event %s failed.", ev->name);
+                               WARN("Adding callsite to SDT userspace probe "
+                                       "failed.");
                                ret = LTTNG_ERR_KERN_ENABLE_FAIL;
                                free(offsets);
                                goto end;
@@ -443,6 +478,71 @@ end:
        return ret;
 }
 
+/*
+ * Extract the offsets of the instrumentation point for the different lookup
+ * methods.
+ */
+static
+int userspace_probe_event_add_callsites(struct lttng_event *ev,
+                       struct ltt_kernel_session *session, int fd)
+{
+       const struct lttng_userspace_probe_location *location = NULL;
+       int ret;
+
+       assert(ev);
+       assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE);
+
+       location = lttng_event_get_userspace_probe_location(ev);
+       if (!location) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = userspace_probe_add_callsite(location, session->uid, session->gid,
+               fd);
+       if (ret) {
+               WARN("Adding callsite to userspace probe event \"%s\" "
+                       "failed.", ev->name);
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Extract the offsets of the instrumentation point for the different lookup
+ * methods.
+ */
+static int userspace_probe_event_rule_add_callsites(
+               const struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds,
+               int fd)
+{
+       const struct lttng_userspace_probe_location *location = NULL;
+       enum lttng_event_rule_status status;
+       int ret;
+
+       assert(rule);
+       assert(creds);
+       assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE);
+
+       status = lttng_event_rule_uprobe_get_location(rule, &location);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = userspace_probe_add_callsite(location, creds->uid, creds->gid,
+               fd);
+       if (ret) {
+               WARN("Adding callsite to userspace probe object %d"
+                       "failed.", fd);
+       }
+
+end:
+       return ret;
+}
+
 /*
  * Create a kernel event, enable it to the kernel tracer and add it to the
  * channel event list of the kernel session.
@@ -512,7 +612,8 @@ int kernel_create_event(struct lttng_event *ev,
        }
 
        if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) {
-               ret = userspace_probe_add_callsites(ev, channel->session, event->fd);
+               ret = userspace_probe_event_add_callsites(ev, channel->session,
+                       event->fd);
                if (ret) {
                        goto add_callsite_error;
                }
@@ -669,6 +770,36 @@ error:
        return ret;
 }
 
+/*
+ * Disable a kernel trigger.
+ */
+int kernel_disable_token_event_rule(struct ltt_kernel_token_event_rule *event)
+{
+       int ret;
+
+       assert(event);
+
+       ret = kernctl_disable(event->fd);
+       if (ret < 0) {
+               switch (-ret) {
+               case EEXIST:
+                       ret = LTTNG_ERR_KERN_EVENT_EXIST;
+                       break;
+               default:
+                       PERROR("disable kernel event");
+                       break;
+               }
+               goto error;
+       }
+
+       event->enabled = 0;
+       DBG("Kernel trigger token %" PRIu64" disabled (fd: %d)", event->token, event->fd);
+
+       return 0;
+
+error:
+       return ret;
+}
 static struct lttng_tracker_list *get_id_tracker_list(
                struct ltt_kernel_session *session,
                enum lttng_tracker_type tracker_type)
@@ -1732,20 +1863,37 @@ int init_kernel_tracer(void)
        if (ret < 0) {
                goto error_modules;
        }
-
        if (ret < 1) {
                WARN("Kernel tracer does not support buffer monitoring. "
                        "The monitoring timer of channels in the kernel domain "
                        "will be set to 0 (disabled).");
        }
 
+       ret = kernel_create_trigger_group(&kernel_tracer_trigger_group_fd);
+       if (ret < 0) {
+               /* TODO: error handling if it is not supported etc. */
+               WARN("Failed trigger group creation");
+               kernel_tracer_trigger_group_fd = -1;
+               /* This is not fatal */
+       } else {
+               ret = kernel_create_trigger_group_notification_fd(&kernel_tracer_trigger_group_notification_fd);
+               if (ret < 0) {
+                       goto error_modules;
+               }
+       }
+
+       CDS_INIT_LIST_HEAD(&kernel_tracer_token_list.head);
+
        DBG("Kernel tracer fd %d", kernel_tracer_fd);
+       DBG("Kernel tracer trigger group fd %d", kernel_tracer_trigger_group_fd);
+       DBG("Kernel tracer trigger group notificationi fd %d", kernel_tracer_trigger_group_notification_fd);
 
        ret = syscall_init_table(kernel_tracer_fd);
        if (ret < 0) {
                ERR("Unable to populate syscall table. Syscall tracing won't "
                        "work for this session daemon.");
        }
+
        return 0;
 
 error_version:
@@ -1781,6 +1929,31 @@ void cleanup_kernel_tracer(void)
 {
        int ret;
 
+       struct ltt_kernel_token_event_rule *rule, *rtmp;
+        cds_list_for_each_entry_safe(rule, rtmp, &kernel_tracer_token_list.head, list) {
+               kernel_disable_token_event_rule(rule);
+               trace_kernel_destroy_token_event_rule(rule);
+       }
+
+       DBG2("Closing kernel trigger group notification fd");
+       if (kernel_tracer_trigger_group_notification_fd >= 0) {
+               ret = close(kernel_tracer_trigger_group_notification_fd);
+               if (ret) {
+                       PERROR("close");
+               }
+               kernel_tracer_trigger_group_notification_fd = -1;
+       }
+
+       /* TODO: do we iterate over the list to remove all token? */
+       DBG2("Closing kernel trigger group fd");
+       if (kernel_tracer_trigger_group_fd >= 0) {
+               ret = close(kernel_tracer_trigger_group_fd);
+               if (ret) {
+                       PERROR("close");
+               }
+               kernel_tracer_trigger_group_fd = -1;
+       }
+
        DBG2("Closing kernel fd");
        if (kernel_tracer_fd >= 0) {
                ret = close(kernel_tracer_fd);
@@ -1789,6 +1962,8 @@ void cleanup_kernel_tracer(void)
                }
                kernel_tracer_fd = -1;
        }
+
+       
        DBG("Unloading kernel modules");
        modprobe_remove_lttng_all();
        free(syscall_table);
@@ -1879,3 +2054,274 @@ end:
        rcu_read_unlock();
        return status;
 }
+
+enum lttng_error_code kernel_create_trigger_group_notification_fd(
+               int *trigger_group_notification_fd)
+{
+       enum lttng_error_code ret = LTTNG_OK;
+       int local_fd = -1;
+
+       assert(trigger_group_notification_fd);
+
+       ret = kernctl_create_trigger_group_notification_fd(kernel_tracer_trigger_group_fd);
+       if (ret < 0) {
+               PERROR("ioctl kernel create trigger group");
+               ret = -1;
+               goto error;
+       }
+
+       /* Store locally */
+       local_fd = ret;
+
+       /* Prevent fd duplication after execlp() */
+       ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC);
+       if (ret < 0) {
+               PERROR("fcntl session fd");
+       }
+
+       DBG("Kernel trigger group notification created (fd: %d)",
+                       local_fd);
+       ret = 0;
+
+error:
+       *trigger_group_notification_fd = local_fd;
+       return ret;
+}
+
+enum lttng_error_code kernel_destroy_trigger_group_notification_fd(
+               int trigger_group_notification_fd)
+{
+       enum lttng_error_code ret = LTTNG_OK;
+       DBG("Closing trigger group notification fd %d", trigger_group_notification_fd);
+       if (trigger_group_notification_fd >= 0) {
+               ret = close(trigger_group_notification_fd);
+               if (ret) {
+                       PERROR("close");
+               }
+       }
+       return ret;
+}
+
+static int kernel_create_token_event_rule(struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds, uint64_t token)
+{
+       int err, fd;
+       enum lttng_error_code ret;
+       struct ltt_kernel_token_event_rule *event;
+       struct lttng_kernel_trigger trigger;
+
+       assert(rule);
+
+       ret = trace_kernel_create_token_event_rule(rule, token, &event);
+       if (ret != LTTNG_OK) {
+               goto error;
+       }
+       
+       trace_kernel_init_trigger_from_event_rule(event->event_rule, &trigger);
+       trigger.id = event->token;
+
+       fd = kernctl_create_trigger(kernel_tracer_trigger_group_fd, &trigger);
+       if (fd < 0) {
+               switch (-fd) {
+               case EEXIST:
+                       ret = LTTNG_ERR_KERN_EVENT_EXIST;
+                       break;
+               case ENOSYS:
+                       WARN("Trigger type not implemented");
+                       ret = LTTNG_ERR_KERN_EVENT_ENOSYS;
+                       break;
+               case ENOENT:
+                       WARN("Event %s not found!", trigger.name);
+                       ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+                       break;
+               default:
+                       ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+                       PERROR("create trigger ioctl");
+               }
+               goto free_event;
+       }
+
+       event->fd = fd;
+       /* Prevent fd duplication after execlp() */
+       err = fcntl(event->fd, F_SETFD, FD_CLOEXEC);
+       if (err < 0) {
+               PERROR("fcntl session fd");
+       }
+
+       if (event->filter) {
+               err = kernctl_filter(event->fd, event->filter);
+               if (err < 0) {
+                       switch (-err) {
+                       case ENOMEM:
+                               ret = LTTNG_ERR_FILTER_NOMEM;
+                               break;
+                       default:
+                               ret = LTTNG_ERR_FILTER_INVAL;
+                               break;
+                       }
+                       goto filter_error;
+               }
+       }
+
+       if (lttng_event_rule_get_type(event->event_rule) ==
+                       LTTNG_EVENT_RULE_TYPE_UPROBE) {
+               ret = userspace_probe_event_rule_add_callsites(
+                               rule, creds, event->fd);
+               if (ret) {
+                       goto add_callsite_error;
+               }
+       }
+
+       err = kernctl_enable(event->fd);
+       if (err < 0) {
+               switch (-err) {
+               case EEXIST:
+                       ret = LTTNG_ERR_KERN_EVENT_EXIST;
+                       break;
+               default:
+                       PERROR("enable kernel trigger");
+                       ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+                       break;
+               }
+               goto enable_error;
+       }
+
+       /* Add event to event list */
+       cds_list_add(&event->list, &kernel_tracer_token_list.head);
+
+       DBG("Trigger %s created (fd: %d)", trigger.name, event->fd);
+
+       return 0;
+
+add_callsite_error:
+enable_error:
+filter_error:
+       {
+               int closeret;
+
+               closeret = close(event->fd);
+               if (closeret) {
+                       PERROR("close event fd");
+               }
+       }
+free_event:
+       free(event);
+error:
+       return ret;
+}
+
+enum lttng_error_code kernel_update_tokens(void)
+{
+       enum lttng_error_code ret = LTTNG_OK;
+       enum lttng_trigger_status t_status;
+       struct ltt_kernel_token_event_rule *token_event_rule_element;
+       struct lttng_triggers *triggers;
+       unsigned int count;
+       
+       /* TODO error handling */
+
+       /* Get list of token trigger from the notification thread here */
+       rcu_read_lock();
+       pthread_mutex_lock(&notification_trigger_tokens_ht_lock);
+       ret = notification_thread_command_get_tokens(notification_thread_handle, &triggers);
+       if (ret != LTTNG_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       assert(triggers);
+
+       t_status = lttng_triggers_get_count(triggers, &count);
+       if (t_status != LTTNG_TRIGGER_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       for (unsigned int i = 0; i < count; i++) {
+               struct lttng_condition *condition;
+               struct lttng_event_rule *event_rule;
+               struct lttng_trigger *trigger;
+               struct ltt_kernel_token_event_rule *k_token;
+               const struct lttng_credentials *creds;
+               uint64_t token;
+
+               trigger = lttng_triggers_get_pointer_of_index(triggers, i);
+               assert(trigger);
+
+               /* TODO: error checking and type checking */
+               token = lttng_trigger_get_key(trigger);
+               condition = lttng_trigger_get_condition(trigger);
+               (void) lttng_condition_event_rule_get_rule_no_const(condition, &event_rule);
+
+               if (lttng_event_rule_get_domain_type(event_rule) != LTTNG_DOMAIN_KERNEL) {
+                       /* Skip ust related trigger */
+                       continue;
+               }
+
+               creds = lttng_trigger_get_credentials(trigger);
+               /* Iterate over all known token trigger */
+               k_token = trace_kernel_find_trigger_by_token(&kernel_tracer_token_list, token);
+               if (!k_token) {
+                       ret = kernel_create_token_event_rule(event_rule, creds, token);
+                       if (ret < 0) {
+                               goto end;
+                       }
+               }
+       }
+
+       /* Remove all unknown trigger from the app
+        * TODO find a way better way then this, do it on the unregister command
+        * and be specific on the token to remove instead of going over all
+        * trigger known to the app. This is sub optimal.
+        */
+       cds_list_for_each_entry (token_event_rule_element, &kernel_tracer_token_list.head,
+                       list) {
+               uint64_t token;
+               bool found = false;
+
+               token = token_event_rule_element->token;
+
+       /*
+                * Check if the app event trigger still exists on the
+                * notification side.
+                * TODO: might want to change the backing data struct of the
+                * lttng_triggers object to allow quick lookup?
+                * For kernel mostly all of this can be removed once we delete
+                * on a per trigger basis.
+                */
+
+               for (unsigned int i = 0; i < count; i++) {
+                       struct lttng_trigger *trigger;
+                       uint64_t inner_token;
+
+                       trigger = lttng_triggers_get_pointer_of_index(
+                                       triggers, i);
+                       assert(trigger);
+
+                       inner_token = lttng_trigger_get_key(trigger);
+
+                       if (inner_token == token) {
+                               found = true;
+                               break;
+                       }
+               }
+
+               if (found) {
+                       /* Still valid */
+                       continue;
+               }
+
+               kernel_disable_token_event_rule(token_event_rule_element);
+               trace_kernel_destroy_token_event_rule(token_event_rule_element);
+       }
+end:
+       rcu_read_unlock();
+       pthread_mutex_unlock(&notification_trigger_tokens_ht_lock);
+       return ret;
+
+}
+
+int kernel_get_notification_fd(void)
+{
+       return kernel_tracer_trigger_group_notification_fd;
+}
index 4f3bedea5c392d09f94e9fbdd43ab60583e4c9e1..d22a5b7a07375477869df702a4dad4bf860fae43 100644 (file)
@@ -74,4 +74,11 @@ bool kernel_tracer_is_initialized(void);
 enum lttng_error_code kernel_create_channel_subdirectories(
                const struct ltt_kernel_session *ksess);
 
+enum lttng_error_code kernel_create_trigger_group_notification_fd(
+               int *trigger_group_notification_fd);
+enum lttng_error_code kernel_destroy_trigger_group_notification_fd(
+               int trigger_group_notification_fd);
+enum lttng_error_code kernel_update_tokens(void);
+int kernel_get_notification_fd(void);
+
 #endif /* _LTT_KERNEL_CTL_H */
index 277fc23e30f22ff4be3d9c93f7b66bd343721be6..ee8ea2400359ea8791fe3fa9105abfdcf7f04326 100644 (file)
@@ -70,6 +70,7 @@ extern struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version;
 
 /* Notification thread handle. */
 extern struct notification_thread_handle *notification_thread_handle;
+extern pthread_mutex_t notification_trigger_tokens_ht_lock;
 
 /*
  * This contains extra data needed for processing a command received by the
index 55e0ad3d7a28623cb6f1bdad7ccc04e289f410f8..965625c9bb8ad7f70c74a61422b8e299ce164648 100644 (file)
@@ -314,6 +314,9 @@ static void sessiond_cleanup(void)
 
        pthread_mutex_destroy(&session_list->lock);
 
+       DBG("Cleaning up all trigger agents");
+       trigger_agent_ht_clean();
+
        DBG("Cleaning up all agent apps");
        agent_app_ht_clean();
        DBG("Closing all UST sockets");
@@ -1302,6 +1305,7 @@ int main(int argc, char **argv)
        struct lttng_thread *notification_thread = NULL;
        struct lttng_thread *register_apps_thread = NULL;
 
+       logger_set_thread_name("Main", false);
        init_kernel_workarounds();
 
        rcu_register_thread();
@@ -1538,6 +1542,11 @@ int main(int argc, char **argv)
                goto stop_threads;
        }
 
+       if (trigger_agent_ht_alloc()) {
+               ERR("Failed to allocate trigger agent hash table");
+               retval = -1;
+               goto stop_threads;
+       }
        /*
         * These actions must be executed as root. We do that *after* setting up
         * the sockets path because we MUST make the check for another daemon using
@@ -1635,7 +1644,8 @@ int main(int argc, char **argv)
        notification_thread_handle = notification_thread_handle_create(
                        ust32_channel_monitor_pipe,
                        ust64_channel_monitor_pipe,
-                       kernel_channel_monitor_pipe);
+                       kernel_channel_monitor_pipe,
+                       kernel_get_notification_fd());
        if (!notification_thread_handle) {
                retval = -1;
                ERR("Failed to create notification thread shared data");
index 66e80e75b26dbc1b5ae8bfa4e122c37136ceadfa..7bee25f887cf95b08bb5f8edfa3c7bd69d991e9d 100644 (file)
 #define LTTNG_MOD_OPTIONAL     0
 
 /* LTTng kernel tracer mandatory core modules list */
+/* TODO: the new trigger client might not be present in previous lttng-modules
+ * should it be optional?
+ * Can we reuse this to also know of the trigger feature is supported?
+ */
 struct kern_modules_param kern_modules_control_core[] = {
-       { "lttng-ring-buffer-client-discard" },
-       { "lttng-ring-buffer-client-overwrite" },
-       { "lttng-ring-buffer-metadata-client" },
-       { "lttng-ring-buffer-client-mmap-discard" },
-       { "lttng-ring-buffer-client-mmap-overwrite" },
-       { "lttng-ring-buffer-metadata-mmap-client" },
+       { (char *) "lttng-ring-buffer-client-discard" },
+       { (char *) "lttng-ring-buffer-client-overwrite" },
+       { (char *) "lttng-ring-buffer-metadata-client" },
+       { (char *) "lttng-ring-buffer-client-mmap-discard" },
+       { (char *) "lttng-ring-buffer-client-mmap-overwrite" },
+       { (char *) "lttng-ring-buffer-metadata-mmap-client" },
+       { (char *) "lttng-ring-buffer-trigger-client" },
 };
 
 /* LTTng kernel tracer probe modules list */
 struct kern_modules_param kern_modules_probes_default[] = {
-       { "lttng-probe-asoc" },
-       { "lttng-probe-block" },
-       { "lttng-probe-btrfs" },
-       { "lttng-probe-compaction" },
-       { "lttng-probe-ext3" },
-       { "lttng-probe-ext4" },
-       { "lttng-probe-gpio" },
-       { "lttng-probe-i2c" },
-       { "lttng-probe-irq" },
-       { "lttng-probe-jbd" },
-       { "lttng-probe-jbd2" },
-       { "lttng-probe-kmem" },
-       { "lttng-probe-kvm" },
-       { "lttng-probe-kvm-x86" },
-       { "lttng-probe-kvm-x86-mmu" },
-       { "lttng-probe-lock" },
-       { "lttng-probe-module" },
-       { "lttng-probe-napi" },
-       { "lttng-probe-net" },
-       { "lttng-probe-power" },
-       { "lttng-probe-preemptirq" },
-       { "lttng-probe-printk" },
-       { "lttng-probe-random" },
-       { "lttng-probe-rcu" },
-       { "lttng-probe-regmap" },
-       { "lttng-probe-regulator" },
-       { "lttng-probe-rpm" },
-       { "lttng-probe-sched" },
-       { "lttng-probe-scsi" },
-       { "lttng-probe-signal" },
-       { "lttng-probe-skb" },
-       { "lttng-probe-sock" },
-       { "lttng-probe-statedump" },
-       { "lttng-probe-sunrpc" },
-       { "lttng-probe-timer" },
-       { "lttng-probe-udp" },
-       { "lttng-probe-vmscan" },
-       { "lttng-probe-v4l2" },
-       { "lttng-probe-workqueue" },
-       { "lttng-probe-writeback" },
-       { "lttng-probe-x86-irq-vectors" },
-       { "lttng-probe-x86-exceptions" },
+       { (char *) "lttng-probe-asoc" },
+       { (char *) "lttng-probe-block" },
+       { (char *) "lttng-probe-btrfs" },
+       { (char *) "lttng-probe-compaction" },
+       { (char *) "lttng-probe-ext3" },
+       { (char *) "lttng-probe-ext4" },
+       { (char *) "lttng-probe-gpio" },
+       { (char *) "lttng-probe-i2c" },
+       { (char *) "lttng-probe-irq" },
+       { (char *) "lttng-probe-jbd" },
+       { (char *) "lttng-probe-jbd2" },
+       { (char *) "lttng-probe-kmem" },
+       { (char *) "lttng-probe-kvm" },
+       { (char *) "lttng-probe-kvm-x86" },
+       { (char *) "lttng-probe-kvm-x86-mmu" },
+       { (char *) "lttng-probe-lock" },
+       { (char *) "lttng-probe-module" },
+       { (char *) "lttng-probe-napi" },
+       { (char *) "lttng-probe-net" },
+       { (char *) "lttng-probe-power" },
+       { (char *) "lttng-probe-preemptirq" },
+       { (char *) "lttng-probe-printk" },
+       { (char *) "lttng-probe-random" },
+       { (char *) "lttng-probe-rcu" },
+       { (char *) "lttng-probe-regmap" },
+       { (char *) "lttng-probe-regulator" },
+       { (char *) "lttng-probe-rpm" },
+       { (char *) "lttng-probe-sched" },
+       { (char *) "lttng-probe-scsi" },
+       { (char *) "lttng-probe-signal" },
+       { (char *) "lttng-probe-skb" },
+       { (char *) "lttng-probe-sock" },
+       { (char *) "lttng-probe-statedump" },
+       { (char *) "lttng-probe-sunrpc" },
+       { (char *) "lttng-probe-timer" },
+       { (char *) "lttng-probe-udp" },
+       { (char *) "lttng-probe-vmscan" },
+       { (char *) "lttng-probe-v4l2" },
+       { (char *) "lttng-probe-workqueue" },
+       { (char *) "lttng-probe-writeback" },
+       { (char *) "lttng-probe-x86-irq-vectors" },
+       { (char *) "lttng-probe-x86-exceptions" },
 };
 
 /* dynamic probe modules list */
index 4474d1978ed524f29866248910c6b697cc494282..9d346f947b9395cd33e9973d081a585712b2a3e0 100644 (file)
@@ -17,7 +17,6 @@
 static
 void init_notification_thread_command(struct notification_thread_command *cmd)
 {
-       memset(cmd, 0, sizeof(*cmd));
        CDS_INIT_LIST_HEAD(&cmd->cmd_list_node);
        lttng_waiter_init(&cmd->reply_waiter);
 }
@@ -54,13 +53,69 @@ error_unlock_queue:
        return -1;
 }
 
+static
+struct notification_thread_command *notification_thread_command_copy(
+       const struct notification_thread_command *original_cmd)
+{
+       struct notification_thread_command *new_cmd;
+
+       new_cmd = zmalloc(sizeof(*new_cmd));
+       if (!new_cmd) {
+               goto end;
+       }
+
+       *new_cmd = *original_cmd;
+       init_notification_thread_command(new_cmd);
+end:
+       return new_cmd;
+}
+
+static
+int run_command_no_wait(struct notification_thread_handle *handle,
+               const struct notification_thread_command *in_cmd)
+{
+       int ret;
+       uint64_t notification_counter = 1;
+       struct notification_thread_command *new_cmd =
+                       notification_thread_command_copy(in_cmd);
+
+       if (!new_cmd) {
+               goto error;
+       }
+       new_cmd->is_async = true;
+
+       pthread_mutex_lock(&handle->cmd_queue.lock);
+       /* Add to queue. */
+       cds_list_add_tail(&new_cmd->cmd_list_node,
+                       &handle->cmd_queue.list);
+       /* Wake-up thread. */
+       ret = lttng_write(lttng_pipe_get_writefd(handle->cmd_queue.event_pipe),
+                       &notification_counter, sizeof(notification_counter));
+       if (ret != sizeof(notification_counter)) {
+               PERROR("write to notification thread's queue event fd");
+               /*
+                * Remove the command from the list so the notification
+                * thread does not process it.
+                */
+               cds_list_del(&new_cmd->cmd_list_node);
+               goto error_unlock_queue;
+       }
+       pthread_mutex_unlock(&handle->cmd_queue.lock);
+       return 0;
+error_unlock_queue:
+       free(new_cmd);
+       pthread_mutex_unlock(&handle->cmd_queue.lock);
+error:
+       return -1;
+}
+
 enum lttng_error_code notification_thread_command_register_trigger(
                struct notification_thread_handle *handle,
                struct lttng_trigger *trigger)
 {
        int ret;
        enum lttng_error_code ret_code;
-       struct notification_thread_command cmd;
+       struct notification_thread_command cmd = {};
 
        init_notification_thread_command(&cmd);
 
@@ -83,7 +138,7 @@ enum lttng_error_code notification_thread_command_unregister_trigger(
 {
        int ret;
        enum lttng_error_code ret_code;
-       struct notification_thread_command cmd;
+       struct notification_thread_command cmd = {};
 
        init_notification_thread_command(&cmd);
 
@@ -108,7 +163,7 @@ enum lttng_error_code notification_thread_command_add_channel(
 {
        int ret;
        enum lttng_error_code ret_code;
-       struct notification_thread_command cmd;
+       struct notification_thread_command cmd = {};
 
        init_notification_thread_command(&cmd);
 
@@ -137,7 +192,7 @@ enum lttng_error_code notification_thread_command_remove_channel(
 {
        int ret;
        enum lttng_error_code ret_code;
-       struct notification_thread_command cmd;
+       struct notification_thread_command cmd = {};
 
        init_notification_thread_command(&cmd);
 
@@ -162,7 +217,7 @@ enum lttng_error_code notification_thread_command_session_rotation_ongoing(
 {
        int ret;
        enum lttng_error_code ret_code;
-       struct notification_thread_command cmd;
+       struct notification_thread_command cmd = {};
 
        init_notification_thread_command(&cmd);
 
@@ -191,7 +246,7 @@ enum lttng_error_code notification_thread_command_session_rotation_completed(
 {
        int ret;
        enum lttng_error_code ret_code;
-       struct notification_thread_command cmd;
+       struct notification_thread_command cmd = {};
 
        init_notification_thread_command(&cmd);
 
@@ -213,11 +268,115 @@ end:
        return ret_code;
 }
 
+enum lttng_error_code notification_thread_command_add_application(
+               struct notification_thread_handle *handle,
+               struct lttng_pipe *pipe)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct notification_thread_command cmd = {};
+
+       init_notification_thread_command(&cmd);
+
+       cmd.type = NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION;
+       cmd.parameters.application.read_side_trigger_event_application_pipe = lttng_pipe_get_readfd(pipe);
+
+       ret = run_command_wait(handle, &cmd);
+       if (ret) {
+               ret_code = LTTNG_ERR_UNK;
+               goto end;
+       }
+       ret_code = cmd.reply_code;
+end:
+       return ret_code;
+}
+
+enum lttng_error_code notification_thread_command_remove_application(
+               struct notification_thread_handle *handle,
+               struct lttng_pipe *pipe)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct notification_thread_command cmd = {};
+
+       init_notification_thread_command(&cmd);
+
+       cmd.type = NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION;
+       cmd.parameters.application.read_side_trigger_event_application_pipe = lttng_pipe_get_readfd(pipe);
+
+       ret = run_command_wait(handle, &cmd);
+       if (ret) {
+               ret_code = LTTNG_ERR_UNK;
+               goto end;
+       }
+       ret_code = cmd.reply_code;
+end:
+       return ret_code;
+}
+
+enum lttng_error_code notification_thread_command_get_tokens(
+               struct notification_thread_handle *handle,
+               struct lttng_triggers **tokens_triggers)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct notification_thread_command cmd = {};
+
+       assert(handle);
+       assert(tokens_triggers);
+
+       init_notification_thread_command(&cmd);
+
+       cmd.type = NOTIFICATION_COMMAND_TYPE_GET_TOKENS;
+
+       ret = run_command_wait(handle, &cmd);
+       if (ret) {
+               ret_code = LTTNG_ERR_UNK;
+               goto end;
+       }
+       ret_code = cmd.reply_code;
+       *tokens_triggers = cmd.reply.get_tokens.triggers;
+
+end:
+       return ret_code;
+}
+
+enum lttng_error_code notification_thread_command_list_triggers(
+               struct notification_thread_handle *handle,
+               uid_t uid,
+               gid_t gid,
+               struct lttng_triggers **triggers)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct notification_thread_command cmd = {};
+
+       assert(handle);
+       assert(triggers);
+
+       init_notification_thread_command(&cmd);
+
+       cmd.type = NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS;
+       cmd.parameters.list_triggers.uid = uid;
+       cmd.parameters.list_triggers.gid = gid;
+
+       ret = run_command_wait(handle, &cmd);
+       if (ret) {
+               ret_code = LTTNG_ERR_UNK;
+               goto end;
+       }
+       ret_code = cmd.reply_code;
+       *triggers = cmd.reply.list_triggers.triggers;
+
+end:
+       return ret_code;
+}
+
 void notification_thread_command_quit(
                struct notification_thread_handle *handle)
 {
        int ret;
-       struct notification_thread_command cmd;
+       struct notification_thread_command cmd = {};
 
        init_notification_thread_command(&cmd);
 
index a90d1ac2b2f662697fd54412596cba2e06689b9d..f0e857fae3f7d785bec815d26347154d971977a3 100644 (file)
@@ -15,6 +15,7 @@
 #include "notification-thread-internal.h"
 #include "notification-thread-events.h"
 #include <common/waiter.h>
+#include <stdbool.h>
 
 struct notification_thread_data;
 struct lttng_trigger;
@@ -26,6 +27,10 @@ enum notification_thread_command_type {
        NOTIFICATION_COMMAND_TYPE_REMOVE_CHANNEL,
        NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING,
        NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_COMPLETED,
+       NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION,
+       NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION,
+       NOTIFICATION_COMMAND_TYPE_GET_TOKENS,
+       NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS,
        NOTIFICATION_COMMAND_TYPE_QUIT,
 };
 
@@ -62,11 +67,32 @@ struct notification_thread_command {
                        uint64_t trace_archive_chunk_id;
                        struct lttng_trace_archive_location *location;
                } session_rotation;
+               /* Add/Remove application */
+               struct {
+                       int read_side_trigger_event_application_pipe;
+               } application;
+               /* List triggers */
+               struct {
+                       /* Credential */
+                       uid_t uid;
+                       gid_t gid;
+               } list_triggers;
+
        } parameters;
 
+       union {
+               struct {
+                       struct lttng_triggers *triggers;
+               } get_tokens;
+               struct {
+                       struct lttng_triggers *triggers;
+               } list_triggers;
+       } reply;
+
        /* lttng_waiter on which to wait for command reply (optional). */
        struct lttng_waiter reply_waiter;
        enum lttng_error_code reply_code;
+       bool is_async;
 };
 
 enum lttng_error_code notification_thread_command_register_trigger(
@@ -99,6 +125,28 @@ enum lttng_error_code notification_thread_command_session_rotation_completed(
                uint64_t trace_archive_chunk_id,
                struct lttng_trace_archive_location *location);
 
+enum lttng_error_code notification_thread_command_add_application(
+               struct notification_thread_handle *handle,
+               struct lttng_pipe *trigger_event_application_pipe);
+
+enum lttng_error_code notification_thread_command_remove_application(
+               struct notification_thread_handle *handle,
+               struct lttng_pipe *trigger_event_application_pipe);
+
+/* Must hold the notification_trigger_tokens_ht_lock to protect against
+ * insertion removal of triggers TODO: is it the case even with refcounting? */
+/* todo find a better way....*/
+enum lttng_error_code notification_thread_command_get_tokens(
+               struct notification_thread_handle *handle,
+               struct lttng_triggers **triggers);
+
+/* TODO: for now we borrow with no refcount the trigger. THIS IS DANGEROUS */
+enum lttng_error_code notification_thread_command_list_triggers(
+               struct notification_thread_handle *handle,
+               uid_t uid,
+               gid_t gid,
+               struct lttng_triggers **triggers);
+
 void notification_thread_command_quit(
                struct notification_thread_handle *handle);
 
index 73ec72f89c308b86ce2c95da5c364738d5b42680..e3c35f3a5e9e4f7cbbabfe48b923ed0556a05b9d 100644 (file)
 #include <common/macros.h>
 #include <lttng/condition/condition.h>
 #include <lttng/action/action-internal.h>
+#include <lttng/action/group-internal.h>
 #include <lttng/notification/notification-internal.h>
 #include <lttng/condition/condition-internal.h>
 #include <lttng/condition/buffer-usage-internal.h>
 #include <lttng/condition/session-consumed-size-internal.h>
 #include <lttng/condition/session-rotation-internal.h>
+#include <lttng/condition/event-rule-internal.h>
 #include <lttng/notification/channel-internal.h>
+#include <lttng/trigger/trigger-internal.h>
 
 #include <time.h>
 #include <unistd.h>
@@ -50,7 +53,7 @@ enum lttng_object_type {
 
 struct lttng_trigger_list_element {
        /* No ownership of the trigger object is assumed. */
-       const struct lttng_trigger *trigger;
+       struct lttng_trigger *trigger;
        struct cds_list_head node;
 };
 
@@ -108,6 +111,7 @@ struct lttng_session_trigger_list {
 struct lttng_trigger_ht_element {
        struct lttng_trigger *trigger;
        struct cds_lfht_node node;
+       struct cds_lfht_node node_by_name;
        /* call_rcu delayed reclaim. */
        struct rcu_head rcu_node;
 };
@@ -117,85 +121,17 @@ struct lttng_condition_list_element {
        struct cds_list_head node;
 };
 
-struct notification_client_list_element {
-       struct notification_client *client;
-       struct cds_list_head node;
-};
-
-struct notification_client_list {
-       const struct lttng_trigger *trigger;
-       struct cds_list_head list;
-       struct cds_lfht_node notification_trigger_ht_node;
-       /* call_rcu delayed reclaim. */
-       struct rcu_head rcu_node;
-};
-
-struct notification_client {
-       int socket;
-       /* Client protocol version. */
-       uint8_t major, minor;
-       uid_t uid;
-       gid_t gid;
-       /*
-        * Indicates if the credentials and versions of the client have been
-        * checked.
-        */
-       bool validated;
-       /*
-        * Conditions to which the client's notification channel is subscribed.
-        * List of struct lttng_condition_list_node. The condition member is
-        * owned by the client.
-        */
-       struct cds_list_head condition_list;
-       struct cds_lfht_node client_socket_ht_node;
-       struct {
-               struct {
-                       /*
-                        * During the reception of a message, the reception
-                        * buffers' "size" is set to contain the current
-                        * message's complete payload.
-                        */
-                       struct lttng_dynamic_buffer buffer;
-                       /* Bytes left to receive for the current message. */
-                       size_t bytes_to_receive;
-                       /* Type of the message being received. */
-                       enum lttng_notification_channel_message_type msg_type;
-                       /*
-                        * Indicates whether or not credentials are expected
-                        * from the client.
-                        */
-                       bool expect_creds;
-                       /*
-                        * Indicates whether or not credentials were received
-                        * from the client.
-                        */
-                       bool creds_received;
-                       /* Only used during credentials reception. */
-                       lttng_sock_cred creds;
-               } inbound;
-               struct {
-                       /*
-                        * Indicates whether or not a notification addressed to
-                        * this client was dropped because a command reply was
-                        * already buffered.
-                        *
-                        * A notification is dropped whenever the buffer is not
-                        * empty.
-                        */
-                       bool dropped_notification;
-                       /*
-                        * Indicates whether or not a command reply is already
-                        * buffered. In this case, it means that the client is
-                        * not consuming command replies before emitting a new
-                        * one. This could be caused by a protocol error or a
-                        * misbehaving/malicious client.
-                        */
-                       bool queued_command_reply;
-                       struct lttng_dynamic_buffer buffer;
-               } outbound;
-       } communication;
-       /* call_rcu delayed reclaim. */
-       struct rcu_head rcu_node;
+/*
+ * Facilities to carry the different notifications type in the action processing
+ * code path.
+ */
+struct lttng_trigger_notification {
+       union {
+               struct lttng_ust_trigger_notification *ust;
+               uint64_t *kernel;
+       } u;
+       uint64_t id;
+       enum lttng_domain_type type;
 };
 
 struct channel_state_sample {
@@ -258,20 +194,29 @@ void lttng_session_trigger_list_destroy(
                struct lttng_session_trigger_list *list);
 static
 int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list,
-               const struct lttng_trigger *trigger);
+               struct lttng_trigger *trigger);
 
 
 static
-int match_client(struct cds_lfht_node *node, const void *key)
+int match_client_socket(struct cds_lfht_node *node, const void *key)
 {
        /* This double-cast is intended to supress pointer-to-cast warning. */
-       int socket = (int) (intptr_t) key;
-       struct notification_client *client;
+       const int socket = (int) (intptr_t) key;
+       const struct notification_client *client = caa_container_of(node,
+                       struct notification_client, client_socket_ht_node);
 
-       client = caa_container_of(node, struct notification_client,
-                       client_socket_ht_node);
+       return client->socket == socket;
+}
 
-       return !!(client->socket == socket);
+static
+int match_client_id(struct cds_lfht_node *node, const void *key)
+{
+       /* This double-cast is intended to supress pointer-to-cast warning. */
+       const notification_client_id id = *((notification_client_id *) key);
+       const struct notification_client *client = caa_container_of(
+                       node, struct notification_client, client_id_ht_node);
+
+       return client->id == id;
 }
 
 static
@@ -326,18 +271,42 @@ int match_channel_info(struct cds_lfht_node *node, const void *key)
 }
 
 static
-int match_condition(struct cds_lfht_node *node, const void *key)
+int match_trigger(struct cds_lfht_node *node, const void *key)
 {
-       struct lttng_condition *condition_key = (struct lttng_condition *) key;
-       struct lttng_trigger_ht_element *trigger;
-       struct lttng_condition *condition;
+       bool match = false;
+       struct lttng_trigger *trigger_key = (struct lttng_trigger *) key;
+       struct lttng_trigger_ht_element *trigger_ht_element;
+       const struct lttng_credentials *creds_key;
+       const struct lttng_credentials *creds_node;
 
-       trigger = caa_container_of(node, struct lttng_trigger_ht_element,
+       trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element,
                        node);
-       condition = lttng_trigger_get_condition(trigger->trigger);
-       assert(condition);
 
-       return !!lttng_condition_is_equal(condition_key, condition);
+       match = lttng_trigger_is_equal(trigger_key, trigger_ht_element->trigger);
+       if (!match) {
+               goto end;
+       }
+
+       /* Validate credential */
+       /* TODO: this could be moved to lttng_trigger_equal depending on how we
+        * handle root behaviour on disable and listing.
+        */
+       creds_key = lttng_trigger_get_credentials(trigger_key);
+       creds_node = lttng_trigger_get_credentials(trigger_ht_element->trigger);
+       match = lttng_credentials_is_equal(creds_key, creds_node);
+end:
+       return !!match;
+}
+
+static
+int match_trigger_token(struct cds_lfht_node *node, const void *key)
+{
+       const uint64_t *_key = key;
+       struct notification_trigger_tokens_ht_element *element;
+
+       element = caa_container_of(node, struct notification_trigger_tokens_ht_element,
+                       node);
+       return *_key == element->token ;
 }
 
 static
@@ -350,7 +319,7 @@ int match_client_list_condition(struct cds_lfht_node *node, const void *key)
        assert(condition_key);
 
        client_list = caa_container_of(node, struct notification_client_list,
-                       notification_trigger_ht_node);
+                       notification_trigger_clients_ht_node);
        condition = lttng_trigger_get_const_condition(client_list->trigger);
 
        return !!lttng_condition_is_equal(condition_key, condition);
@@ -366,6 +335,23 @@ int match_session(struct cds_lfht_node *node, const void *key)
        return !strcmp(session_info->name, name);
 }
 
+/*
+ * Match function for string node.
+ */
+static int match_str(struct cds_lfht_node *node, const void *key)
+{
+       struct lttng_trigger_ht_element *trigger_ht_element;
+       const char *name;
+
+       trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element,
+                       node_by_name);
+
+       /* TODO error checking */
+       lttng_trigger_get_name(trigger_ht_element->trigger, &name);
+
+       return hash_match_key_str(name, (void *) key);
+}
+
 static
 unsigned long lttng_condition_buffer_usage_hash(
        const struct lttng_condition *_condition)
@@ -442,6 +428,22 @@ unsigned long lttng_condition_session_rotation_hash(
        return hash;
 }
 
+static
+unsigned long lttng_condition_event_rule_hash(
+       const struct lttng_condition *_condition)
+{
+       unsigned long hash, condition_type;
+       struct lttng_condition_event_rule *condition;
+
+       condition = container_of(_condition,
+                       struct lttng_condition_event_rule, parent);
+       condition_type = (unsigned long) condition->parent.type;
+       hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
+
+       /* TODO: further hasg using the event rule? on pattern maybe?*/
+       return hash;
+}
+
 /*
  * The lttng_condition hashing code is kept in this file (rather than
  * condition.c) since it makes use of GPLv2 code (hashtable utils), which we
@@ -459,6 +461,8 @@ unsigned long lttng_condition_hash(const struct lttng_condition *condition)
        case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
        case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
                return lttng_condition_session_rotation_hash(condition);
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               return lttng_condition_event_rule_hash(condition);
        default:
                ERR("[notification-thread] Unexpected condition type caught");
                abort();
@@ -475,6 +479,18 @@ unsigned long hash_channel_key(struct channel_key *key)
        return key_hash ^ domain_hash;
 }
 
+static
+unsigned long hash_client_socket(int socket)
+{
+       return hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed);
+}
+
+static
+unsigned long hash_client_id(notification_client_id id)
+{
+       return hash_key_u64(&id, lttng_ht_seed);
+}
+
 /*
  * Get the type of object to which a given condition applies. Bindings let
  * the notification system evaluate a trigger's condition when a given
@@ -495,6 +511,8 @@ enum lttng_object_type get_condition_binding_object(
        case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
        case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
                return LTTNG_OBJECT_TYPE_SESSION;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               return LTTNG_OBJECT_TYPE_NONE;
        default:
                return LTTNG_OBJECT_TYPE_UNKNOWN;
        }
@@ -666,7 +684,90 @@ error:
        return NULL;
 }
 
-/* RCU read lock must be held by the caller. */
+LTTNG_HIDDEN
+bool notification_client_list_get(struct notification_client_list *list)
+{
+       return urcu_ref_get_unless_zero(&list->ref);
+}
+
+static
+void free_notification_client_list_rcu(struct rcu_head *node)
+{
+       free(caa_container_of(node, struct notification_client_list,
+                       rcu_node));
+}
+
+static
+void notification_client_list_release(struct urcu_ref *list_ref)
+{
+       struct notification_client_list *list =
+                       container_of(list_ref, typeof(*list), ref);
+       struct notification_client_list_element *client_list_element, *tmp;
+
+       if (list->notification_trigger_clients_ht) {
+               rcu_read_lock();
+               cds_lfht_del(list->notification_trigger_clients_ht,
+                               &list->notification_trigger_clients_ht_node);
+               rcu_read_unlock();
+               list->notification_trigger_clients_ht = NULL;
+       }
+       cds_list_for_each_entry_safe(client_list_element, tmp,
+                                    &list->list, node) {
+               free(client_list_element);
+       }
+       pthread_mutex_destroy(&list->lock);
+       call_rcu(&list->rcu_node, free_notification_client_list_rcu);
+}
+
+static
+struct notification_client_list *notification_client_list_create(
+               const struct lttng_trigger *trigger)
+{
+       struct notification_client_list *client_list =
+                       zmalloc(sizeof(*client_list));
+
+       if (!client_list) {
+               goto error;
+       }
+       pthread_mutex_init(&client_list->lock, NULL);
+       urcu_ref_init(&client_list->ref);
+       cds_lfht_node_init(&client_list->notification_trigger_clients_ht_node);
+       CDS_INIT_LIST_HEAD(&client_list->list);
+       client_list->trigger = trigger;
+error:
+       return client_list;
+}
+
+static
+void publish_notification_client_list(
+               struct notification_thread_state *state,
+               struct notification_client_list *list)
+{
+       const struct lttng_condition *condition =
+                       lttng_trigger_get_const_condition(list->trigger);
+
+       assert(!list->notification_trigger_clients_ht);
+
+       list->notification_trigger_clients_ht =
+                       state->notification_trigger_clients_ht;
+
+       rcu_read_lock();
+       cds_lfht_add(state->notification_trigger_clients_ht,
+                       lttng_condition_hash(condition),
+                       &list->notification_trigger_clients_ht_node);
+       rcu_read_unlock();
+}
+
+LTTNG_HIDDEN
+void notification_client_list_put(struct notification_client_list *list)
+{
+       if (!list) {
+               return;
+       }
+       return urcu_ref_put(&list->ref, notification_client_list_release);
+}
+
+/* Provides a reference to the returned list. */
 static
 struct notification_client_list *get_client_list_from_condition(
        struct notification_thread_state *state,
@@ -674,20 +775,24 @@ struct notification_client_list *get_client_list_from_condition(
 {
        struct cds_lfht_node *node;
        struct cds_lfht_iter iter;
+       struct notification_client_list *list = NULL;
 
+       rcu_read_lock();
        cds_lfht_lookup(state->notification_trigger_clients_ht,
                        lttng_condition_hash(condition),
                        match_client_list_condition,
                        condition,
                        &iter);
        node = cds_lfht_iter_get_node(&iter);
-
-        return node ? caa_container_of(node,
-                       struct notification_client_list,
-                       notification_trigger_ht_node) : NULL;
+       if (node) {
+               list = container_of(node, struct notification_client_list,
+                               notification_trigger_clients_ht_node);
+               list = notification_client_list_get(list) ? list : NULL;
+       }
+       rcu_read_unlock();
+        return list;
 }
 
-/* This function must be called with the RCU read lock held. */
 static
 int evaluate_channel_condition_for_client(
                const struct lttng_condition *condition,
@@ -703,6 +808,8 @@ int evaluate_channel_condition_for_client(
        struct channel_state_sample *last_sample = NULL;
        struct lttng_channel_trigger_list *channel_trigger_list = NULL;
 
+       rcu_read_lock();
+
        /* Find the channel associated with the condition. */
        cds_lfht_for_each_entry(state->channel_triggers_ht, &iter,
                        channel_trigger_list, channel_triggers_ht_node) {
@@ -777,6 +884,7 @@ int evaluate_channel_condition_for_client(
        *session_uid = channel_info->session_info->uid;
        *session_gid = channel_info->session_info->gid;
 end:
+       rcu_read_unlock();
        return ret;
 }
 
@@ -812,7 +920,6 @@ end:
        return session_name;
 }
 
-/* This function must be called with the RCU read lock held. */
 static
 int evaluate_session_condition_for_client(
                const struct lttng_condition *condition,
@@ -826,6 +933,7 @@ int evaluate_session_condition_for_client(
        const char *session_name;
        struct session_info *session_info = NULL;
 
+       rcu_read_lock();
        session_name = get_condition_session_name(condition);
 
        /* Find the session associated with the trigger. */
@@ -879,10 +987,10 @@ int evaluate_session_condition_for_client(
 end_session_put:
        session_info_put(session_info);
 end:
+       rcu_read_unlock();
        return ret;
 }
 
-/* This function must be called with the RCU read lock held. */
 static
 int evaluate_condition_for_client(const struct lttng_trigger *trigger,
                const struct lttng_condition *condition,
@@ -891,7 +999,9 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger,
 {
        int ret;
        struct lttng_evaluation *evaluation = NULL;
-       struct notification_client_list client_list = { 0 };
+       struct notification_client_list client_list = {
+               .lock = PTHREAD_MUTEX_INITIALIZER,
+       };
        struct notification_client_list_element client_list_element = { 0 };
        uid_t object_uid = 0;
        gid_t object_gid = 0;
@@ -911,6 +1021,7 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger,
                                &evaluation, &object_uid, &object_gid);
                break;
        case LTTNG_OBJECT_TYPE_NONE:
+               DBG("[notification-thread] Newly subscribed-to condition not binded to object, nothing to evaluate");
                ret = 0;
                goto end;
        case LTTNG_OBJECT_TYPE_UNKNOWN:
@@ -933,7 +1044,7 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger,
         * Create a temporary client list with the client currently
         * subscribing.
         */
-       cds_lfht_node_init(&client_list.notification_trigger_ht_node);
+       cds_lfht_node_init(&client_list.notification_trigger_clients_ht_node);
        CDS_INIT_LIST_HEAD(&client_list.list);
        client_list.trigger = trigger;
 
@@ -957,7 +1068,7 @@ int notification_thread_client_subscribe(struct notification_client *client,
                enum lttng_notification_channel_status *_status)
 {
        int ret = 0;
-       struct notification_client_list *client_list;
+       struct notification_client_list *client_list = NULL;
        struct lttng_condition_list_element *condition_list_element = NULL;
        struct notification_client_list_element *client_list_element = NULL;
        enum lttng_notification_channel_status status =
@@ -986,8 +1097,6 @@ int notification_thread_client_subscribe(struct notification_client *client,
                goto error;
        }
 
-       rcu_read_lock();
-
        /*
         * Add the newly-subscribed condition to the client's subscription list.
         */
@@ -1003,20 +1112,24 @@ int notification_thread_client_subscribe(struct notification_client *client,
                 * since this trigger is not registered yet.
                 */
                free(client_list_element);
-               goto end_unlock;
+               goto end;
        }
 
        /*
         * The condition to which the client just subscribed is evaluated
         * at this point so that conditions that are already TRUE result
         * in a notification being sent out.
+        *
+        * The client_list's trigger is used without locking the list itself.
+        * This is correct since the list doesn't own the trigger and the
+        * object is immutable.
         */
        if (evaluate_condition_for_client(client_list->trigger, condition,
                        client, state)) {
                WARN("[notification-thread] Evaluation of a condition on client subscription failed, aborting.");
                ret = -1;
                free(client_list_element);
-               goto end_unlock;
+               goto end;
        }
 
        /*
@@ -1026,13 +1139,17 @@ int notification_thread_client_subscribe(struct notification_client *client,
         */
        client_list_element->client = client;
        CDS_INIT_LIST_HEAD(&client_list_element->node);
+
+       pthread_mutex_lock(&client_list->lock);
        cds_list_add(&client_list_element->node, &client_list->list);
-end_unlock:
-       rcu_read_unlock();
+       pthread_mutex_unlock(&client_list->lock);
 end:
        if (_status) {
                *_status = status;
        }
+       if (client_list) {
+               notification_client_list_put(client_list);
+       }
        return ret;
 error:
        free(condition_list_element);
@@ -1088,23 +1205,24 @@ int notification_thread_client_unsubscribe(
         * Remove the client from the list of clients interested the trigger
         * matching the condition.
         */
-       rcu_read_lock();
        client_list = get_client_list_from_condition(state, condition);
        if (!client_list) {
-               goto end_unlock;
+               goto end;
        }
 
+       pthread_mutex_lock(&client_list->lock);
        cds_list_for_each_entry_safe(client_list_element, client_tmp,
                        &client_list->list, node) {
-               if (client_list_element->client->socket != client->socket) {
+               if (client_list_element->client->id != client->id) {
                        continue;
                }
                cds_list_del(&client_list_element->node);
                free(client_list_element);
                break;
        }
-end_unlock:
-       rcu_read_unlock();
+       pthread_mutex_unlock(&client_list->lock);
+       notification_client_list_put(client_list);
+       client_list = NULL;
 end:
        lttng_condition_destroy(condition);
        if (_status) {
@@ -1123,24 +1241,22 @@ static
 void notification_client_destroy(struct notification_client *client,
                struct notification_thread_state *state)
 {
-       struct lttng_condition_list_element *condition_list_element, *tmp;
-
        if (!client) {
                return;
        }
 
-       /* Release all conditions to which the client was subscribed. */
-       cds_list_for_each_entry_safe(condition_list_element, tmp,
-                       &client->condition_list, node) {
-               (void) notification_thread_client_unsubscribe(client,
-                               condition_list_element->condition, state, NULL);
-       }
-
+       /*
+        * The client object is not reachable by other threads, no need to lock
+        * the client here.
+        */
        if (client->socket >= 0) {
                (void) lttcomm_close_unix_sock(client->socket);
+               client->socket = -1;
        }
+       client->communication.active = false;
        lttng_dynamic_buffer_reset(&client->communication.inbound.buffer);
        lttng_dynamic_buffer_reset(&client->communication.outbound.buffer);
+       pthread_mutex_destroy(&client->lock);
        call_rcu(&client->rcu_node, free_notification_client_rcu);
 }
 
@@ -1157,8 +1273,8 @@ struct notification_client *get_client_from_socket(int socket,
        struct notification_client *client = NULL;
 
        cds_lfht_lookup(state->client_socket_ht,
-                       hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed),
-                       match_client,
+                       hash_client_socket(socket),
+                       match_client_socket,
                        (void *) (unsigned long) socket,
                        &iter);
        node = cds_lfht_iter_get_node(&iter);
@@ -1172,6 +1288,34 @@ end:
        return client;
 }
 
+/*
+ * Call with rcu_read_lock held (and hold for the lifetime of the returned
+ * client pointer).
+ */
+static
+struct notification_client *get_client_from_id(notification_client_id id,
+               struct notification_thread_state *state)
+{
+       struct cds_lfht_iter iter;
+       struct cds_lfht_node *node;
+       struct notification_client *client = NULL;
+
+       cds_lfht_lookup(state->client_id_ht,
+                       hash_client_id(id),
+                       match_client_id,
+                       &id,
+                       &iter);
+       node = cds_lfht_iter_get_node(&iter);
+       if (!node) {
+               goto end;
+       }
+
+       client = caa_container_of(node, struct notification_client,
+                       client_id_ht_node);
+end:
+       return client;
+}
+
 static
 bool buffer_usage_condition_applies_to_channel(
                const struct lttng_condition *condition,
@@ -1375,7 +1519,7 @@ void lttng_session_trigger_list_destroy(struct lttng_session_trigger_list *list)
 
 static
 int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list,
-               const struct lttng_trigger *trigger)
+               struct lttng_trigger *trigger)
 {
        int ret = 0;
        struct lttng_trigger_list_element *new_element =
@@ -1762,6 +1906,7 @@ int handle_notification_thread_command_session_rotation(
                struct notification_client_list *client_list;
                struct lttng_evaluation *evaluation = NULL;
                enum lttng_condition_type condition_type;
+               bool client_list_is_empty;
 
                trigger = trigger_list_element->trigger;
                condition = lttng_trigger_get_const_condition(trigger);
@@ -1785,7 +1930,10 @@ int handle_notification_thread_command_session_rotation(
                client_list = get_client_list_from_condition(state, condition);
                assert(client_list);
 
-               if (cds_list_empty(&client_list->list)) {
+               pthread_mutex_lock(&client_list->lock);
+               client_list_is_empty = cds_list_empty(&client_list->list);
+               pthread_mutex_unlock(&client_list->lock);
+               if (client_list_is_empty) {
                        /*
                         * No clients interested in the evaluation's result,
                         * skip it.
@@ -1805,7 +1953,7 @@ int handle_notification_thread_command_session_rotation(
                        /* Internal error */
                        ret = -1;
                        cmd_result = LTTNG_ERR_UNK;
-                       goto end;
+                       goto put_list;
                }
 
                /* Dispatch evaluation result to all clients. */
@@ -1814,8 +1962,10 @@ int handle_notification_thread_command_session_rotation(
                                session_info->uid,
                                session_info->gid);
                lttng_evaluation_destroy(evaluation);
+put_list:
+               notification_client_list_put(client_list);
                if (caa_unlikely(ret)) {
-                       goto end;
+                       break;
                }
        }
 end:
@@ -1826,59 +1976,294 @@ end:
 }
 
 static
-int condition_is_supported(struct lttng_condition *condition)
+int handle_notification_thread_command_add_application(
+       struct notification_thread_handle *handle,
+       struct notification_thread_state *state,
+       int read_side_trigger_event_application_pipe,
+       enum lttng_error_code *_cmd_result)
 {
-       int ret;
+       int ret = 0;
+       enum lttng_error_code cmd_result = LTTNG_OK;
+       struct notification_event_trigger_source_element *element = NULL;
 
-       switch (lttng_condition_get_type(condition)) {
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-       {
-               enum lttng_domain_type domain;
+       element = zmalloc(sizeof(*element));
+       if (!element) {
+               cmd_result = LTTNG_ERR_NOMEM;
+               ret = -1;
+               goto end;
+       }
 
-               ret = lttng_condition_buffer_usage_get_domain_type(condition,
-                               &domain);
-               if (ret) {
-                       ret = -1;
-                       goto end;
-               }
+       CDS_INIT_LIST_HEAD(&element->node);
+       element->fd = read_side_trigger_event_application_pipe;
 
-               if (domain != LTTNG_DOMAIN_KERNEL) {
-                       ret = 1;
-                       goto end;
-               }
+       pthread_mutex_lock(&handle->event_trigger_sources.lock);
+       cds_list_add(&element->node, &handle->event_trigger_sources.list);
+       pthread_mutex_unlock(&handle->event_trigger_sources.lock);
 
-               /*
-                * Older kernel tracers don't expose the API to monitor their
-                * buffers. Therefore, we reject triggers that require that
-                * mechanism to be available to be evaluated.
-                */
-               ret = kernel_supports_ring_buffer_snapshot_sample_positions();
-               break;
-       }
-       default:
-               ret = 1;
+       /* TODO: remove on failure to add to list? */
+
+       /* Adding the read side pipe to the event poll */
+       ret = lttng_poll_add(&state->events,
+                       read_side_trigger_event_application_pipe,
+                       LPOLLIN | LPOLLERR);
+
+       DBG3("[notification-thread] Adding application event source from fd: %d", read_side_trigger_event_application_pipe);
+       if (ret < 0) {
+               /* TODO: what should be the value of cmd_result??? */
+               ERR("[notification-thread] Failed to add event source pipe fd to pollset");
+               goto end;
        }
+
 end:
+       *_cmd_result = cmd_result;
        return ret;
 }
 
-/* Must be called with RCU read lock held. */
 static
-int bind_trigger_to_matching_session(const struct lttng_trigger *trigger,
-               struct notification_thread_state *state)
+int handle_notification_thread_command_remove_application(
+       struct notification_thread_handle *handle,
+       struct notification_thread_state *state,
+       int read_side_trigger_event_application_pipe,
+       enum lttng_error_code *_cmd_result)
 {
        int ret = 0;
-       const struct lttng_condition *condition;
-       const char *session_name;
-       struct lttng_session_trigger_list *trigger_list;
+       enum lttng_error_code cmd_result = LTTNG_OK;
 
-       condition = lttng_trigger_get_const_condition(trigger);
-       switch (lttng_condition_get_type(condition)) {
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-       {
-               enum lttng_condition_status status;
+       /* TODO: missing a lock propably to revisit */
+       struct notification_event_trigger_source_element *source_element, *tmp;
+       cds_list_for_each_entry_safe(source_element, tmp,
+                       &handle->event_trigger_sources.list, node) {
+               if (source_element->fd != read_side_trigger_event_application_pipe) {
+                       continue;
+               }
+
+               DBG("[notification-thread] Removed event source from event source list");
+               cds_list_del(&source_element->node);
+               break;
+       }
+
+       DBG3("[notification-thread] Removing application event source from fd: %d", read_side_trigger_event_application_pipe);
+       /* Removing the read side pipe to the event poll */
+       ret = lttng_poll_del(&state->events,
+                       read_side_trigger_event_application_pipe);
+       if (ret < 0) {
+               /* TODO: what should be the value of cmd_result??? */
+               ERR("[notification-thread] Failed to remove event source pipe fd from pollset");
+               goto end;
+       }
+
+end:
+       *_cmd_result = cmd_result;
+       return ret;
+}
+
+static int handle_notification_thread_command_get_tokens(
+               struct notification_thread_handle *handle,
+               struct notification_thread_state *state,
+               struct lttng_triggers **triggers,
+               enum lttng_error_code *_cmd_result)
+{
+       int ret = 0, i = 0;
+       enum lttng_error_code cmd_result = LTTNG_OK;
+       struct cds_lfht_iter iter;
+       struct notification_trigger_tokens_ht_element *element;
+       struct lttng_triggers *local_triggers = NULL;
+
+       local_triggers = lttng_triggers_create();
+       if (!local_triggers) {
+               cmd_result = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       rcu_read_lock();
+       cds_lfht_for_each_entry (
+                       state->trigger_tokens_ht, &iter, element, node) {
+               ret = lttng_triggers_add(local_triggers, element->trigger);
+               if (ret < 0) {
+                       cmd_result = LTTNG_ERR_FATAL;
+                       ret = -1;
+                       goto end;
+               }
+
+               /* Ownership is shared with the lttng_triggers object */
+               lttng_trigger_get(element->trigger);
+
+               i++;
+       }
+
+       /* Passing ownership up */
+       *triggers = local_triggers;
+       local_triggers = NULL;
+
+end:
+       rcu_read_unlock();
+       lttng_triggers_destroy(local_triggers);
+       *_cmd_result = cmd_result;
+       return ret;
+}
+
+static
+int handle_notification_thread_command_list_triggers(
+       struct notification_thread_handle *handle,
+       struct notification_thread_state *state,
+       uid_t uid,
+       gid_t gid,
+       struct lttng_triggers **triggers,
+       enum lttng_error_code *_cmd_result)
+{
+       int ret = 0, i = 0;
+       enum lttng_error_code cmd_result = LTTNG_OK;
+       struct cds_lfht_iter iter;
+       struct lttng_trigger_ht_element *trigger_ht_element;
+       struct lttng_triggers *local_triggers = NULL;
+       const struct lttng_credentials *creds;
+       
+       long scb, sca;
+       unsigned long count;
+
+       rcu_read_lock();
+       cds_lfht_count_nodes(state->triggers_ht, &scb, &count, &sca);
+
+       /* TODO check downcasting */
+       local_triggers = lttng_triggers_create();
+       if (!local_triggers) {
+               cmd_result = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       cds_lfht_for_each_entry (state->triggers_ht, &iter,
+                       trigger_ht_element, node) {
+               /* Only return the trigger for which the requestion client have
+                * access. For now the root user can only list its own
+                * triggers.
+                * TODO: root user behavior
+                */
+               creds = lttng_trigger_get_credentials(trigger_ht_element->trigger);
+               if ((uid != creds->uid) || (gid != creds->gid)) {
+                       continue;
+               }
+
+               ret = lttng_triggers_add(local_triggers, trigger_ht_element->trigger);
+               if (ret < 0) {
+                       ret = -1;
+                       goto end;
+               }
+               /* Ownership is shared with the lttng_triggers object */
+               lttng_trigger_get(trigger_ht_element->trigger);
+
+               i++;
+       }
+
+       /* Passing ownership up */
+       *triggers = local_triggers;
+       local_triggers = NULL;
+
+end:
+       rcu_read_unlock();
+       lttng_triggers_destroy(local_triggers);
+       *_cmd_result = cmd_result;
+       return ret;
+}
+
+static
+int condition_is_supported(struct lttng_condition *condition)
+{
+       int ret;
+
+       switch (lttng_condition_get_type(condition)) {
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+       {
+               enum lttng_domain_type domain;
+
+               ret = lttng_condition_buffer_usage_get_domain_type(condition,
+                               &domain);
+               if (ret) {
+                       ret = -1;
+                       goto end;
+               }
+
+               if (domain != LTTNG_DOMAIN_KERNEL) {
+                       ret = 1;
+                       goto end;
+               }
+
+               /*
+                * Older kernel tracers don't expose the API to monitor their
+                * buffers. Therefore, we reject triggers that require that
+                * mechanism to be available to be evaluated.
+                */
+               ret = kernel_supports_ring_buffer_snapshot_sample_positions();
+               break;
+       }
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+       {
+               /* TODO:
+                * Check for kernel support.
+                * Check for ust support ??
+                */
+               ret = 1;
+               break;
+       }
+       default:
+               ret = 1;
+       }
+end:
+       return ret;
+}
+
+static
+int action_is_supported(struct lttng_action *action)
+{
+       int ret;
+
+       switch (lttng_action_get_type(action)) {
+       case LTTNG_ACTION_TYPE_NOTIFY:
+       case LTTNG_ACTION_TYPE_START_SESSION:
+       case LTTNG_ACTION_TYPE_STOP_SESSION:
+       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+       {
+               /* TODO validate that this is true for kernel in regards to
+                * rotation and snapshot. Start stop is not a problem notify
+                * either.
+                */
+               /* For now all type of actions are supported */
+               ret = 1;
+               break;
+       }
+       case LTTNG_ACTION_TYPE_GROUP:
+       {
+               /* TODO: Iterate over all internal actions and validate that
+                * they are supported
+                */
+               ret = 1;
+               break;
+
+       }
+       default:
+               ret = 1;
+       }
+
+       return ret;
+}
+
+/* Must be called with RCU read lock held. */
+static
+int bind_trigger_to_matching_session(struct lttng_trigger *trigger,
+               struct notification_thread_state *state)
+{
+       int ret = 0;
+       const struct lttng_condition *condition;
+       const char *session_name;
+       struct lttng_session_trigger_list *trigger_list;
+
+       condition = lttng_trigger_get_const_condition(trigger);
+       switch (lttng_condition_get_type(condition)) {
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+       {
+               enum lttng_condition_status status;
 
                status = lttng_condition_session_rotation_get_session_name(
                                condition, &session_name);
@@ -1911,7 +2296,7 @@ end:
 
 /* Must be called with RCU read lock held. */
 static
-int bind_trigger_to_matching_channels(const struct lttng_trigger *trigger,
+int bind_trigger_to_matching_channels(struct lttng_trigger *trigger,
                struct notification_thread_state *state)
 {
        int ret = 0;
@@ -1955,95 +2340,26 @@ end:
        return ret;
 }
 
-/*
- * FIXME A client's credentials are not checked when registering a trigger, nor
- *       are they stored alongside with the trigger.
- *
- * The effects of this are benign since:
- *     - The client will succeed in registering the trigger, as it is valid,
- *     - The trigger will, internally, be bound to the channel/session,
- *     - The notifications will not be sent since the client's credentials
- *       are checked against the channel at that moment.
- *
- * If this function returns a non-zero value, it means something is
- * fundamentally broken and the whole subsystem/thread will be torn down.
- *
- * If a non-fatal error occurs, just set the cmd_result to the appropriate
- * error code.
- */
-static
-int handle_notification_thread_command_register_trigger(
+static int action_notify_register_trigger(
                struct notification_thread_state *state,
-               struct lttng_trigger *trigger,
-               enum lttng_error_code *cmd_result)
+               struct lttng_trigger *trigger)
 {
+
        int ret = 0;
        struct lttng_condition *condition;
        struct notification_client *client;
        struct notification_client_list *client_list = NULL;
-       struct lttng_trigger_ht_element *trigger_ht_element = NULL;
-       struct notification_client_list_element *client_list_element, *tmp;
-       struct cds_lfht_node *node;
        struct cds_lfht_iter iter;
-       bool free_trigger = true;
-
-       rcu_read_lock();
+       struct notification_client_list_element *client_list_element, *tmp;
 
        condition = lttng_trigger_get_condition(trigger);
        assert(condition);
 
-       ret = condition_is_supported(condition);
-       if (ret < 0) {
-               goto error;
-       } else if (ret == 0) {
-               *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
-               goto error;
-       } else {
-               /* Feature is supported, continue. */
-               ret = 0;
-       }
-
-       trigger_ht_element = zmalloc(sizeof(*trigger_ht_element));
-       if (!trigger_ht_element) {
-               ret = -1;
-               goto error;
-       }
-
-       /* Add trigger to the trigger_ht. */
-       cds_lfht_node_init(&trigger_ht_element->node);
-       trigger_ht_element->trigger = trigger;
-
-       node = cds_lfht_add_unique(state->triggers_ht,
-                       lttng_condition_hash(condition),
-                       match_condition,
-                       condition,
-                       &trigger_ht_element->node);
-       if (node != &trigger_ht_element->node) {
-               /* Not a fatal error, simply report it to the client. */
-               *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
-               goto error_free_ht_element;
-       }
-
-       /*
-        * Ownership of the trigger and of its wrapper was transfered to
-        * the triggers_ht.
-        */
-       trigger_ht_element = NULL;
-       free_trigger = false;
-
-       /*
-        * The rest only applies to triggers that have a "notify" action.
-        * It is not skipped as this is the only action type currently
-        * supported.
-        */
-       client_list = zmalloc(sizeof(*client_list));
+       client_list = notification_client_list_create(trigger);
        if (!client_list) {
                ret = -1;
-               goto error_free_ht_element;
+               goto end;
        }
-       cds_lfht_node_init(&client_list->notification_trigger_ht_node);
-       CDS_INIT_LIST_HEAD(&client_list->list);
-       client_list->trigger = trigger;
 
        /* Build a list of clients to which this new trigger applies. */
        cds_lfht_for_each_entry(state->client_socket_ht, &iter, client,
@@ -2055,23 +2371,19 @@ int handle_notification_thread_command_register_trigger(
                client_list_element = zmalloc(sizeof(*client_list_element));
                if (!client_list_element) {
                        ret = -1;
-                       goto error_free_client_list;
+                       goto error_put_client_list;
                }
                CDS_INIT_LIST_HEAD(&client_list_element->node);
                client_list_element->client = client;
                cds_list_add(&client_list_element->node, &client_list->list);
        }
 
-       cds_lfht_add(state->notification_trigger_clients_ht,
-                       lttng_condition_hash(condition),
-                       &client_list->notification_trigger_ht_node);
-
        switch (get_condition_binding_object(condition)) {
        case LTTNG_OBJECT_TYPE_SESSION:
                /* Add the trigger to the list if it matches a known session. */
                ret = bind_trigger_to_matching_session(trigger, state);
                if (ret) {
-                       goto error_free_client_list;
+                       goto error_put_client_list;
                }
                break;
        case LTTNG_OBJECT_TYPE_CHANNEL:
@@ -2081,7 +2393,7 @@ int handle_notification_thread_command_register_trigger(
                 */
                ret = bind_trigger_to_matching_channels(trigger, state);
                if (ret) {
-                       goto error_free_client_list;
+                       goto error_put_client_list;
                }
                break;
        case LTTNG_OBJECT_TYPE_NONE:
@@ -2089,7 +2401,7 @@ int handle_notification_thread_command_register_trigger(
        default:
                ERR("[notification-thread] Unknown object type on which to bind a newly registered trigger was encountered");
                ret = -1;
-               goto error_free_client_list;
+               goto error_put_client_list;
        }
 
        /*
@@ -2117,13 +2429,15 @@ int handle_notification_thread_command_register_trigger(
         * current state. Otherwise, the next evaluation cycle may only see
         * that the evaluations remain the same (true for samples n-1 and n) and
         * the client will never know that the condition has been met.
+        *
+        * No need to lock the list here as it has not been published yet.
         */
        cds_list_for_each_entry_safe(client_list_element, tmp,
                        &client_list->list, node) {
                ret = evaluate_condition_for_client(trigger, condition,
                                client_list_element->client, state);
                if (ret) {
-                       goto error_free_client_list;
+                       goto error_put_client_list;
                }
        }
 
@@ -2131,25 +2445,252 @@ int handle_notification_thread_command_register_trigger(
         * Client list ownership transferred to the
         * notification_trigger_clients_ht.
         */
+       publish_notification_client_list(state, client_list);
        client_list = NULL;
+error_put_client_list:
+       notification_client_list_put(client_list);
+end:
+       return ret;
+}
 
-       *cmd_result = LTTNG_OK;
-error_free_client_list:
-       if (client_list) {
-               cds_list_for_each_entry_safe(client_list_element, tmp,
-                               &client_list->list, node) {
-                       free(client_list_element);
+static
+bool trigger_name_taken(struct notification_thread_state *state, const char *name)
+{
+       struct cds_lfht_node *triggers_by_name_ht_node;
+       struct cds_lfht_iter iter;
+       /* TODO change hashing for trigger */
+       cds_lfht_lookup(state->triggers_by_name_ht,
+                       hash_key_str(name, lttng_ht_seed),
+                       match_str,
+                       name,
+                       &iter);
+       triggers_by_name_ht_node = cds_lfht_iter_get_node(&iter);
+       if (triggers_by_name_ht_node) {
+               return true;
+       } else {
+               return false;
+       }
+
+}
+static
+void generate_trigger_name(struct notification_thread_state *state, struct lttng_trigger *trigger, const char **name)
+{
+       /* Here the offset criteria guarantee an end. This will be a nice
+        * bikeshedding conversation. I would simply generate uuid and use them
+        * as trigger name.
+        */
+       bool taken = false;
+       do {
+               lttng_trigger_generate_name(trigger, state->trigger_id.name_offset);
+               /* TODO error checking */
+               lttng_trigger_get_name(trigger, name);
+               taken = trigger_name_taken(state, *name);
+               if (taken) {
+                       state->trigger_id.name_offset++;
+               }
+       } while (taken || state->trigger_id.name_offset == UINT32_MAX);
+}
+
+static bool action_is_notify(const struct lttng_action *action)
+{
+       /* TODO for action groups we need to iterate over all of them */
+       enum lttng_action_type type = lttng_action_get_type_const(action);
+       bool ret = false;
+       enum lttng_action_status status;
+       const struct lttng_action *tmp;
+       unsigned int i, count;
+
+       switch (type) {
+       case LTTNG_ACTION_TYPE_NOTIFY:
+               ret = true;
+               break;
+       case LTTNG_ACTION_TYPE_GROUP:
+               status = lttng_action_group_get_count(action, &count);
+               if (status != LTTNG_ACTION_STATUS_OK) {
+                       assert(0);
+               }
+               for (i = 0; i < count; i++) {
+                       tmp = lttng_action_group_get_at_index_const(action, i);
+                       assert(tmp);
+                       ret = action_is_notify(tmp);
+                       if (ret) {
+                               break;
+                       }
+               }
+               break;
+       default:
+               ret = false;
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * TODO: REVIEW THIS COMMENT.
+ * FIXME A client's credentials are not checked when registering a trigger, nor
+ *       are they stored alongside with the trigger.
+ *
+ * The effects of this are benign since:
+ *     - The client will succeed in registering the trigger, as it is valid,
+ *     - The trigger will, internally, be bound to the channel/session,
+ *     - The notifications will not be sent since the client's credentials
+ *       are checked against the channel at that moment.
+ *
+ * If this function returns a non-zero value, it means something is
+ * fundamentally broken and the whole subsystem/thread will be torn down.
+ *
+ * If a non-fatal error occurs, just set the cmd_result to the appropriate
+ * error code.
+ */
+static
+int handle_notification_thread_command_register_trigger(
+               struct notification_thread_state *state,
+               struct lttng_trigger *trigger,
+               enum lttng_error_code *cmd_result)
+{
+       int ret = 0;
+       int is_supported;
+       struct lttng_condition *condition;
+       struct lttng_action *action;
+       struct lttng_trigger_ht_element *trigger_ht_element = NULL;
+       struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL;
+       struct cds_lfht_node *node;
+       const char* trigger_name;
+       bool free_trigger = true;
+
+       assert(trigger->creds.set);
+
+       rcu_read_lock();
+
+       /* Set the trigger's key */
+       lttng_trigger_set_key(trigger, state->trigger_id.token_generator);
+
+       if (lttng_trigger_get_name(trigger, &trigger_name) == LTTNG_TRIGGER_STATUS_UNSET) {
+               generate_trigger_name(state, trigger, &trigger_name);
+       } else if (trigger_name_taken(state, trigger_name)) {
+               /* Not a fatal error */
+               *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+               ret = 0;
+               goto error;
+       }
+
+       condition = lttng_trigger_get_condition(trigger);
+       assert(condition);
+
+       action = lttng_trigger_get_action(trigger);
+       assert(action);
+
+       is_supported = condition_is_supported(condition);
+       if (is_supported < 0) {
+               goto error;
+       } else if (is_supported == 0) {
+               ret = 0;
+               *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
+               goto error;
+       }
+
+       is_supported = action_is_supported(action);
+       if (is_supported < 0) {
+               goto error;
+       } else if (is_supported == 0) {
+               ret = 0;
+               *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
+               goto error;
+       }
+
+       trigger_ht_element = zmalloc(sizeof(*trigger_ht_element));
+       if (!trigger_ht_element) {
+               ret = -1;
+               goto error;
+       }
+
+       /* Add trigger to the trigger_ht. */
+       cds_lfht_node_init(&trigger_ht_element->node);
+       cds_lfht_node_init(&trigger_ht_element->node_by_name);
+
+       /*
+        * This element own the trigger object from now own, this is why there
+        * is no lttng_trigger_get here.
+        * This thread is now the owner of the trigger object.
+        */
+       trigger_ht_element->trigger = trigger;
+
+       node = cds_lfht_add_unique(state->triggers_ht,
+                       lttng_condition_hash(condition),
+                       match_trigger,
+                       trigger,
+                       &trigger_ht_element->node);
+       if (node != &trigger_ht_element->node) {
+               /* Not a fatal error, simply report it to the client. */
+               *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+               goto error_free_ht_element;
+       }
+
+       node = cds_lfht_add_unique(state->triggers_by_name_ht,
+                       hash_key_str(trigger_name, lttng_ht_seed),
+                       match_str,
+                       trigger_name,
+                       &trigger_ht_element->node_by_name);
+       if (node != &trigger_ht_element->node_by_name) {
+               /* This should never happen */
+               /* Not a fatal error, simply report it to the client. */
+               /* TODO remove from the trigger_ht */
+               *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+               goto error_free_ht_element;
+       }
+
+       if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+               trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element));
+               if (!trigger_tokens_ht_element) {
+                       ret = -1;
+                       goto error;
+               }
+
+               /* Add trigger token to the trigger_tokens_ht. */
+               cds_lfht_node_init(&trigger_tokens_ht_element->node);
+               trigger_tokens_ht_element->token = trigger->key.value;
+               trigger_tokens_ht_element->trigger = trigger;
+
+               node = cds_lfht_add_unique(state->trigger_tokens_ht,
+                               hash_key_u64(&trigger_tokens_ht_element->token, lttng_ht_seed),
+                               match_trigger_token,
+                               &trigger_tokens_ht_element->token,
+                               &trigger_tokens_ht_element->node);
+               if (node != &trigger_tokens_ht_element->node) {
+                       /* TODO: THIS IS A FATAL ERROR... should never happen */
+                       /* Not a fatal error, simply report it to the client. */
+                       *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+                       goto error_free_ht_element;
+               }
+       }
+
+       /*
+        * Ownership of the trigger and of its wrapper was transfered to
+        * the triggers_ht. Same for token ht element if necessary.
+        */
+       trigger_tokens_ht_element = NULL;
+       trigger_ht_element = NULL;
+       free_trigger = false;
+
+       if (action_is_notify(action)) {
+               ret = action_notify_register_trigger(state, trigger);
+               if (ret < 0) {
+                       /* TODO should cmd_result be set here? */
+                       ret = -1;
+                       goto error_free_ht_element;
                }
-               free(client_list);
        }
+
+       /* Increment the trigger unique id generator */
+       state->trigger_id.token_generator++;
+       *cmd_result = LTTNG_OK;
+
 error_free_ht_element:
        free(trigger_ht_element);
+       free(trigger_tokens_ht_element);
 error:
        if (free_trigger) {
-               struct lttng_action *action = lttng_trigger_get_action(trigger);
-
-               lttng_condition_destroy(condition);
-               lttng_action_destroy(action);
                lttng_trigger_destroy(trigger);
        }
        rcu_read_unlock();
@@ -2157,16 +2698,16 @@ error:
 }
 
 static
-void free_notification_client_list_rcu(struct rcu_head *node)
+void free_lttng_trigger_ht_element_rcu(struct rcu_head *node)
 {
-       free(caa_container_of(node, struct notification_client_list,
+       free(caa_container_of(node, struct lttng_trigger_ht_element,
                        rcu_node));
 }
 
 static
-void free_lttng_trigger_ht_element_rcu(struct rcu_head *node)
+void free_notification_trigger_tokens_ht_element_rcu(struct rcu_head *node)
 {
-       free(caa_container_of(node, struct lttng_trigger_ht_element,
+       free(caa_container_of(node, struct notification_trigger_tokens_ht_element,
                        rcu_node));
 }
 
@@ -2180,19 +2721,22 @@ int handle_notification_thread_command_unregister_trigger(
        struct cds_lfht_node *triggers_ht_node;
        struct lttng_channel_trigger_list *trigger_list;
        struct notification_client_list *client_list;
-       struct notification_client_list_element *client_list_element, *tmp;
        struct lttng_trigger_ht_element *trigger_ht_element = NULL;
        struct lttng_condition *condition = lttng_trigger_get_condition(
                        trigger);
-       struct lttng_action *action;
+       struct lttng_action *action = lttng_trigger_get_action(trigger);
        enum lttng_error_code cmd_reply;
 
        rcu_read_lock();
 
+       /* TODO change hashing for trigger */
+       /* TODO Disabling for the root user is not complete, for now the root
+        * user cannot disable the trigger from another user.
+        */
        cds_lfht_lookup(state->triggers_ht,
                        lttng_condition_hash(condition),
-                       match_condition,
-                       condition,
+                       match_trigger,
+                       trigger,
                        &iter);
        triggers_ht_node = cds_lfht_iter_get_node(&iter);
        if (!triggers_ht_node) {
@@ -2209,13 +2753,7 @@ int handle_notification_thread_command_unregister_trigger(
 
                cds_list_for_each_entry_safe(trigger_element, tmp,
                                &trigger_list->list, node) {
-                       const struct lttng_condition *current_condition =
-                                       lttng_trigger_get_const_condition(
-                                               trigger_element->trigger);
-
-                       assert(current_condition);
-                       if (!lttng_condition_is_equal(condition,
-                                       current_condition)) {
+                       if (!lttng_trigger_is_equal(trigger, trigger_element->trigger)) {
                                continue;
                        }
 
@@ -2226,30 +2764,45 @@ int handle_notification_thread_command_unregister_trigger(
                }
        }
 
-       /*
-        * Remove and release the client list from
-        * notification_trigger_clients_ht.
-        */
-       client_list = get_client_list_from_condition(state, condition);
-       assert(client_list);
+       if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+               struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element;
+               cds_lfht_for_each_entry(state->trigger_tokens_ht, &iter, trigger_tokens_ht_element,
+                               node) {
+                       if (!lttng_trigger_is_equal(trigger, trigger_tokens_ht_element->trigger)) {
+                               continue;
+                       }
 
-       cds_list_for_each_entry_safe(client_list_element, tmp,
-                       &client_list->list, node) {
-               free(client_list_element);
+                       /* TODO talk to all app and remove it */
+                       DBG("[notification-thread] Removed trigger from tokens_ht");
+                       cds_lfht_del(state->trigger_tokens_ht,
+                                       &trigger_tokens_ht_element->node);
+                       call_rcu(&trigger_tokens_ht_element->rcu_node, free_notification_trigger_tokens_ht_element_rcu);
+
+                       break;
+               }
+       }
+
+       if (action_is_notify(action)) {
+               /*
+                * Remove and release the client list from
+                * notification_trigger_clients_ht.
+                */
+               client_list = get_client_list_from_condition(state, condition);
+               assert(client_list);
+
+               /* Put new reference and the hashtable's reference. */
+               notification_client_list_put(client_list);
+               notification_client_list_put(client_list);
+               client_list = NULL;
        }
-       cds_lfht_del(state->notification_trigger_clients_ht,
-                       &client_list->notification_trigger_ht_node);
-       call_rcu(&client_list->rcu_node, free_notification_client_list_rcu);
 
        /* Remove trigger from triggers_ht. */
        trigger_ht_element = caa_container_of(triggers_ht_node,
                        struct lttng_trigger_ht_element, node);
+       cds_lfht_del(state->triggers_by_name_ht, &trigger_ht_element->node_by_name);
        cds_lfht_del(state->triggers_ht, triggers_ht_node);
 
-       condition = lttng_trigger_get_condition(trigger_ht_element->trigger);
-       lttng_condition_destroy(condition);
-       action = lttng_trigger_get_action(trigger_ht_element->trigger);
-       lttng_action_destroy(action);
+       /* Release the ownership of the trigger */
        lttng_trigger_destroy(trigger_ht_element->trigger);
        call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu);
 end:
@@ -2327,6 +2880,47 @@ int handle_notification_thread_command(
                                cmd->parameters.session_rotation.location,
                                &cmd->reply_code);
                break;
+       case NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION:
+               ret = handle_notification_thread_command_add_application(
+                               handle,
+                               state,
+                               cmd->parameters.application.read_side_trigger_event_application_pipe,
+                               &cmd->reply_code);
+               break;
+       case NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION:
+               ret = handle_notification_thread_command_remove_application(
+                               handle,
+                               state,
+                               cmd->parameters.application.read_side_trigger_event_application_pipe,
+                               &cmd->reply_code);
+               break;
+       case NOTIFICATION_COMMAND_TYPE_GET_TOKENS:
+       {
+               struct lttng_triggers *triggers = NULL;
+               ret = handle_notification_thread_command_get_tokens(
+                               handle, state, &triggers, &cmd->reply_code);
+               cmd->reply.get_tokens.triggers = triggers;
+               ret = 0;
+               break;
+
+               cmd->reply_code = LTTNG_OK;
+               ret = 0;
+               break;
+       }
+       case NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS:
+       {
+               struct lttng_triggers *triggers = NULL;
+               ret = handle_notification_thread_command_list_triggers(
+                               handle,
+                               state,
+                               cmd->parameters.list_triggers.uid,
+                               cmd->parameters.list_triggers.gid,
+                               &triggers,
+                               &cmd->reply_code);
+               cmd->reply.list_triggers.triggers = triggers;
+               ret = 0;
+               break;
+       }
        case NOTIFICATION_COMMAND_TYPE_QUIT:
                DBG("[notification-thread] Received quit command");
                cmd->reply_code = LTTNG_OK;
@@ -2342,7 +2936,12 @@ int handle_notification_thread_command(
        }
 end:
        cds_list_del(&cmd->cmd_list_node);
-       lttng_waiter_wake_up(&cmd->reply_waiter);
+       if (cmd->is_async) {
+               free(cmd);
+               cmd = NULL;
+       } else {
+               lttng_waiter_wake_up(&cmd->reply_waiter);
+       }
        pthread_mutex_unlock(&handle->cmd_queue.lock);
        return ret;
 error_unlock:
@@ -2355,12 +2954,6 @@ error:
        return -1;
 }
 
-static
-unsigned long hash_client_socket(int socket)
-{
-       return hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed);
-}
-
 static
 int socket_set_non_blocking(int socket)
 {
@@ -2384,11 +2977,14 @@ end:
        return ret;
 }
 
+/* Client lock must be acquired by caller. */
 static
 int client_reset_inbound_state(struct notification_client *client)
 {
        int ret;
 
+       ASSERT_LOCKED(client->lock);
+
        ret = lttng_dynamic_buffer_set_size(
                        &client->communication.inbound.buffer, 0);
        assert(!ret);
@@ -2419,11 +3015,16 @@ int handle_notification_thread_client_connect(
                ret = -1;
                goto error;
        }
+       pthread_mutex_init(&client->lock, NULL);
+       client->id = state->next_notification_client_id++;
        CDS_INIT_LIST_HEAD(&client->condition_list);
        lttng_dynamic_buffer_init(&client->communication.inbound.buffer);
        lttng_dynamic_buffer_init(&client->communication.outbound.buffer);
        client->communication.inbound.expect_creds = true;
+
+       pthread_mutex_lock(&client->lock);
        ret = client_reset_inbound_state(client);
+       pthread_mutex_unlock(&client->lock);
        if (ret) {
                ERR("[notification-thread] Failed to reset client communication's inbound state");
                ret = 0;
@@ -2467,6 +3068,9 @@ int handle_notification_thread_client_connect(
        cds_lfht_add(state->client_socket_ht,
                        hash_client_socket(client->socket),
                        &client->client_socket_ht_node);
+       cds_lfht_add(state->client_id_ht,
+                       hash_client_id(client->id),
+                       &client->client_id_ht_node);
        rcu_read_unlock();
 
        return ret;
@@ -2475,9 +3079,45 @@ error:
        return ret;
 }
 
-int handle_notification_thread_client_disconnect(
-               int client_socket,
+/* RCU read-lock must be held by the caller. */
+static
+int notification_thread_client_disconnect(
+               struct notification_client *client,
                struct notification_thread_state *state)
+{
+       int ret;
+       struct lttng_condition_list_element *condition_list_element, *tmp;
+
+       /* Acquire the client lock to disable its communication atomically. */
+       pthread_mutex_lock(&client->lock);
+       client->communication.active = false;
+       ret = lttng_poll_del(&state->events, client->socket);
+       if (ret) {
+               ERR("[notification-thread] Failed to remove client socket %d from poll set",
+                               client->socket);
+       }
+       pthread_mutex_unlock(&client->lock);
+
+       cds_lfht_del(state->client_socket_ht, &client->client_socket_ht_node);
+       cds_lfht_del(state->client_id_ht, &client->client_id_ht_node);
+
+       /* Release all conditions to which the client was subscribed. */
+       cds_list_for_each_entry_safe(condition_list_element, tmp,
+                       &client->condition_list, node) {
+               (void) notification_thread_client_unsubscribe(client,
+                               condition_list_element->condition, state, NULL);
+       }
+
+       /*
+        * Client no longer accessible to other threads (through the
+        * client lists).
+        */
+       notification_client_destroy(client, state);
+       return ret;
+}
+
+int handle_notification_thread_client_disconnect(
+               int client_socket, struct notification_thread_state *state)
 {
        int ret = 0;
        struct notification_client *client;
@@ -2494,13 +3134,7 @@ int handle_notification_thread_client_disconnect(
                goto end;
        }
 
-       ret = lttng_poll_del(&state->events, client_socket);
-       if (ret) {
-               ERR("[notification-thread] Failed to remove client socket from poll set");
-       }
-        cds_lfht_del(state->client_socket_ht,
-                       &client->client_socket_ht_node);
-       notification_client_destroy(client, state);
+       ret = notification_thread_client_disconnect(client, state);
 end:
        rcu_read_unlock();
        return ret;
@@ -2516,11 +3150,11 @@ int handle_notification_thread_client_disconnect_all(
        rcu_read_lock();
        DBG("[notification-thread] Closing all client connections");
        cds_lfht_for_each_entry(state->client_socket_ht, &iter, client,
-               client_socket_ht_node) {
+                       client_socket_ht_node) {
                int ret;
 
-               ret = handle_notification_thread_client_disconnect(
-                               client->socket, state);
+               ret = notification_thread_client_disconnect(
+                               client, state);
                if (ret) {
                        error_encoutered = true;
                }
@@ -2550,11 +3184,63 @@ int handle_notification_thread_trigger_unregister_all(
 }
 
 static
-int client_flush_outgoing_queue(struct notification_client *client,
+int client_handle_transmission_status(
+               struct notification_client *client,
+               enum client_transmission_status transmission_status,
                struct notification_thread_state *state)
+{
+       int ret = 0;
+
+       ASSERT_LOCKED(client->lock);
+
+       switch (transmission_status) {
+       case CLIENT_TRANSMISSION_STATUS_COMPLETE:
+               ret = lttng_poll_mod(&state->events, client->socket,
+                               CLIENT_POLL_MASK_IN);
+               if (ret) {
+                       goto end;
+               }
+
+               client->communication.outbound.queued_command_reply = false;
+               client->communication.outbound.dropped_notification = false;
+               break;
+       case CLIENT_TRANSMISSION_STATUS_QUEUED:
+               /*
+                * We want to be notified whenever there is buffer space
+                * available to send the rest of the payload.
+                */
+               ret = lttng_poll_mod(&state->events, client->socket,
+                               CLIENT_POLL_MASK_IN_OUT);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       case CLIENT_TRANSMISSION_STATUS_FAIL:
+               ret = notification_thread_client_disconnect(client, state);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       case CLIENT_TRANSMISSION_STATUS_ERROR:
+               ret = -1;
+               goto end;
+       default:
+               abort();
+       }
+end:
+       return ret;
+}
+
+/* Client lock must be acquired by caller. */
+static
+enum client_transmission_status client_flush_outgoing_queue(
+               struct notification_client *client)
 {
        ssize_t ret;
        size_t to_send_count;
+       enum client_transmission_status status;
+
+       ASSERT_LOCKED(client->lock);
 
        assert(client->communication.outbound.buffer.size != 0);
        to_send_count = client->communication.outbound.buffer.size;
@@ -2580,25 +3266,12 @@ int client_flush_outgoing_queue(struct notification_client *client,
                if (ret) {
                        goto error;
                }
-
-               /*
-                * We want to be notified whenever there is buffer space
-                * available to send the rest of the payload.
-                */
-               ret = lttng_poll_mod(&state->events, client->socket,
-                               CLIENT_POLL_MASK_IN_OUT);
-               if (ret) {
-                       goto error;
-               }
+               status = CLIENT_TRANSMISSION_STATUS_QUEUED;
        } else if (ret < 0) {
                /* Generic error, disconnect the client. */
-               ERR("[notification-thread] Failed to send flush outgoing queue, disconnecting client (socket fd = %i)",
+               ERR("[notification-thread] Failed to flush outgoing queue, disconnecting client (socket fd = %i)",
                                client->socket);
-               ret = handle_notification_thread_client_disconnect(
-                               client->socket, state);
-               if (ret) {
-                       goto error;
-               }
+               status = CLIENT_TRANSMISSION_STATUS_FAIL;
        } else {
                /* No error and flushed the queue completely. */
                ret = lttng_dynamic_buffer_set_size(
@@ -2606,21 +3279,15 @@ int client_flush_outgoing_queue(struct notification_client *client,
                if (ret) {
                        goto error;
                }
-               ret = lttng_poll_mod(&state->events, client->socket,
-                               CLIENT_POLL_MASK_IN);
-               if (ret) {
-                       goto error;
-               }
-
-               client->communication.outbound.queued_command_reply = false;
-               client->communication.outbound.dropped_notification = false;
+               status = CLIENT_TRANSMISSION_STATUS_COMPLETE;
        }
 
-       return 0;
+       return status;
 error:
-       return -1;
+       return CLIENT_TRANSMISSION_STATUS_ERROR;
 }
 
+/* Client lock must be acquired by caller. */
 static
 int client_send_command_reply(struct notification_client *client,
                struct notification_thread_state *state,
@@ -2635,6 +3302,9 @@ int client_send_command_reply(struct notification_client *client,
                .size = sizeof(reply),
        };
        char buffer[sizeof(msg) + sizeof(reply)];
+       enum client_transmission_status transmission_status;
+
+       ASSERT_LOCKED(client->lock);
 
        if (client->communication.outbound.queued_command_reply) {
                /* Protocol error. */
@@ -2650,22 +3320,214 @@ int client_send_command_reply(struct notification_client *client,
                        &client->communication.outbound.buffer,
                        buffer, sizeof(buffer));
        if (ret) {
-               goto error;
+               goto error;
+       }
+
+       transmission_status = client_flush_outgoing_queue(client);
+       ret = client_handle_transmission_status(
+                       client, transmission_status, state);
+       if (ret) {
+               goto error;
+       }
+
+       if (client->communication.outbound.buffer.size != 0) {
+               /* Queue could not be emptied. */
+               client->communication.outbound.queued_command_reply = true;
+       }
+
+       return 0;
+error:
+       return -1;
+}
+
+static
+int client_handle_message_unknown(struct notification_client *client,
+               struct notification_thread_state *state)
+{
+       int ret;
+
+       pthread_mutex_lock(&client->lock);
+
+       /*
+        * Receiving message header. The function will be called again
+        * once the rest of the message as been received and can be
+        * interpreted.
+        */
+       const struct lttng_notification_channel_message *msg;
+
+       assert(sizeof(*msg) == client->communication.inbound.buffer.size);
+       msg = (const struct lttng_notification_channel_message *)
+                             client->communication.inbound.buffer.data;
+
+       if (msg->size == 0 ||
+                       msg->size > DEFAULT_MAX_NOTIFICATION_CLIENT_MESSAGE_PAYLOAD_SIZE) {
+               ERR("[notification-thread] Invalid notification channel message: length = %u",
+                               msg->size);
+               ret = -1;
+               goto end;
+       }
+
+       switch (msg->type) {
+       case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE:
+       case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE:
+       case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE:
+               break;
+       default:
+               ret = -1;
+               ERR("[notification-thread] Invalid notification channel message: unexpected message type");
+               goto end;
+       }
+
+       client->communication.inbound.bytes_to_receive = msg->size;
+       client->communication.inbound.msg_type =
+                       (enum lttng_notification_channel_message_type) msg->type;
+       ret = lttng_dynamic_buffer_set_size(
+                       &client->communication.inbound.buffer, msg->size);
+end:
+       pthread_mutex_unlock(&client->lock);
+       return ret;
+}
+
+static
+int client_handle_message_handshake(struct notification_client *client,
+               struct notification_thread_state *state)
+{
+       int ret;
+       struct lttng_notification_channel_command_handshake *handshake_client;
+       const struct lttng_notification_channel_command_handshake handshake_reply = {
+                       .major = LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR,
+                       .minor = LTTNG_NOTIFICATION_CHANNEL_VERSION_MINOR,
+       };
+       const struct lttng_notification_channel_message msg_header = {
+                       .type = LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE,
+                       .size = sizeof(handshake_reply),
+       };
+       enum lttng_notification_channel_status status =
+                       LTTNG_NOTIFICATION_CHANNEL_STATUS_OK;
+       char send_buffer[sizeof(msg_header) + sizeof(handshake_reply)];
+       enum client_transmission_status transmission_status;
+
+       pthread_mutex_lock(&client->lock);
+
+       memcpy(send_buffer, &msg_header, sizeof(msg_header));
+       memcpy(send_buffer + sizeof(msg_header), &handshake_reply,
+                       sizeof(handshake_reply));
+
+       handshake_client =
+                       (struct lttng_notification_channel_command_handshake *)
+                                       client->communication.inbound.buffer
+                                                       .data;
+       client->major = handshake_client->major;
+       client->minor = handshake_client->minor;
+       if (!client->communication.inbound.creds_received) {
+               ERR("[notification-thread] No credentials received from client");
+               ret = -1;
+               goto end;
+       }
+
+       client->uid = LTTNG_SOCK_GET_UID_CRED(
+                       &client->communication.inbound.creds);
+       client->gid = LTTNG_SOCK_GET_GID_CRED(
+                       &client->communication.inbound.creds);
+       DBG("[notification-thread] Received handshake from client (uid = %u, gid = %u) with version %i.%i",
+                       client->uid, client->gid, (int) client->major,
+                       (int) client->minor);
+
+       if (handshake_client->major !=
+                       LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR) {
+               status = LTTNG_NOTIFICATION_CHANNEL_STATUS_UNSUPPORTED_VERSION;
+       }
+
+       ret = lttng_dynamic_buffer_append(
+                       &client->communication.outbound.buffer, send_buffer,
+                       sizeof(send_buffer));
+       if (ret) {
+               ERR("[notification-thread] Failed to send protocol version to notification channel client");
+               goto end;
+       }
+
+       transmission_status = client_flush_outgoing_queue(client);
+       ret = client_handle_transmission_status(
+                       client, transmission_status, state);
+       if (ret) {
+               goto end;
+       }
+
+       ret = client_send_command_reply(client, state, status);
+       if (ret) {
+               ERR("[notification-thread] Failed to send reply to notification channel client");
+               goto end;
+       }
+
+       /* Set reception state to receive the next message header. */
+       ret = client_reset_inbound_state(client);
+       if (ret) {
+               ERR("[notification-thread] Failed to reset client communication's inbound state");
+               goto end;
+       }
+       client->validated = true;
+       client->communication.active = true;
+
+end:
+       pthread_mutex_unlock(&client->lock);
+       return ret;
+}
+
+static
+int client_handle_message_subscription(
+               struct notification_client *client,
+               enum lttng_notification_channel_message_type msg_type,
+               struct notification_thread_state *state)
+{
+       int ret;
+       struct lttng_condition *condition;
+       enum lttng_notification_channel_status status =
+                       LTTNG_NOTIFICATION_CHANNEL_STATUS_OK;
+       const struct lttng_buffer_view condition_view =
+                       lttng_buffer_view_from_dynamic_buffer(
+                                       &client->communication.inbound.buffer,
+                                       0, -1);
+       size_t expected_condition_size;
+
+       pthread_mutex_lock(&client->lock);
+       expected_condition_size = client->communication.inbound.buffer.size;
+       pthread_mutex_unlock(&client->lock);
+
+       ret = lttng_condition_create_from_buffer(&condition_view, &condition);
+       if (ret != expected_condition_size) {
+               ERR("[notification-thread] Malformed condition received from client");
+               goto end;
+       }
+
+       if (msg_type == LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE) {
+               ret = notification_thread_client_subscribe(
+                               client, condition, state, &status);
+       } else {
+               ret = notification_thread_client_unsubscribe(
+                               client, condition, state, &status);
+       }
+       if (ret) {
+               goto end;
        }
 
-       ret = client_flush_outgoing_queue(client, state);
+       pthread_mutex_lock(&client->lock);
+       ret = client_send_command_reply(client, state, status);
        if (ret) {
-               goto error;
+               ERR("[notification-thread] Failed to send reply to notification channel client");
+               goto end_unlock;
        }
 
-       if (client->communication.outbound.buffer.size != 0) {
-               /* Queue could not be emptied. */
-               client->communication.outbound.queued_command_reply = true;
+       /* Set reception state to receive the next message header. */
+       ret = client_reset_inbound_state(client);
+       if (ret) {
+               ERR("[notification-thread] Failed to reset client communication's inbound state");
+               goto end_unlock;
        }
 
-       return 0;
-error:
-       return -1;
+end_unlock:
+       pthread_mutex_unlock(&client->lock);
+end:
+       return ret;
 }
 
 static
@@ -2687,158 +3549,19 @@ int client_dispatch_message(struct notification_client *client,
        switch (client->communication.inbound.msg_type) {
        case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNKNOWN:
        {
-               /*
-                * Receiving message header. The function will be called again
-                * once the rest of the message as been received and can be
-                * interpreted.
-                */
-               const struct lttng_notification_channel_message *msg;
-
-               assert(sizeof(*msg) ==
-                               client->communication.inbound.buffer.size);
-               msg = (const struct lttng_notification_channel_message *)
-                               client->communication.inbound.buffer.data;
-
-               if (msg->size == 0 || msg->size > DEFAULT_MAX_NOTIFICATION_CLIENT_MESSAGE_PAYLOAD_SIZE) {
-                       ERR("[notification-thread] Invalid notification channel message: length = %u", msg->size);
-                       ret = -1;
-                       goto end;
-               }
-
-               switch (msg->type) {
-               case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE:
-               case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE:
-               case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE:
-                       break;
-               default:
-                       ret = -1;
-                       ERR("[notification-thread] Invalid notification channel message: unexpected message type");
-                       goto end;
-               }
-
-               client->communication.inbound.bytes_to_receive = msg->size;
-               client->communication.inbound.msg_type =
-                               (enum lttng_notification_channel_message_type) msg->type;
-               ret = lttng_dynamic_buffer_set_size(
-                               &client->communication.inbound.buffer, msg->size);
-               if (ret) {
-                       goto end;
-               }
+               ret = client_handle_message_unknown(client, state);
                break;
        }
        case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE:
        {
-               struct lttng_notification_channel_command_handshake *handshake_client;
-               struct lttng_notification_channel_command_handshake handshake_reply = {
-                       .major = LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR,
-                       .minor = LTTNG_NOTIFICATION_CHANNEL_VERSION_MINOR,
-               };
-               struct lttng_notification_channel_message msg_header = {
-                       .type = LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE,
-                       .size = sizeof(handshake_reply),
-               };
-               enum lttng_notification_channel_status status =
-                               LTTNG_NOTIFICATION_CHANNEL_STATUS_OK;
-               char send_buffer[sizeof(msg_header) + sizeof(handshake_reply)];
-
-               memcpy(send_buffer, &msg_header, sizeof(msg_header));
-               memcpy(send_buffer + sizeof(msg_header), &handshake_reply,
-                               sizeof(handshake_reply));
-
-               handshake_client =
-                               (struct lttng_notification_channel_command_handshake *)
-                                       client->communication.inbound.buffer.data;
-               client->major = handshake_client->major;
-               client->minor = handshake_client->minor;
-               if (!client->communication.inbound.creds_received) {
-                       ERR("[notification-thread] No credentials received from client");
-                       ret = -1;
-                       goto end;
-               }
-
-               client->uid = LTTNG_SOCK_GET_UID_CRED(
-                               &client->communication.inbound.creds);
-               client->gid = LTTNG_SOCK_GET_GID_CRED(
-                               &client->communication.inbound.creds);
-               DBG("[notification-thread] Received handshake from client (uid = %u, gid = %u) with version %i.%i",
-                               client->uid, client->gid, (int) client->major,
-                               (int) client->minor);
-
-               if (handshake_client->major != LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR) {
-                       status = LTTNG_NOTIFICATION_CHANNEL_STATUS_UNSUPPORTED_VERSION;
-               }
-
-               ret = lttng_dynamic_buffer_append(&client->communication.outbound.buffer,
-                               send_buffer, sizeof(send_buffer));
-               if (ret) {
-                       ERR("[notification-thread] Failed to send protocol version to notification channel client");
-                       goto end;
-               }
-
-               ret = client_flush_outgoing_queue(client, state);
-               if (ret) {
-                       goto end;
-               }
-
-               ret = client_send_command_reply(client, state, status);
-               if (ret) {
-                       ERR("[notification-thread] Failed to send reply to notification channel client");
-                       goto end;
-               }
-
-               /* Set reception state to receive the next message header. */
-               ret = client_reset_inbound_state(client);
-               if (ret) {
-                       ERR("[notification-thread] Failed to reset client communication's inbound state");
-                       goto end;
-               }
-               client->validated = true;
+               ret = client_handle_message_handshake(client, state);
                break;
        }
        case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE:
        case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE:
        {
-               struct lttng_condition *condition;
-               enum lttng_notification_channel_status status =
-                               LTTNG_NOTIFICATION_CHANNEL_STATUS_OK;
-               const struct lttng_buffer_view condition_view =
-                               lttng_buffer_view_from_dynamic_buffer(
-                                       &client->communication.inbound.buffer,
-                                       0, -1);
-               size_t expected_condition_size =
-                               client->communication.inbound.buffer.size;
-
-               ret = lttng_condition_create_from_buffer(&condition_view,
-                               &condition);
-               if (ret != expected_condition_size) {
-                       ERR("[notification-thread] Malformed condition received from client");
-                       goto end;
-               }
-
-               if (client->communication.inbound.msg_type ==
-                               LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE) {
-                       ret = notification_thread_client_subscribe(client,
-                                       condition, state, &status);
-               } else {
-                       ret = notification_thread_client_unsubscribe(client,
-                                       condition, state, &status);
-               }
-               if (ret) {
-                       goto end;
-               }
-
-               ret = client_send_command_reply(client, state, status);
-               if (ret) {
-                       ERR("[notification-thread] Failed to send reply to notification channel client");
-                       goto end;
-               }
-
-               /* Set reception state to receive the next message header. */
-               ret = client_reset_inbound_state(client);
-               if (ret) {
-                       ERR("[notification-thread] Failed to reset client communication's inbound state");
-                       goto end;
-               }
+               ret = client_handle_message_subscription(client,
+                               client->communication.inbound.msg_type, state);
                break;
        }
        default:
@@ -2856,6 +3579,7 @@ int handle_notification_thread_client_in(
        struct notification_client *client;
        ssize_t recv_ret;
        size_t offset;
+       bool message_is_complete = false;
 
        client = get_client_from_socket(socket, state);
        if (!client) {
@@ -2864,6 +3588,7 @@ int handle_notification_thread_client_in(
                goto end;
        }
 
+       pthread_mutex_lock(&client->lock);
        offset = client->communication.inbound.buffer.size -
                        client->communication.inbound.bytes_to_receive;
        if (client->communication.inbound.expect_creds) {
@@ -2880,12 +3605,17 @@ int handle_notification_thread_client_in(
                                client->communication.inbound.buffer.data + offset,
                                client->communication.inbound.bytes_to_receive);
        }
+       if (recv_ret >= 0) {
+               client->communication.inbound.bytes_to_receive -= recv_ret;
+               message_is_complete = client->communication.inbound
+                                                     .bytes_to_receive == 0;
+       }
+       pthread_mutex_unlock(&client->lock);
        if (recv_ret < 0) {
                goto error_disconnect_client;
        }
 
-       client->communication.inbound.bytes_to_receive -= recv_ret;
-       if (client->communication.inbound.bytes_to_receive == 0) {
+       if (message_is_complete) {
                ret = client_dispatch_message(client, state);
                if (ret) {
                        /*
@@ -2894,13 +3624,11 @@ int handle_notification_thread_client_in(
                         */
                        goto error_disconnect_client;
                }
-       } else {
-               goto end;
        }
 end:
        return ret;
 error_disconnect_client:
-       ret = handle_notification_thread_client_disconnect(socket, state);
+       ret = notification_thread_client_disconnect(client, state);
        return ret;
 }
 
@@ -2910,6 +3638,7 @@ int handle_notification_thread_client_out(
 {
        int ret;
        struct notification_client *client;
+       enum client_transmission_status transmission_status;
 
        client = get_client_from_socket(socket, state);
        if (!client) {
@@ -2918,7 +3647,11 @@ int handle_notification_thread_client_out(
                goto end;
        }
 
-       ret = client_flush_outgoing_queue(client, state);
+       pthread_mutex_lock(&client->lock);
+       transmission_status = client_flush_outgoing_queue(client);
+       ret = client_handle_transmission_status(
+                       client, transmission_status, state);
+       pthread_mutex_unlock(&client->lock);
        if (ret) {
                goto end;
        }
@@ -3089,33 +3822,77 @@ end:
 }
 
 static
-int client_enqueue_dropped_notification(struct notification_client *client,
-               struct notification_thread_state *state)
+int client_notification_overflow(struct notification_client *client)
 {
-       int ret;
-       struct lttng_notification_channel_message msg = {
+       int ret = 0;
+       const struct lttng_notification_channel_message msg = {
                .type = (int8_t) LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION_DROPPED,
-               .size = 0,
        };
 
+       ASSERT_LOCKED(client->lock);
+
+       DBG("Dropping notification addressed to client (socket fd = %i)",
+                       client->socket);
+       if (client->communication.outbound.dropped_notification) {
+               /*
+                * The client already has a "notification dropped" message
+                * in its outgoing queue. Nothing to do since all
+                * of those messages are coalesced.
+                */
+               goto end;
+       }
+
+       client->communication.outbound.dropped_notification = true;
        ret = lttng_dynamic_buffer_append(
                        &client->communication.outbound.buffer, &msg,
                        sizeof(msg));
+       if (ret) {
+               PERROR("Failed to enqueue \"dropped notification\" message in client's (socket fd = %i) outgoing queue",
+                               client->socket);
+       }
+end:
        return ret;
 }
 
+static int client_handle_transmission_status_wrapper(
+               struct notification_client *client,
+               enum client_transmission_status status,
+               void *user_data)
+{
+       return client_handle_transmission_status(client, status,
+                       (struct notification_thread_state *) user_data);
+}
+
 static
 int send_evaluation_to_clients(const struct lttng_trigger *trigger,
                const struct lttng_evaluation *evaluation,
                struct notification_client_list* client_list,
                struct notification_thread_state *state,
-               uid_t channel_uid, gid_t channel_gid)
+               uid_t object_uid, gid_t object_gid)
+{
+       return notification_client_list_send_evaluation(client_list,
+                       lttng_trigger_get_const_condition(trigger), evaluation,
+                       lttng_trigger_get_credentials(trigger),
+                       &(struct lttng_credentials){
+                                       .uid = object_uid, .gid = object_gid},
+                       client_handle_transmission_status_wrapper, state);
+}
+
+LTTNG_HIDDEN
+int notification_client_list_send_evaluation(
+               struct notification_client_list *client_list,
+               const struct lttng_condition *condition,
+               const struct lttng_evaluation *evaluation,
+               const struct lttng_credentials *trigger_creds,
+               const struct lttng_credentials *source_object_creds,
+               report_client_transmission_result_cb client_report,
+               void *user_data)
 {
        int ret = 0;
        struct lttng_dynamic_buffer msg_buffer;
        struct notification_client_list_element *client_list_element, *tmp;
        const struct lttng_notification notification = {
-               .condition = (struct lttng_condition *) lttng_trigger_get_const_condition(trigger),
+               .condition = (struct lttng_condition *) condition,
                .evaluation = (struct lttng_evaluation *) evaluation,
        };
        struct lttng_notification_channel_message msg_header = {
@@ -3141,16 +3918,40 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger,
        ((struct lttng_notification_channel_message * ) msg_buffer.data)->size =
                        (uint32_t) (msg_buffer.size - sizeof(msg_header));
 
+       pthread_mutex_lock(&client_list->lock);
        cds_list_for_each_entry_safe(client_list_element, tmp,
                        &client_list->list, node) {
+               enum client_transmission_status transmission_status;
                struct notification_client *client =
                                client_list_element->client;
 
-               if (client->uid != channel_uid && client->gid != channel_gid &&
-                               client->uid != 0) {
-                       /* Client is not allowed to monitor this channel. */
-                       DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this channel");
-                       continue;
+               ret = 0;
+               pthread_mutex_lock(&client->lock);
+               if (source_object_creds) {
+                       if (client->uid != source_object_creds->uid &&
+                                       client->gid != source_object_creds->gid &&
+                                       client->uid != 0) {
+                               /*
+                                * Client is not allowed to monitor this
+                                * object.
+                                */
+                               DBG("[notification-thread] Skipping client at it does not have the object permission to receive notification for this trigger");
+                               goto unlock_client;
+                       }
+               }
+
+               /* TODO: what is the behavior for root client on non root
+                * trigger? Since multiple triggers (different user) can have the same condition
+                * but with different action group that can have each a notify.
+                * Does the root client receive multiple notification for all
+                * those triggers with the same condition or only notification
+                * for triggers the root user configured?
+                * For now we do the later. All users including the root user
+                * can only receive notification from trigger it registered.
+                */
+               if (client->uid != trigger_creds->uid && client->gid != trigger_creds->gid) {
+                       DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this trigger");
+                       goto unlock_client;
                }
 
                DBG("[notification-thread] Sending notification to client (fd = %i, %zu bytes)",
@@ -3163,37 +3964,302 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger,
                         * notification since the socket spilled-over to the
                         * queue.
                         */
-                       DBG("[notification-thread] Dropping notification addressed to client (socket fd = %i)",
-                                       client->socket);
-                       if (!client->communication.outbound.dropped_notification) {
-                               client->communication.outbound.dropped_notification = true;
-                               ret = client_enqueue_dropped_notification(
-                                               client, state);
-                               if (ret) {
-                                       goto end;
-                               }
+                       ret = client_notification_overflow(client);
+                       if (ret) {
+                               goto unlock_client;
                        }
-                       continue;
                }
 
                ret = lttng_dynamic_buffer_append_buffer(
                                &client->communication.outbound.buffer,
                                &msg_buffer);
                if (ret) {
-                       goto end;
+                       goto unlock_client;
                }
 
-               ret = client_flush_outgoing_queue(client, state);
+               transmission_status = client_flush_outgoing_queue(client);
+               ret = client_report(client, transmission_status, user_data);
                if (ret) {
-                       goto end;
+                       goto unlock_client;
+               }
+unlock_client:
+               pthread_mutex_unlock(&client->lock);
+               if (ret) {
+                       goto end_unlock_list;
                }
        }
        ret = 0;
+
+end_unlock_list:
+       pthread_mutex_unlock(&client_list->lock);
 end:
        lttng_dynamic_buffer_reset(&msg_buffer);
        return ret;
 }
 
+int perform_event_action_notify(struct notification_thread_state *state,
+               const struct lttng_trigger *trigger,
+               const struct lttng_trigger_notification *notification,
+               const struct lttng_action *action)
+{
+       int ret;
+       struct notification_client_list *client_list;
+       struct lttng_evaluation *evaluation = NULL;
+       const struct lttng_credentials *creds = lttng_trigger_get_credentials(trigger);
+
+       /*
+        * Check if any client is subscribed to the result of this
+        * evaluation.
+        */
+       client_list = get_client_list_from_condition(state, trigger->condition);
+       assert(client_list);
+       if (cds_list_empty(&client_list->list)) {
+               ret = 0;
+               goto end;
+       }
+
+       evaluation = lttng_evaluation_event_rule_create(trigger->name);
+       if (!evaluation) {
+               ERR("Failed to create event rule hit evaluation");
+               ret = -1;
+               goto end;
+       }
+
+       /* Dispatch evaluation result to all clients.
+        * Note that here the passed credentials are the one from the trigger,
+        * this is because there is no internal object binded to the trigger per
+        * see and the credential validation is done at the registration level
+        * for the event rule based trigger. For a channel the credential
+        * validation can only be done on notify since the trigger can be
+        * registered before the channel/session creation.
+        */
+       ret = send_evaluation_to_clients(trigger,
+                       evaluation, client_list, state,
+                       creds->uid,
+                       creds->gid);
+       lttng_evaluation_destroy(evaluation);
+       if (caa_unlikely(ret)) {
+               goto end;
+       }
+end:
+       return ret;
+
+}
+
+/* This can be called recursively, pass NULL for action on the first iteration */
+int perform_event_action(struct notification_thread_state *state,
+               const struct lttng_trigger *trigger,
+               const struct lttng_trigger_notification *notification,
+               const struct lttng_action *action)
+{
+       int ret = 0;
+       enum lttng_action_type action_type;
+
+       assert(trigger);
+
+       if (!action) {
+               action = lttng_trigger_get_const_action(trigger);
+       }
+
+       action_type = lttng_action_get_type_const(action);
+       DBG("Handling action %s for trigger id %s (%" PRIu64 ")",
+                       lttng_action_type_string(action_type), trigger->name,
+                       trigger->key.value);
+
+       switch (action_type) {
+       case LTTNG_ACTION_TYPE_GROUP:
+       {
+               /* Recurse into the group */
+               const struct lttng_action *tmp = NULL;
+               unsigned int count = 0;
+               (void) lttng_action_group_get_count(action, &count);
+               for (int i = 0; i < count; i++) {
+                       tmp = lttng_action_group_get_at_index_const(action, i);
+                       assert(tmp);
+                       ret = perform_event_action(state, trigger, notification, tmp);
+                       if (ret < 0) {
+                               goto end;
+                       }
+               }
+               break;
+       }
+       case LTTNG_ACTION_TYPE_NOTIFY:
+       {
+               ret = perform_event_action_notify(state, trigger, notification, action);
+               break;
+       }
+       default:
+               break;
+       }
+end:
+       return ret;
+}
+
+int handle_notification_thread_event(struct notification_thread_state *state,
+               int pipe,
+               enum lttng_domain_type domain)
+{
+       int ret;
+       struct lttng_ust_trigger_notification ust_notification;
+       uint64_t kernel_notification;
+       struct cds_lfht_node *node;
+       struct cds_lfht_iter iter;
+       struct notification_trigger_tokens_ht_element *element;
+       struct lttng_trigger_notification notification;
+       void *reception_buffer;
+       size_t reception_size;
+       enum action_executor_status executor_status;
+       struct notification_client_list *client_list = NULL;
+
+       notification.type = domain;
+
+       switch(domain) {
+       case LTTNG_DOMAIN_UST:
+               reception_buffer = (void *) &ust_notification;
+               reception_size = sizeof(ust_notification);
+               notification.u.ust = &ust_notification;
+               break;
+       case LTTNG_DOMAIN_KERNEL:
+               reception_buffer = (void *) &kernel_notification;
+               reception_size = sizeof(kernel_notification);
+               notification.u.kernel = &kernel_notification;
+               break;
+       default:
+               assert(0);
+       }
+
+       /*
+        * The monitoring pipe only holds messages smaller than PIPE_BUF,
+        * ensuring that read/write of sampling messages are atomic.
+        */
+       /* TODO: should we read as much as we can ? EWOULDBLOCK? */
+
+       ret = lttng_read(pipe, reception_buffer, reception_size);
+       if (ret != reception_size) {
+               ERR("[notification-thread] Failed to read from event source pipe (fd = %i)",
+                               pipe);
+               /* TODO: Should this error out completly.
+                * This can happen when an app is killed as of today
+                * ret = -1 cause the whole thread to die and fuck up
+                * everything.
+                */
+               goto end;
+       }
+
+       switch(domain) {
+       case LTTNG_DOMAIN_UST:
+               notification.id = ust_notification.id;
+               break;
+       case LTTNG_DOMAIN_KERNEL:
+               notification.id = kernel_notification;
+               break;
+       default:
+               assert(0);
+       }
+
+       /* Find triggers associated with this token. */
+       rcu_read_lock();
+       cds_lfht_lookup(state->trigger_tokens_ht,
+                       hash_key_u64(&notification.id, lttng_ht_seed), match_trigger_token,
+                       &notification.id, &iter);
+       node = cds_lfht_iter_get_node(&iter);
+       if (caa_unlikely(!node)) {
+               /* TODO: is this an error? This might happen if the receive side
+                * is slow to process event from source and that the trigger was
+                * removed but the app still kicking. This yield another
+                * question on the trigger lifetime and when we can remove a
+                * trigger. How to guarantee that all event with the token idea
+                * have be processed? Do we want to provide this guarantee?
+                *
+                * Update: I have encountered this when using a trigger on
+                * sched_switch and then removing it. The frequency is quite
+                * high hence we en up exactly in the mentionned scenario.
+                * AFAIK this might be the best way to handle this.
+                */
+               ret = 0;
+               goto end_unlock;
+       }
+       element = caa_container_of(node,
+                       struct notification_trigger_tokens_ht_element,
+                       node);
+
+       if (!lttng_trigger_is_ready_to_fire(element->trigger)) {
+               ret = 0;
+               goto end_unlock;
+       }
+
+       client_list = get_client_list_from_condition(state,
+                       lttng_trigger_get_const_condition(element->trigger));
+       executor_status = action_executor_enqueue(
+                       state->executor, element->trigger, client_list);
+       switch (executor_status) {
+       case ACTION_EXECUTOR_STATUS_OK:
+               ret = 0;
+               break;
+       case ACTION_EXECUTOR_STATUS_OVERFLOW:
+       {
+               struct notification_client_list_element *client_list_element,
+                               *tmp;
+
+               /*
+                * Not a fatal error; this is expected and simply means the
+                * executor has too much work queued already.
+                */
+               ret = 0;
+
+               if (!client_list) {
+                       break;
+               }
+
+               /* Warn clients that a notification (or more) was dropped. */
+               pthread_mutex_lock(&client_list->lock);
+               cds_list_for_each_entry_safe(client_list_element, tmp,
+                               &client_list->list, node) {
+                       enum client_transmission_status transmission_status;
+                       struct notification_client *client =
+                                       client_list_element->client;
+
+                       pthread_mutex_lock(&client->lock);
+                       ret = client_notification_overflow(client);
+                       if (ret) {
+                               /* Fatal error. */
+                               goto next_client;
+                       }
+
+                       transmission_status =
+                                       client_flush_outgoing_queue(client);
+                       ret = client_handle_transmission_status(
+                                       client, transmission_status, state);
+                       if (ret) {
+                               /* Fatal error. */
+                               goto next_client;
+                       }
+next_client:
+                       pthread_mutex_unlock(&client->lock);
+                       if (ret) {
+                               break;
+                       }
+               }
+               pthread_mutex_lock(&client_list->lock);
+               break;
+       }
+       case ACTION_EXECUTOR_STATUS_ERROR:
+               /* Fatal error, shut down everything. */
+               ERR("Fatal error encoutered while enqueuing action");
+               ret = -1;
+               goto end_unlock;
+       default:
+               /* Unhandled error. */
+               abort();
+       }
+
+end_unlock:
+       notification_client_list_put(client_list);
+       rcu_read_unlock();
+end:
+       return ret;
+}
+
 int handle_notification_thread_channel_sample(
                struct notification_thread_state *state, int pipe,
                enum lttng_domain_type domain)
@@ -3332,16 +4398,23 @@ int handle_notification_thread_channel_sample(
                        node) {
                const struct lttng_condition *condition;
                const struct lttng_action *action;
-               const struct lttng_trigger *trigger;
-               struct notification_client_list *client_list;
+               struct lttng_trigger *trigger;
+               struct notification_client_list *client_list = NULL;
                struct lttng_evaluation *evaluation = NULL;
+               bool client_list_is_empty;
 
+               ret = 0;
                trigger = trigger_list_element->trigger;
                condition = lttng_trigger_get_const_condition(trigger);
                assert(condition);
                action = lttng_trigger_get_const_action(trigger);
+               
+               if (!lttng_trigger_is_ready_to_fire(trigger)) {
+                       goto put_list;
+               }
 
                /* Notify actions are the only type currently supported. */
+               /* TODO support other type of action */
                assert(lttng_action_get_type_const(action) ==
                                LTTNG_ACTION_TYPE_NOTIFY);
 
@@ -3351,12 +4424,13 @@ int handle_notification_thread_channel_sample(
                 */
                client_list = get_client_list_from_condition(state, condition);
                assert(client_list);
-               if (cds_list_empty(&client_list->list)) {
+               client_list_is_empty = cds_list_empty(&client_list->list);
+               if (client_list_is_empty) {
                        /*
                         * No clients interested in the evaluation's result,
                         * skip it.
                         */
-                       continue;
+                       goto put_list;
                }
 
                ret = evaluate_buffer_condition(condition, &evaluation, state,
@@ -3366,11 +4440,11 @@ int handle_notification_thread_channel_sample(
                                latest_session_consumed_total,
                                channel_info);
                if (caa_unlikely(ret)) {
-                       goto end_unlock;
+                       goto put_list;
                }
 
                if (caa_likely(!evaluation)) {
-                       continue;
+                       goto put_list;
                }
 
                /* Dispatch evaluation result to all clients. */
@@ -3379,8 +4453,10 @@ int handle_notification_thread_channel_sample(
                                channel_info->session_info->uid,
                                channel_info->session_info->gid);
                lttng_evaluation_destroy(evaluation);
+put_list:
+               notification_client_list_put(client_list);
                if (caa_unlikely(ret)) {
-                       goto end_unlock;
+                       break;
                }
        }
 end_unlock:
index 2f699bf457cef13feb26d272787aa83550eac3ba..7affb752da9679467e16061078ff507b26f40bd7 100644 (file)
@@ -44,4 +44,8 @@ int handle_notification_thread_channel_sample(
                struct notification_thread_state *state, int pipe,
                enum lttng_domain_type domain);
 
+int handle_notification_thread_event(
+               struct notification_thread_state *state, int pipe,
+               enum lttng_domain_type domain);
+
 #endif /* NOTIFICATION_THREAD_EVENTS_H */
index 5242695f05205171f3e77152841bb6c0df0a1d2e..fc0e8858fdb983e3d8fb77e93c320ffb8fee9d16 100644 (file)
@@ -8,9 +8,13 @@
 #ifndef NOTIFICATION_THREAD_INTERNAL_H
 #define NOTIFICATION_THREAD_INTERNAL_H
 
+#include <common/credentials.h>
+#include <lttng/notification/channel-internal.h>
 #include <lttng/ref-internal.h>
-#include <urcu/rculfhash.h>
+#include <stdbool.h>
 #include <unistd.h>
+#include <urcu/rculfhash.h>
+#include <urcu/ref.h>
 
 struct channel_key {
        uint64_t key;
@@ -64,4 +68,150 @@ struct channel_info {
        struct rcu_head rcu_node;
 };
 
+struct notification_client_list_element {
+       struct notification_client *client;
+       struct cds_list_head node;
+};
+
+/*
+ * Thread safety of notification_client and notification_client_list.
+ *
+ * The notification thread (main thread) and the action executor
+ * interact through client lists. Hence, when the action executor
+ * thread looks-up the list of clients subscribed to a given
+ * condition, it will acquire a reference to the list and lock it
+ * while attempting to communicate with the various clients.
+ *
+ * It is not necessary to reference-count clients as they are guaranteed
+ * to be 'alive' if they are present in a list and that list is locked. Indeed,
+ * removing references to the client from those subscription lists is part of
+ * the work performed on destruction of a client.
+ *
+ * No provision for other access scenarios are taken into account;
+ * this is the bare minimum to make these accesses safe and the
+ * notification thread's state is _not_ "thread-safe" in any general
+ * sense.
+ */
+struct notification_client_list {
+       pthread_mutex_t lock;
+       struct urcu_ref ref;
+       const struct lttng_trigger *trigger;
+       struct cds_list_head list;
+       /* Weak reference to container. */
+       struct cds_lfht *notification_trigger_clients_ht;
+       struct cds_lfht_node notification_trigger_clients_ht_node;
+       /* call_rcu delayed reclaim. */
+       struct rcu_head rcu_node;
+};
+
+struct notification_client {
+       /* Nests within the notification_client_list lock. */
+       pthread_mutex_t lock;
+       notification_client_id id;
+       int socket;
+       /* Client protocol version. */
+       uint8_t major, minor;
+       uid_t uid;
+       gid_t gid;
+       /*
+        * Indicates if the credentials and versions of the client have been
+        * checked.
+        */
+       bool validated;
+       /*
+        * Conditions to which the client's notification channel is subscribed.
+        * List of struct lttng_condition_list_node. The condition member is
+        * owned by the client.
+        */
+       struct cds_list_head condition_list;
+       struct cds_lfht_node client_socket_ht_node;
+       struct cds_lfht_node client_id_ht_node;
+       struct {
+               /*
+                * If a client's communication is inactive, it means that a
+                * fatal error has occurred (could be either a protocol error or
+                * the socket API returned a fatal error). No further
+                * communication should be attempted; the client is queued for
+                * clean-up.
+                */
+               bool active;
+               struct {
+                       /*
+                        * During the reception of a message, the reception
+                        * buffers' "size" is set to contain the current
+                        * message's complete payload.
+                        */
+                       struct lttng_dynamic_buffer buffer;
+                       /* Bytes left to receive for the current message. */
+                       size_t bytes_to_receive;
+                       /* Type of the message being received. */
+                       enum lttng_notification_channel_message_type msg_type;
+                       /*
+                        * Indicates whether or not credentials are expected
+                        * from the client.
+                        */
+                       bool expect_creds;
+                       /*
+                        * Indicates whether or not credentials were received
+                        * from the client.
+                        */
+                       bool creds_received;
+                       /* Only used during credentials reception. */
+                       lttng_sock_cred creds;
+               } inbound;
+               struct {
+                       /*
+                        * Indicates whether or not a notification addressed to
+                        * this client was dropped because a command reply was
+                        * already buffered.
+                        *
+                        * A notification is dropped whenever the buffer is not
+                        * empty.
+                        */
+                       bool dropped_notification;
+                       /*
+                        * Indicates whether or not a command reply is already
+                        * buffered. In this case, it means that the client is
+                        * not consuming command replies before emitting a new
+                        * one. This could be caused by a protocol error or a
+                        * misbehaving/malicious client.
+                        */
+                       bool queued_command_reply;
+                       struct lttng_dynamic_buffer buffer;
+               } outbound;
+       } communication;
+       /* call_rcu delayed reclaim. */
+       struct rcu_head rcu_node;
+};
+
+enum client_transmission_status {
+       CLIENT_TRANSMISSION_STATUS_COMPLETE,
+       CLIENT_TRANSMISSION_STATUS_QUEUED,
+       /* Communication failure. */
+       CLIENT_TRANSMISSION_STATUS_FAIL,
+       /* Fatal error. */
+       CLIENT_TRANSMISSION_STATUS_ERROR,
+};
+
+LTTNG_HIDDEN
+bool notification_client_list_get(struct notification_client_list *list);
+
+LTTNG_HIDDEN
+void notification_client_list_put(struct notification_client_list *list);
+
+typedef int (*report_client_transmission_result_cb)(
+               struct notification_client *client,
+               enum client_transmission_status status,
+               void *user_data);
+
+LTTNG_HIDDEN
+int notification_client_list_send_evaluation(
+               struct notification_client_list *list,
+               const struct lttng_condition *condition,
+               const struct lttng_evaluation *evaluation,
+               const struct lttng_credentials *trigger_creds,
+               const struct lttng_credentials *source_object_creds,
+               report_client_transmission_result_cb client_report,
+               void *user_data);
+
 #endif /* NOTIFICATION_THREAD_INTERNAL_H */
index 7dadb2c6748a7ae6738dad9294144407996a4d0d..a52756c5a387ea023ed8ad34f30ab2a4fb0a314f 100644 (file)
@@ -29,6 +29,9 @@
 #include "health-sessiond.h"
 #include "thread.h"
 
+#include "kernel.h"
+#include <common/kernel-ctl/kernel-ctl.h>
+
 #include <urcu.h>
 #include <urcu/list.h>
 #include <urcu/rculfhash.h>
@@ -70,14 +73,27 @@ void notification_thread_handle_destroy(
                        PERROR("close kernel consumer channel monitoring pipe");
                }
        }
+
+       /* TODO: refactor this if needed. Lifetime of the kernel notification
+        * event source.
+        * event_trigger_sources.kernel_tracer is owned by the main thread and
+        * is closed at this point.
+        */
+       handle->event_trigger_sources.kernel_tracer = -1;
 end:
        free(handle);
 }
 
+/*
+ * TODO: refactor this if needed. Lifetime of the kernel notification event source.
+ * The kernel_notification_monitor_fd ownwership remain to the main thread.
+ * This is because we need to close this fd before removing the modules.
+ */
 struct notification_thread_handle *notification_thread_handle_create(
                struct lttng_pipe *ust32_channel_monitor_pipe,
                struct lttng_pipe *ust64_channel_monitor_pipe,
-               struct lttng_pipe *kernel_channel_monitor_pipe)
+               struct lttng_pipe *kernel_channel_monitor_pipe,
+               int kernel_notification_monitor_fd)
 {
        int ret;
        struct notification_thread_handle *handle;
@@ -88,6 +104,8 @@ struct notification_thread_handle *notification_thread_handle_create(
                goto end;
        }
 
+       handle->event_trigger_sources.kernel_tracer = -1;
+
        sem_init(&handle->ready, 0, 0);
 
        event_pipe = lttng_pipe_open(FD_CLOEXEC);
@@ -135,6 +153,14 @@ struct notification_thread_handle *notification_thread_handle_create(
        } else {
                handle->channel_monitoring_pipes.kernel_consumer = -1;
        }
+
+       handle->event_trigger_sources.kernel_tracer = kernel_notification_monitor_fd;
+
+       CDS_INIT_LIST_HEAD(&handle->event_trigger_sources.list);
+       ret = pthread_mutex_init(&handle->event_trigger_sources.lock, NULL);
+       if (ret) {
+               goto error;
+       }
 end:
        return handle;
 error:
@@ -266,14 +292,15 @@ int init_poll_set(struct lttng_poll_event *poll_set,
        int ret;
 
        /*
-        * Create pollset with size 5:
+        * Create pollset with size 6:
         *      - notification channel socket (listen for new connections),
         *      - command queue event fd (internal sessiond commands),
         *      - consumerd (32-bit user space) channel monitor pipe,
         *      - consumerd (64-bit user space) channel monitor pipe,
         *      - consumerd (kernel) channel monitor pipe.
+        *      - kernel trigger event pipe,
         */
-       ret = lttng_poll_create(poll_set, 5, LTTNG_CLOEXEC);
+       ret = lttng_poll_create(poll_set, 6, LTTNG_CLOEXEC);
        if (ret < 0) {
                goto end;
        }
@@ -305,7 +332,7 @@ int init_poll_set(struct lttng_poll_event *poll_set,
                goto error;
        }
        if (handle->channel_monitoring_pipes.kernel_consumer < 0) {
-               goto end;
+               goto skip_kernel_consumer;
        }
        ret = lttng_poll_add(poll_set,
                        handle->channel_monitoring_pipes.kernel_consumer,
@@ -314,6 +341,19 @@ int init_poll_set(struct lttng_poll_event *poll_set,
                ERR("[notification-thread] Failed to add kernel channel monitoring pipe fd to pollset");
                goto error;
        }
+
+skip_kernel_consumer:
+       if (handle->event_trigger_sources.kernel_tracer < 0) {
+               goto end;
+       }
+
+       ret = lttng_poll_add(poll_set,
+                       handle->event_trigger_sources.kernel_tracer,
+                       LPOLLIN | LPOLLERR);
+       if (ret < 0) {
+               ERR("[notification-thread] Failed to add kernel trigger notification monitoring pipe fd to pollset");
+               goto error;
+       }
 end:
        return ret;
 error:
@@ -332,6 +372,10 @@ void fini_thread_state(struct notification_thread_state *state)
                ret = cds_lfht_destroy(state->client_socket_ht, NULL);
                assert(!ret);
        }
+       if (state->client_id_ht) {
+               ret = cds_lfht_destroy(state->client_id_ht, NULL);
+               assert(!ret);
+       }
        if (state->triggers_ht) {
                ret = handle_notification_thread_trigger_unregister_all(state);
                assert(!ret);
@@ -359,6 +403,14 @@ void fini_thread_state(struct notification_thread_state *state)
                ret = cds_lfht_destroy(state->sessions_ht, NULL);
                assert(!ret);
        }
+       if (state->trigger_tokens_ht) {
+               ret = cds_lfht_destroy(state->trigger_tokens_ht, NULL);
+               assert(!ret);
+       }
+       if (state->triggers_by_name_ht) {
+               ret = cds_lfht_destroy(state->triggers_by_name_ht, NULL);
+               assert(!ret);
+       }
        /*
         * Must be destroyed after all channels have been destroyed.
         * See comment in struct lttng_session_trigger_list.
@@ -371,6 +423,9 @@ void fini_thread_state(struct notification_thread_state *state)
                notification_channel_socket_destroy(
                                state->notification_channel_socket);
        }
+       if (state->executor) {
+               action_executor_destroy(state->executor);
+       }
        lttng_poll_clean(&state->events);
 }
 
@@ -397,6 +452,7 @@ int init_thread_state(struct notification_thread_handle *handle,
 
        memset(state, 0, sizeof(*state));
        state->notification_channel_socket = -1;
+       state->trigger_id.token_generator = 1;
        lttng_poll_init(&state->events);
 
        ret = notification_channel_socket_create();
@@ -424,6 +480,12 @@ int init_thread_state(struct notification_thread_handle *handle,
                goto error;
        }
 
+       state->client_id_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
+                       CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+       if (!state->client_id_ht) {
+               goto error;
+       }
+
        state->channel_triggers_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
                        CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
        if (!state->channel_triggers_ht) {
@@ -463,6 +525,20 @@ int init_thread_state(struct notification_thread_handle *handle,
        if (!state->triggers_ht) {
                goto error;
        }
+       state->trigger_tokens_ht = cds_lfht_new(DEFAULT_HT_SIZE,
+                       1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+       if (!state->trigger_tokens_ht) {
+               goto error;
+       }
+       state->triggers_by_name_ht = cds_lfht_new(DEFAULT_HT_SIZE,
+                       1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+       if (!state->triggers_by_name_ht) {
+               goto error;
+       }
+       state->executor = action_executor_create(handle);
+       if (!state->executor) {
+               goto error;
+       }
        mark_thread_as_ready(handle);
 end:
        return 0;
@@ -507,6 +583,54 @@ end:
        return ret;
 }
 
+static int handle_trigger_event_pipe(int fd,
+               uint32_t revents,
+               struct notification_thread_handle *handle,
+               struct notification_thread_state *state)
+{
+       int ret = 0;
+       enum lttng_domain_type domain;
+
+       if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+               ret = lttng_poll_del(&state->events, fd);
+               if (ret) {
+                       ERR("[notification-thread] Failed to remove event monitoring pipe from poll set");
+               }
+               goto end;
+       }
+
+       if (fd == handle->event_trigger_sources.kernel_tracer) {
+               domain = LTTNG_DOMAIN_KERNEL;
+       } else {
+               domain = LTTNG_DOMAIN_UST;
+       }
+
+       ret = handle_notification_thread_event(state, fd, domain);
+       if (ret) {
+               ERR("[notification-thread] Event sample handling error occurred for fd: %d", fd);
+               ret = -1;
+               goto end;
+       }
+end:
+       return ret;
+}
+
+static bool fd_is_event_source(struct notification_thread_handle *handle, int fd)
+{
+       struct notification_event_trigger_source_element *source_element, *tmp;
+       cds_list_for_each_entry_safe(source_element, tmp,
+                       &handle->event_trigger_sources.list, node) {
+               if (source_element->fd != fd) {
+                       continue;
+               }
+               return true;
+       }
+       if (fd == handle->event_trigger_sources.kernel_tracer) {
+               return true;
+       }
+       return false;
+}
+
 /*
  * This thread services notification channel clients and commands received
  * from various lttng-sessiond components over a command queue.
@@ -594,6 +718,11 @@ void *thread_notification(void *data)
                                if (ret) {
                                        goto error;
                                }
+                       } else if (fd_is_event_source(handle, fd)) {
+                               ret = handle_trigger_event_pipe(fd, revents, handle, &state);
+                               if (ret) {
+                                       goto error;
+                               }
                        } else {
                                /* Activity on a client's socket. */
                                if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
index 3d766780e6fcc8a35e54cf53553cd7fdf6d19e60..e23cc1a32e2e320f34c796feae8568b01ccd4676 100644 (file)
@@ -8,16 +8,32 @@
 #ifndef NOTIFICATION_THREAD_H
 #define NOTIFICATION_THREAD_H
 
-#include <urcu/list.h>
-#include <urcu.h>
-#include <urcu/rculfhash.h>
-#include <lttng/trigger/trigger.h>
-#include <common/pipe.h>
+#include "action-executor.h"
+#include "thread.h"
 #include <common/compat/poll.h>
 #include <common/hashtable/hashtable.h>
+#include <common/pipe.h>
+#include <lttng/trigger/trigger.h>
 #include <pthread.h>
 #include <semaphore.h>
-#include "thread.h"
+#include <urcu.h>
+#include <urcu/list.h>
+#include <urcu/rculfhash.h>
+
+typedef uint64_t notification_client_id;
+
+struct notification_event_trigger_source_element {
+       int fd;
+       struct cds_list_head node;
+};
+
+struct notification_trigger_tokens_ht_element {
+    uint64_t token;
+    struct lttng_trigger *trigger;
+    struct cds_lfht_node node;
+    /* call_rcu delayed reclaim. */
+    struct rcu_head rcu_node;
+};
 
 struct notification_thread_handle {
        /*
@@ -39,8 +55,20 @@ struct notification_thread_handle {
                int ust64_consumer;
                int kernel_consumer;
        } channel_monitoring_pipes;
+       /*
+        * Read side of pipes used to reveice event trigger generetated by
+        * registered applications
+        */
+       struct {
+               /* List of notification_event_trigger_source_element */
+               struct cds_list_head list;
+               pthread_mutex_t lock;
+               int kernel_tracer;
+       } event_trigger_sources;
        /* Used to wait for the launch of the notification thread. */
        sem_t ready;
+
+       pthread_mutex_t trigger_lock;
 };
 
 /**
@@ -49,9 +77,14 @@ struct notification_thread_handle {
  * In order to speed-up and simplify queries, hash tables providing the
  * following associations are maintained:
  *
- *   - client_socket_ht: associate a client's socket (fd) to its "struct client"
- *             This hash table owns the "struct client" which must thus be
- *             disposed-of on removal from the hash table.
+ *   - client_socket_ht: associate a client's socket (fd) to its
+ *             "struct notification_client".
+ *             This hash table owns the "struct notification_client" which must
+ *             thus be disposed-of on removal from the hash table.
+ *
+ *   - client_id_ht: associate a client's id to its "struct notification_client"
+ *             This hash table holds a _weak_ reference to the
+ *             "struct notification_client".
  *
  *   - channel_triggers_ht:
  *             associates a channel key to a list of
@@ -99,9 +132,13 @@ struct notification_thread_handle {
  *             channels through their struct channel_info (ref-counting is used).
  *
  *   - triggers_ht:
- *             associates a condition to a struct lttng_trigger_ht_element.
+ *             associates a trigger to a struct lttng_trigger_ht_element.
  *             The hash table holds the ownership of the
  *             lttng_trigger_ht_elements along with the triggers themselves.
+ *   - triggers_by_name_ht:
+ *             associates a trigger name to a struct lttng_trigger_ht_element.
+ *             The hash table does not hold any ownership and is used strictly
+ *             for lookup on registration.
  *
  * The thread reacts to the following internal events:
  *   1) creation of a tracing channel,
@@ -145,6 +182,7 @@ struct notification_thread_handle {
  *          notification_trigger_clients_ht,
  *    - add trigger to channel_triggers_ht (if applicable),
  *    - add trigger to session_triggers_ht (if applicable),
+ *    - add trigger to triggers_by_name_ht
  *    - add trigger to triggers_ht
  *    - evaluate the trigger's condition right away to react if that condition
  *      is true from the beginning.
@@ -154,6 +192,7 @@ struct notification_thread_handle {
  *      - remove the trigger from the notification_trigger_clients_ht,
  *    - remove trigger from channel_triggers_ht (if applicable),
  *    - remove trigger from session_triggers_ht (if applicable),
+ *    - remove trigger to triggers_by_name_ht
  *    - remove trigger from triggers_ht
  *
  * 5) Reception of a channel monitor sample from the consumer daemon
@@ -168,9 +207,11 @@ struct notification_thread_handle {
  * 7) Session rotation completed
  *
  * 8) Connection of a client
- *    - add client socket to the client_socket_ht.
+ *    - add client socket to the client_socket_ht,
+ *    - add client socket to the client_id_ht.
  *
  * 9) Disconnection of a client
+ *    - remove client socket from the client_id_ht,
  *    - remove client socket from the client_socket_ht,
  *    - traverse all conditions to which the client is subscribed and remove
  *      the client from the notification_trigger_clients_ht.
@@ -191,6 +232,7 @@ struct notification_thread_state {
        int notification_channel_socket;
        struct lttng_poll_event events;
        struct cds_lfht *client_socket_ht;
+       struct cds_lfht *client_id_ht;
        struct cds_lfht *channel_triggers_ht;
        struct cds_lfht *session_triggers_ht;
        struct cds_lfht *channel_state_ht;
@@ -198,13 +240,22 @@ struct notification_thread_state {
        struct cds_lfht *channels_ht;
        struct cds_lfht *sessions_ht;
        struct cds_lfht *triggers_ht;
+       struct cds_lfht *triggers_by_name_ht;
+       struct cds_lfht *trigger_tokens_ht;
+       struct {
+               uint64_t token_generator;
+               uint64_t name_offset;
+       } trigger_id;
+       notification_client_id next_notification_client_id;
+       struct action_executor *executor;
 };
 
 /* notification_thread_data takes ownership of the channel monitor pipes. */
 struct notification_thread_handle *notification_thread_handle_create(
                struct lttng_pipe *ust32_channel_monitor_pipe,
                struct lttng_pipe *ust64_channel_monitor_pipe,
-               struct lttng_pipe *kernel_channel_monitor_pipe);
+               struct lttng_pipe *kernel_channel_monitor_pipe,
+               int kernel_notification_monitor_fd);
 void notification_thread_handle_destroy(
                struct notification_thread_handle *handle);
 struct lttng_thread *launch_notification_thread(
index e2a3ef9eaac221ef97cc611004db602d7af347f0..6813ce8c27d502a1d91b1a4e68cd9d12a6d2af1c 100644 (file)
@@ -88,6 +88,9 @@ int subscribe_session_consumed_size_rotation(struct ltt_session *session, uint64
                goto end;
        }
 
+       lttng_trigger_set_credentials(
+                       session->rotate_trigger, session->uid, session->gid);
+
        nc_status = lttng_notification_channel_subscribe(
                        rotate_notification_channel, session->rotate_condition);
        if (nc_status != LTTNG_NOTIFICATION_CHANNEL_STATUS_OK) {
index d908da9727452c215e308f208acd5bcaa7afacc2..f0ab4ce0423530b25a6de8beb599a46fcd08ecec 100644 (file)
@@ -31,7 +31,7 @@ struct sessiond_config sessiond_config_build_defaults = {
        .daemonize =                            false,
        .sig_parent =                           false,
 
-       .tracing_group_name.value =             DEFAULT_TRACING_GROUP,
+       .tracing_group_name.value =             (char *) DEFAULT_TRACING_GROUP,
        .kmod_probes_list.value =               NULL,
        .kmod_extra_probes_list.value =         NULL,
 
index ed090f6d6b09d65570c4fced29f951d871e1c1a3..ae7f45fd97f8257b645d37513e80c3274410d938 100644 (file)
@@ -61,9 +61,10 @@ void *launch_thread(void *data)
        void *ret;
        struct lttng_thread *thread = (struct lttng_thread *) data;
 
-       DBG("Launching \"%s\" thread", thread->name);
+       logger_set_thread_name(thread->name, true);
+       DBG("Entering thread entry point");
        ret = thread->entry(thread->data);
-       DBG("Thread \"%s\" has returned", thread->name);
+       DBG("Thread entry point has returned");
        return ret;
 }
 
@@ -164,20 +165,22 @@ bool _lttng_thread_shutdown(struct lttng_thread *thread)
                result = false;
                goto end;
        }
-       /* Release the list's reference to the thread. */
-       cds_list_del(&thread->node);
-       lttng_thread_put(thread);
+       DBG("Joined thread \"%s\"", thread->name);
 end:
        return result;
 }
 
 bool lttng_thread_shutdown(struct lttng_thread *thread)
 {
-       bool result;
-
-       pthread_mutex_lock(&thread_list.lock);
-       result = _lttng_thread_shutdown(thread);
-       pthread_mutex_unlock(&thread_list.lock);
+       const bool result = _lttng_thread_shutdown(thread);
+
+       if (result) {
+               /* Release the list's reference to the thread. */
+               pthread_mutex_lock(&thread_list.lock);
+               cds_list_del(&thread->node);
+               lttng_thread_put(thread);
+               pthread_mutex_unlock(&thread_list.lock);
+       }
        return result;
 }
 
index 52f819b3001390fba5fb71c253a6321497649a01..7ad8f2320fd14965b0de0644797beb2ad128d8ec 100644 (file)
 #include <lttng/lttng-error.h>
 #include <lttng/userspace-probe.h>
 #include <lttng/userspace-probe-internal.h>
-
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kprobe.h>
+#include <lttng/event-rule/kprobe-internal.h>
+#include <lttng/event-rule/kretprobe.h>
+#include <lttng/event-rule/kretprobe-internal.h>
+#include <lttng/event-rule/syscall.h>
+#include <lttng/event-rule/syscall-internal.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event-rule/tracepoint-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
 #include <common/common.h>
 #include <common/defaults.h>
 #include <common/trace-chunk.h>
@@ -130,6 +140,29 @@ struct ltt_kernel_event *trace_kernel_get_event_by_name(
        }
 }
 
+struct ltt_kernel_token_event_rule *trace_kernel_find_trigger_by_token(
+               struct ltt_kernel_token_event_rule_list *list,
+               uint64_t token)
+{
+       struct ltt_kernel_token_event_rule *token_event_rule;
+       int found = 0;
+
+       assert(list);
+
+       cds_list_for_each_entry(token_event_rule, &list->head, list) {
+               if (token_event_rule->token == token) {
+                       found = 1;
+               }
+               break;
+       }
+       if (found) {
+               DBG("Found token event rule %" PRIu64, token);
+               return token_event_rule;
+       } else {
+               return NULL;
+       }
+}
+
 /*
  * Allocate and initialize a kernel session data structure.
  *
@@ -478,6 +511,162 @@ error:
        return ret;
 }
 
+/*
+ * Allocate and initialize a kernel token event rule.
+ *
+ * Return pointer to structure or NULL.
+ */
+enum lttng_error_code trace_kernel_create_token_event_rule(
+               struct lttng_event_rule *event_rule,
+               uint64_t token,
+               struct ltt_kernel_token_event_rule **kernel_token_event_rule)
+{
+       enum lttng_error_code ret = LTTNG_OK;
+       struct ltt_kernel_token_event_rule *local_kernel_token_event_rule;
+
+       assert(kernel_token_event_rule);
+
+       local_kernel_token_event_rule = zmalloc(sizeof(struct ltt_kernel_token_event_rule));
+       if (local_kernel_token_event_rule == NULL) {
+               PERROR("Failed to allocate ltt_kernel_token_event_rule structure");
+               ret = LTTNG_ERR_NOMEM;
+               goto error;
+       }
+
+       local_kernel_token_event_rule->fd = -1;
+       local_kernel_token_event_rule->enabled = 1;
+       local_kernel_token_event_rule->token = token;
+
+       /* Get the reference of the event rule */
+       if (!lttng_event_rule_get(event_rule)) {
+               assert(0);
+       }
+
+       local_kernel_token_event_rule->event_rule = event_rule;
+       /* The event rule still own the filter and bytecode */
+       local_kernel_token_event_rule->filter = lttng_event_rule_get_filter_bytecode(event_rule);
+
+       DBG3("[trace] Kernel token event rule %" PRIu64 " allocated", local_kernel_token_event_rule->token);
+error:
+       *kernel_token_event_rule = local_kernel_token_event_rule;
+       return ret;
+       
+}
+
+/*
+ * Initialize a kernel trigger from an event rule.
+ */
+enum lttng_error_code trace_kernel_init_trigger_from_event_rule(const struct lttng_event_rule *rule,
+               struct lttng_kernel_trigger *kernel_trigger)
+{
+       enum lttng_error_code ret;
+       enum lttng_event_rule_status status;
+       const char *name = NULL;
+
+       /* TODO: do this for now but have disucssion on if this could be the
+        * responsability of the event_rule itself ala
+        * "lttng_even_rule_generate_kernel_trigger"
+        */
+       switch (lttng_event_rule_get_type(rule)) {
+       case LTTNG_EVENT_RULE_TYPE_KPROBE:
+               kernel_trigger->instrumentation = LTTNG_KERNEL_KPROBE;
+               kernel_trigger->u.kprobe.addr = lttng_event_rule_kprobe_get_address(rule);
+               kernel_trigger->u.kprobe.offset = lttng_event_rule_kprobe_get_offset(rule);
+               strncpy(kernel_trigger->u.kprobe.symbol_name,
+                               lttng_event_rule_kprobe_get_symbol_name(rule), LTTNG_KERNEL_SYM_NAME_LEN);
+               kernel_trigger->u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+               (void) lttng_event_rule_kprobe_get_name(rule, &name);
+               ret = LTTNG_OK;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_UPROBE:
+       {
+               const struct lttng_userspace_probe_location* location = NULL;
+               const struct lttng_userspace_probe_location_lookup_method *lookup = NULL;
+
+               status = lttng_event_rule_uprobe_get_location(rule, &location);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+                       goto error;
+               }
+
+               kernel_trigger->instrumentation = LTTNG_KERNEL_UPROBE;
+
+               /*
+                * Only the elf lookup method is supported at the moment.
+                */
+               lookup = lttng_userspace_probe_location_get_lookup_method(
+                               location);
+               if (!lookup) {
+                       ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+                       goto error;
+               }
+
+               /*
+                * From the kernel tracer's perspective, all userspace probe
+                * event types are all the same: a file and an offset.
+                */
+               switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) {
+               case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+                       /* Get the file descriptor on the target binary. */
+                       kernel_trigger->u.uprobe.fd =
+                                       lttng_userspace_probe_location_function_get_binary_fd(location);
+
+                       break;
+               case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+                       /* Get the file descriptor on the target binary. */
+                       kernel_trigger->u.uprobe.fd =
+                                       lttng_userspace_probe_location_tracepoint_get_binary_fd(location);
+                       break;
+               default:
+                       DBG("Unsupported lookup method type");
+                       ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+                       goto error;
+               }
+
+               (void) lttng_event_rule_uprobe_get_name(rule, &name);
+
+               ret = LTTNG_OK;
+               break;
+       }
+       case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+               assert("Not supported" && 0);
+               kernel_trigger->instrumentation = LTTNG_KERNEL_KRETPROBE;
+               kernel_trigger->u.kretprobe.addr = lttng_event_rule_kretprobe_get_address(rule);
+               kernel_trigger->u.kretprobe.offset = lttng_event_rule_kretprobe_get_offset(rule);
+               strncpy(kernel_trigger->u.kretprobe.symbol_name,
+                               lttng_event_rule_kretprobe_get_symbol_name(rule), LTTNG_KERNEL_SYM_NAME_LEN);
+               kernel_trigger->u.kretprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+               (void) lttng_event_rule_kretprobe_get_name(rule, &name);
+               ret = LTTNG_OK;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+               /* TODO: assert his is a kernel domain event-rule */
+               kernel_trigger->instrumentation = LTTNG_KERNEL_TRACEPOINT;
+               (void) lttng_event_rule_tracepoint_get_pattern(rule, &name);
+               ret = LTTNG_OK;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+               kernel_trigger->instrumentation = LTTNG_KERNEL_SYSCALL;
+               (void) lttng_event_rule_syscall_get_pattern(rule, &name);
+               ret = LTTNG_OK;
+               break;
+       default:
+               ERR("Unknown kernel event rule instrumentation type (%d)", lttng_event_rule_get_type(rule));
+               ret = LTTNG_ERR_INVALID;
+               goto error;
+       }
+
+       /*
+        * WTF is LTTNG_EVENT_ALL??? and LTTNG_EVENT_FUNTION_ENTRY?????
+        */
+
+       /* Copy event name */
+       strncpy(kernel_trigger->name, name, LTTNG_KERNEL_SYM_NAME_LEN);
+       kernel_trigger->name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+
+error:
+       return ret;
+}
 /*
  * Allocate and initialize a kernel metadata.
  *
@@ -607,6 +796,33 @@ void trace_kernel_destroy_event(struct ltt_kernel_event *event)
        free(event);
 }
 
+/*
+ * Cleanup kernel event structure.
+ */
+void trace_kernel_destroy_token_event_rule(struct ltt_kernel_token_event_rule *event)
+{
+       /* TODO: review in depth to ensure adequate disposing */
+       assert(event);
+
+       /* Remove from event list */
+       cds_list_del(&event->list);
+
+       if (event->fd >= 0) {
+               int ret;
+
+               DBG("[trace] Closing ,token event rule fd %d", event->fd);
+               /* Close kernel fd */
+               ret = close(event->fd);
+               if (ret) {
+                       PERROR("close");
+               }
+       } else {
+               DBG("[trace] Tearing down token event rule (no associated fd)");
+       }
+
+       lttng_event_rule_put(event->event_rule);
+       free(event);
+}
 /*
  * Cleanup kernel context structure.
  */
index b290ce492a6dac7e35ca8c8eac9a3000e0b90f7c..88e356854dd23217bb5c266f2199f61ad90a1eaa 100644 (file)
@@ -23,6 +23,11 @@ struct ltt_kernel_event_list {
        struct cds_list_head head;
 };
 
+/* Kernel event rule token list */
+struct ltt_kernel_token_event_rule_list {
+       struct cds_list_head head;
+};
+
 /* Channel stream list */
 struct ltt_kernel_stream_list {
        struct cds_list_head head;
@@ -52,6 +57,18 @@ struct ltt_kernel_event {
        struct lttng_userspace_probe_location *userspace_probe_location;
 };
 
+/* Kernel event */
+struct ltt_kernel_token_event_rule {
+       int fd;
+       int enabled;
+       enum lttng_event_type type;
+       struct lttng_event_rule *event_rule;
+       struct cds_list_head list;
+       uint64_t token;
+       const struct lttng_filter_bytecode *filter;
+       struct lttng_userspace_probe_location *userspace_probe_location;
+};
+
 /* Kernel channel */
 struct ltt_kernel_channel {
        int fd;
@@ -135,6 +152,10 @@ struct ltt_kernel_event *trace_kernel_find_event(
 struct ltt_kernel_channel *trace_kernel_get_channel_by_name(
                const char *name, struct ltt_kernel_session *session);
 
+struct ltt_kernel_token_event_rule *trace_kernel_find_trigger_by_token(
+               struct ltt_kernel_token_event_rule_list *list,
+               uint64_t token);
+
 /*
  * Create functions malloc() the data structure.
  */
@@ -149,8 +170,14 @@ struct ltt_kernel_stream *trace_kernel_create_stream(const char *name,
                unsigned int count);
 struct ltt_kernel_context *trace_kernel_create_context(
                struct lttng_kernel_context *ctx);
+enum lttng_error_code trace_kernel_create_token_event_rule(
+               struct lttng_event_rule *event_rule,
+               uint64_t token,
+               struct ltt_kernel_token_event_rule **kernel_token_event_rule);
 struct ltt_kernel_context *trace_kernel_copy_context(
                struct ltt_kernel_context *ctx);
+enum lttng_error_code trace_kernel_init_trigger_from_event_rule(const struct lttng_event_rule *rule,
+               struct lttng_kernel_trigger *kernel_trigger);
 
 /*
  * Destroy functions free() the data structure and remove from linked list if
@@ -162,6 +189,7 @@ void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel);
 void trace_kernel_destroy_event(struct ltt_kernel_event *event);
 void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream);
 void trace_kernel_destroy_context(struct ltt_kernel_context *ctx);
+void trace_kernel_destroy_token_event_rule(struct ltt_kernel_token_event_rule *rule);
 void trace_kernel_free_session(struct ltt_kernel_session *session);
 
 #endif /* _LTT_TRACE_KERNEL_H */
index 92fd0de8116b0d00500d0f5b324f1d88e0bb049f..ebb9ca47450980e72ad0245c0e99e5b2eae41467 100644 (file)
@@ -981,6 +981,7 @@ int trace_ust_track_id(enum lttng_tracker_type tracker_type,
                }
        }
        if (should_update_apps && session->active) {
+               /* TODO: should tokens be affected by tracking? */
                ust_app_global_update_all(session);
        }
        goto end;
@@ -1081,6 +1082,7 @@ int trace_ust_untrack_id(enum lttng_tracker_type tracker_type,
                }
        }
        if (should_update_apps && session->active) {
+               /* TODO should tokens be affected by tracker */
                ust_app_global_update_all(session);
        }
        goto end;
index 99da583d59d7398c2e3b31cbcde0795d8b84cbc9..ea33cd17651533fd57296dd9349b480a4f1252ba 100644 (file)
@@ -87,6 +87,29 @@ struct lttng_ust_stream {
         */
 } LTTNG_PACKED;
 
+#define LTTNG_UST_TRIGGER_PADDING1     16
+#define LTTNG_UST_TRIGGER_PADDING2     (LTTNG_UST_SYM_NAME_LEN + 32)
+struct lttng_ust_trigger {
+       uint64_t id;
+       enum lttng_ust_instrumentation instrumentation;
+       char name[LTTNG_UST_SYM_NAME_LEN];      /* event name */
+
+       enum lttng_ust_loglevel_type loglevel_type;
+       int loglevel;   /* value, -1: all */
+       char padding[LTTNG_UST_TRIGGER_PADDING1];
+
+       /* Per instrumentation type configuration */
+       union {
+               char padding[LTTNG_UST_TRIGGER_PADDING2];
+       } u;
+} LTTNG_PACKED;
+
+#define LTTNG_TRIGGER_NOTIFICATION_PADDING 32
+struct lttng_ust_trigger_notification {
+       uint64_t id;
+       char padding[LTTNG_TRIGGER_NOTIFICATION_PADDING];
+} LTTNG_PACKED;
+
 #define LTTNG_UST_EVENT_PADDING1       16
 #define LTTNG_UST_EVENT_PADDING2       (LTTNG_UST_SYM_NAME_LEN + 32)
 struct lttng_ust_event {
@@ -305,6 +328,9 @@ struct lttng_ust_event_exclusion {
 #define LTTNG_UST_FILTER                       _UST_CMD(0xA0)
 #define LTTNG_UST_EXCLUSION                    _UST_CMD(0xA1)
 
+#define LTTNG_UST_TRIGGER_SEND_FD              _UST_CMD(0xB0)
+#define LTTNG_UST_TRIGGER_CREATE               _UST_CMDW(0xB1, struct lttng_ust_trigger)
+
 #define LTTNG_UST_ROOT_HANDLE  0
 
 struct lttng_ust_obj;
index cf74703c34be55cea413af6e2261b81740f870a2..8fc0efdea8cb2ea14aff6df01f2ae3aecd4b5aaf 100644 (file)
 #include <signal.h>
 
 #include <common/common.h>
+#include <common/hashtable/utils.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/condition/event-rule.h>
 #include <common/sessiond-comm/sessiond-comm.h>
 
 #include "buffer-registry.h"
@@ -34,6 +41,7 @@
 #include "lttng-sessiond.h"
 #include "notification-thread-commands.h"
 #include "rotate.h"
+#include "event.h"
 
 struct lttng_ht *ust_app_ht;
 struct lttng_ht *ust_app_ht_by_sock;
@@ -313,6 +321,34 @@ void delete_ust_app_event(int sock, struct ust_app_event *ua_event,
        free(ua_event);
 }
 
+/*
+ * Delete ust app token event_rule safely. RCU read lock must be held before calling
+ * this function. TODO: or does it????
+ */
+static
+void delete_ust_app_token_event_rule(int sock, struct ust_app_token_event_rule *ua_token,
+               struct ust_app *app)
+{
+       int ret;
+
+       assert(ua_token);
+
+       if (ua_token->exclusion != NULL)
+               free(ua_token->exclusion);
+       if (ua_token->obj != NULL) {
+               pthread_mutex_lock(&app->sock_lock);
+               ret = ustctl_release_object(sock, ua_token->obj);
+               pthread_mutex_unlock(&app->sock_lock);
+               if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+                       ERR("UST app sock %d release event obj failed with ret %d",
+                                       sock, ret);
+               }
+               free(ua_token->obj);
+       }
+       lttng_event_rule_put(ua_token->event_rule);
+       free(ua_token);
+}
+
 /*
  * Release ust data object of the given stream.
  *
@@ -921,6 +957,10 @@ void delete_ust_app(struct ust_app *app)
        ht_cleanup_push(app->ust_sessions_objd);
        ht_cleanup_push(app->ust_objd);
 
+       ustctl_release_object(sock, app->token_communication.handle);
+
+       lttng_pipe_close(app->token_communication.trigger_event_pipe);
+
        /*
         * Wait until we have deleted the application from the sock hash table
         * before closing this socket, otherwise an application could re-use the
@@ -1019,7 +1059,7 @@ error_free:
  * Alloc new UST app channel.
  */
 static
-struct ust_app_channel *alloc_ust_app_channel(char *name,
+struct ust_app_channel *alloc_ust_app_channel(const char *name,
                struct ust_app_session *ua_sess,
                struct lttng_ust_channel_attr *attr)
 {
@@ -1125,6 +1165,41 @@ error:
        return NULL;
 }
 
+/*
+ * Alloc new UST app token event rule.
+ */
+static struct ust_app_token_event_rule *alloc_ust_app_token_event_rule(
+               struct lttng_event_rule *event_rule, uint64_t token)
+{
+       struct ust_app_token_event_rule *ua_token;
+
+       ua_token = zmalloc(sizeof(struct ust_app_token_event_rule));
+       if (ua_token == NULL) {
+               PERROR("Failed to allocate ust_app_token_event_rule structure");
+               goto error;
+       }
+
+       ua_token->enabled = 1;
+       ua_token->token = token;
+       lttng_ht_node_init_u64(&ua_token->node, token);
+
+       /* Get reference of the event_rule */
+       if (!lttng_event_rule_get(event_rule)) {
+               assert(0);
+       }
+
+       ua_token->event_rule = event_rule;
+       ua_token->filter = lttng_event_rule_get_filter_bytecode(event_rule);
+       ua_token->exclusion = lttng_event_rule_generate_exclusions(event_rule);
+
+       DBG3("UST app token event rule %" PRIu64 " allocated", ua_token->token);
+
+       return ua_token;
+
+error:
+       return NULL;
+}
+
 /*
  * Alloc new UST app context.
  */
@@ -1165,36 +1240,13 @@ error:
        return NULL;
 }
 
-/*
- * Allocate a filter and copy the given original filter.
- *
- * Return allocated filter or NULL on error.
- */
-static struct lttng_filter_bytecode *copy_filter_bytecode(
-               struct lttng_filter_bytecode *orig_f)
-{
-       struct lttng_filter_bytecode *filter = NULL;
-
-       /* Copy filter bytecode */
-       filter = zmalloc(sizeof(*filter) + orig_f->len);
-       if (!filter) {
-               PERROR("zmalloc alloc filter bytecode");
-               goto error;
-       }
-
-       memcpy(filter, orig_f, sizeof(*filter) + orig_f->len);
-
-error:
-       return filter;
-}
-
 /*
  * Create a liblttng-ust filter bytecode from given bytecode.
  *
  * Return allocated filter or NULL on error.
  */
 static struct lttng_ust_filter_bytecode *create_ust_bytecode_from_bytecode(
-               struct lttng_filter_bytecode *orig_f)
+               const struct lttng_filter_bytecode *orig_f)
 {
        struct lttng_ust_filter_bytecode *filter = NULL;
 
@@ -1297,6 +1349,32 @@ end:
        return event;
 }
 
+/*
+ * Lookup for an ust app tokens based on a token id.
+ *
+ * Return an ust_app_token_event_rule object or NULL on error.
+ */
+static struct ust_app_token_event_rule *find_ust_app_token_event_rule(struct lttng_ht *ht,
+               uint64_t token)
+{
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_u64 *node;
+       struct ust_app_token_event_rule *token_event_rule = NULL;
+
+       assert(ht);
+
+       lttng_ht_lookup(ht, &token, &iter);
+       node = lttng_ht_iter_get_node_u64(&iter);
+       if (node == NULL) {
+               DBG2("UST app token %" PRIu64 " not found", token);
+               goto end;
+       }
+
+       token_event_rule = caa_container_of(node, struct ust_app_token_event_rule, node);
+end:
+       return token_event_rule;
+}
+
 /*
  * Create the channel context on the tracer.
  *
@@ -1343,33 +1421,28 @@ error:
 /*
  * Set the filter on the tracer.
  */
-static
-int set_ust_event_filter(struct ust_app_event *ua_event,
-               struct ust_app *app)
+static int set_ust_filter(struct ust_app *app,
+               const struct lttng_filter_bytecode *bytecode,
+               struct lttng_ust_object_data *ust_object)
 {
        int ret;
        struct lttng_ust_filter_bytecode *ust_bytecode = NULL;
 
        health_code_update();
 
-       if (!ua_event->filter) {
-               ret = 0;
-               goto error;
-       }
-
-       ust_bytecode = create_ust_bytecode_from_bytecode(ua_event->filter);
+       ust_bytecode = create_ust_bytecode_from_bytecode(bytecode);
        if (!ust_bytecode) {
                ret = -LTTNG_ERR_NOMEM;
                goto error;
        }
        pthread_mutex_lock(&app->sock_lock);
        ret = ustctl_set_filter(app->sock, ust_bytecode,
-                       ua_event->obj);
+                       ust_object);
        pthread_mutex_unlock(&app->sock_lock);
        if (ret < 0) {
                if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
-                       ERR("UST app event %s filter failed for app (pid: %d) "
-                                       "with ret %d", ua_event->attr.name, app->pid, ret);
+                       ERR("UST app set filter failed for object %p of app (pid: %d) "
+                                       "with ret %d", ust_object, app->pid, ret);
                } else {
                        /*
                         * This is normal behavior, an application can die during the
@@ -1377,12 +1450,12 @@ int set_ust_event_filter(struct ust_app_event *ua_event,
                         * continue normally.
                         */
                        ret = 0;
-                       DBG3("UST app filter event failed. Application is dead.");
+                       DBG3("UST app set filter. Application is dead.");
                }
                goto error;
        }
 
-       DBG2("UST filter set successfully for event %s", ua_event->name);
+       DBG2("UST filter set for object %p successfully", ust_object);
 
 error:
        health_code_update();
@@ -1414,33 +1487,30 @@ end:
 /*
  * Set event exclusions on the tracer.
  */
-static
-int set_ust_event_exclusion(struct ust_app_event *ua_event,
-               struct ust_app *app)
+static int set_ust_exclusions(struct ust_app *app,
+               struct lttng_event_exclusion *exclusions,
+               struct lttng_ust_object_data *ust_object)
 {
        int ret;
-       struct lttng_ust_event_exclusion *ust_exclusion = NULL;
+       struct lttng_ust_event_exclusion *ust_exclusions = NULL;
 
-       health_code_update();
+       assert(exclusions && exclusions->count > 0);
 
-       if (!ua_event->exclusion || !ua_event->exclusion->count) {
-               ret = 0;
-               goto error;
-       }
+       health_code_update();
 
-       ust_exclusion = create_ust_exclusion_from_exclusion(
-                       ua_event->exclusion);
-       if (!ust_exclusion) {
+       ust_exclusions = create_ust_exclusion_from_exclusion(
+                       exclusions);
+       if (!ust_exclusions) {
                ret = -LTTNG_ERR_NOMEM;
                goto error;
        }
        pthread_mutex_lock(&app->sock_lock);
-       ret = ustctl_set_exclusion(app->sock, ust_exclusion, ua_event->obj);
+       ret = ustctl_set_exclusion(app->sock, ust_exclusions, ust_object);
        pthread_mutex_unlock(&app->sock_lock);
        if (ret < 0) {
                if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
-                       ERR("UST app event %s exclusions failed for app (pid: %d) "
-                                       "with ret %d", ua_event->attr.name, app->pid, ret);
+                       ERR("UST app exclusions failed for object %p of app (pid: %d) "
+                                       "with ret %d", ust_object, app->pid, ret);
                } else {
                        /*
                         * This is normal behavior, an application can die during the
@@ -1448,37 +1518,36 @@ int set_ust_event_exclusion(struct ust_app_event *ua_event,
                         * continue normally.
                         */
                        ret = 0;
-                       DBG3("UST app event exclusion failed. Application is dead.");
+                       DBG3("UST app set exclusions failed. Application is dead.");
                }
                goto error;
        }
 
-       DBG2("UST exclusion set successfully for event %s", ua_event->name);
+       DBG2("UST exclusions set successfully for object %p", ust_object);
 
 error:
        health_code_update();
-       free(ust_exclusion);
+       free(ust_exclusions);
        return ret;
 }
 
 /*
  * Disable the specified event on to UST tracer for the UST session.
  */
-static int disable_ust_event(struct ust_app *app,
-               struct ust_app_session *ua_sess, struct ust_app_event *ua_event)
+static int disable_ust_object(struct ust_app *app,
+               struct lttng_ust_object_data *object)
 {
        int ret;
 
        health_code_update();
 
        pthread_mutex_lock(&app->sock_lock);
-       ret = ustctl_disable(app->sock, ua_event->obj);
+       ret = ustctl_disable(app->sock, object);
        pthread_mutex_unlock(&app->sock_lock);
        if (ret < 0) {
                if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
-                       ERR("UST app event %s disable failed for app (pid: %d) "
-                                       "and session handle %d with ret %d",
-                                       ua_event->attr.name, app->pid, ua_sess->handle, ret);
+                       ERR("UST app disable failed for object %p app (pid: %d) with ret %d",
+                                       object, app->pid, ret);
                } else {
                        /*
                         * This is normal behavior, an application can die during the
@@ -1491,8 +1560,8 @@ static int disable_ust_event(struct ust_app *app,
                goto error;
        }
 
-       DBG2("UST app event %s disabled successfully for app (pid: %d)",
-                       ua_event->attr.name, app->pid);
+       DBG2("UST app object %p disabled successfully for app (pid: %d)",
+                       object, app->pid);
 
 error:
        health_code_update();
@@ -1580,21 +1649,19 @@ error:
 /*
  * Enable the specified event on to UST tracer for the UST session.
  */
-static int enable_ust_event(struct ust_app *app,
-               struct ust_app_session *ua_sess, struct ust_app_event *ua_event)
+static int enable_ust_object(struct ust_app *app, struct lttng_ust_object_data *ust_object)
 {
        int ret;
 
        health_code_update();
 
        pthread_mutex_lock(&app->sock_lock);
-       ret = ustctl_enable(app->sock, ua_event->obj);
+       ret = ustctl_enable(app->sock, ust_object);
        pthread_mutex_unlock(&app->sock_lock);
        if (ret < 0) {
                if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
-                       ERR("UST app event %s enable failed for app (pid: %d) "
-                                       "and session handle %d with ret %d",
-                                       ua_event->attr.name, app->pid, ua_sess->handle, ret);
+                       ERR("UST app enable failed for object %p app (pid: %d) with ret %d",
+                                       ust_object, app->pid, ret);
                } else {
                        /*
                         * This is normal behavior, an application can die during the
@@ -1602,13 +1669,13 @@ static int enable_ust_event(struct ust_app *app,
                         * continue normally.
                         */
                        ret = 0;
-                       DBG3("UST app enable event failed. Application is dead.");
+                       DBG3("UST app enable failed. Application is dead.");
                }
                goto error;
        }
 
-       DBG2("UST app event %s enabled successfully for app (pid: %d)",
-                       ua_event->attr.name, app->pid);
+       DBG2("UST app object %p enabled successfully for app (pid: %d)",
+                       ust_object, app->pid);
 
 error:
        health_code_update();
@@ -1704,14 +1771,14 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess,
 
        ua_event->handle = ua_event->obj->handle;
 
-       DBG2("UST app event %s created successfully for pid:%d",
-                       ua_event->attr.name, app->pid);
+       DBG2("UST app event %s created successfully for pid:%d object: %p",
+                       ua_event->attr.name, app->pid, ua_event->obj);
 
        health_code_update();
 
        /* Set filter if one is present. */
        if (ua_event->filter) {
-               ret = set_ust_event_filter(ua_event, app);
+               ret = set_ust_filter(app, ua_event->filter, ua_event->obj);
                if (ret < 0) {
                        goto error;
                }
@@ -1719,7 +1786,7 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess,
 
        /* Set exclusions for the event */
        if (ua_event->exclusion) {
-               ret = set_ust_event_exclusion(ua_event, app);
+               ret = set_ust_exclusions(app, ua_event->exclusion, ua_event->obj);
                if (ret < 0) {
                        goto error;
                }
@@ -1731,7 +1798,7 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess,
                 * We now need to explicitly enable the event, since it
                 * is now disabled at creation.
                 */
-               ret = enable_ust_event(app, ua_sess, ua_event);
+               ret = enable_ust_object(app, ua_event->obj);
                if (ret < 0) {
                        /*
                         * If we hit an EPERM, something is wrong with our enable call. If
@@ -1758,6 +1825,159 @@ error:
        return ret;
 }
 
+static
+void init_ust_trigger_from_event_rule(const struct lttng_event_rule *rule, struct lttng_ust_trigger *trigger)
+{
+       enum lttng_event_rule_status status;
+       enum lttng_loglevel_type loglevel_type;
+       enum lttng_ust_loglevel_type ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL;
+       int loglevel = -1;
+       const char *pattern;
+
+       /* For now only LTTNG_EVENT_RULE_TYPE_TRACEPOINT are supported */
+       assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT);
+
+       memset(trigger, 0, sizeof(*trigger));
+
+       if (lttng_event_rule_is_agent(rule)) {
+               /*
+                * Special event for agents
+                * The actual meat of the event is in the filter that will be
+                * attached later on.
+                * Set the default values for the agent event.
+                */
+               pattern = event_get_default_agent_ust_name(lttng_event_rule_get_domain_type(rule));
+               loglevel = 0;
+               ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL;
+       } else {
+               status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       /* At this point this is a fatal error */
+                       assert(0);
+               }
+
+               status = lttng_event_rule_tracepoint_get_loglevel_type(
+                               rule, &loglevel_type);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       /* At this point this is a fatal error */
+                       assert(0);
+               }
+
+               switch (loglevel_type) {
+               case LTTNG_EVENT_LOGLEVEL_ALL:
+                       ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL;
+                       break;
+               case LTTNG_EVENT_LOGLEVEL_RANGE:
+                       ust_loglevel_type = LTTNG_UST_LOGLEVEL_RANGE;
+                       break;
+               case LTTNG_EVENT_LOGLEVEL_SINGLE:
+                       ust_loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE;
+                       break;
+               }
+
+               if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) {
+                       status = lttng_event_rule_tracepoint_get_loglevel(
+                                       rule, &loglevel);
+                       assert(status == LTTNG_EVENT_RULE_STATUS_OK);
+               }
+       }
+
+       trigger->instrumentation = LTTNG_UST_TRACEPOINT;
+       strncpy(trigger->name, pattern, LTTNG_UST_SYM_NAME_LEN - 1);
+       trigger->loglevel_type = ust_loglevel_type;
+       trigger->loglevel = loglevel;
+}
+
+/*
+ * Create the specified event rule token onto the UST tracer for a UST app.
+ *
+ */
+static
+int create_ust_token_event_rule(struct ust_app *app, struct ust_app_token_event_rule *ua_token)
+{
+       int ret = 0;
+       struct lttng_ust_trigger trigger;
+
+       health_code_update();
+
+       init_ust_trigger_from_event_rule(ua_token->event_rule, &trigger);
+       trigger.id = ua_token->token;
+
+       /* Create UST trigger on tracer */
+       pthread_mutex_lock(&app->sock_lock);
+       ret = ustctl_create_trigger(app->sock, &trigger, app->token_communication.handle, &ua_token->obj);
+       pthread_mutex_unlock(&app->sock_lock);
+       if (ret < 0) {
+               if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+                       abort();
+                       ERR("Error ustctl create trigger %s for app pid: %d with ret %d",
+                                       trigger.name, app->pid, ret);
+               } else {
+                       /*
+                        * This is normal behavior, an application can die during the
+                        * creation process. Don't report an error so the execution can
+                        * continue normally.
+                        */
+                       ret = 0;
+                       DBG3("UST app create event failed. Application is dead.");
+               }
+               goto error;
+       }
+
+       ua_token->handle = ua_token->obj->handle;
+
+       DBG2("UST app event %s created successfully for pid:%d object: %p",
+                       trigger.name, app->pid, ua_token->obj);
+
+       health_code_update();
+
+       /* Set filter if one is present. */
+       if (ua_token->filter) {
+               ret = set_ust_filter(app, ua_token->filter, ua_token->obj);
+               if (ret < 0) {
+                       goto error;
+               }
+       }
+
+       /* Set exclusions for the event */
+       if (ua_token->exclusion) {
+               ret = set_ust_exclusions(app, ua_token->exclusion, ua_token->obj);
+               if (ret < 0) {
+                       goto error;
+               }
+       }
+
+       /*
+        * We now need to explicitly enable the event, since it
+        * is disabled at creation.
+        */
+       ret = enable_ust_object(app, ua_token->obj);
+       if (ret < 0) {
+               /*
+                * If we hit an EPERM, something is wrong with our enable call. If
+                * we get an EEXIST, there is a problem on the tracer side since we
+                * just created it.
+                */
+               switch (ret) {
+               case -LTTNG_UST_ERR_PERM:
+                       /* Code flow problem */
+                       assert(0);
+               case -LTTNG_UST_ERR_EXIST:
+                       /* It's OK for our use case. */
+                       ret = 0;
+                       break;
+               default:
+                       break;
+               }
+               goto error;
+       }
+       ua_token->enabled = true;
+
+error:
+       health_code_update();
+       return ret;
+}
+
 /*
  * Copy data between an UST app event and a LTT event.
  */
@@ -2341,7 +2561,7 @@ int enable_ust_app_event(struct ust_app_session *ua_sess,
 {
        int ret;
 
-       ret = enable_ust_event(app, ua_sess, ua_event);
+       ret = enable_ust_object(app, ua_event->obj);
        if (ret < 0) {
                goto error;
        }
@@ -2360,7 +2580,7 @@ static int disable_ust_app_event(struct ust_app_session *ua_sess,
 {
        int ret;
 
-       ret = disable_ust_event(app, ua_sess, ua_event);
+       ret = disable_ust_object(app, ua_event->obj);
        if (ret < 0) {
                goto error;
        }
@@ -3175,6 +3395,57 @@ error:
        return ret;
 }
 
+/*
+ * Create UST app event and create it on the tracer side.
+ *
+ * Called with ust app session mutex held.
+ */
+static
+int create_ust_app_token_event_rule(struct lttng_event_rule *rule,
+               struct ust_app *app, uint64_t token)
+{
+       int ret = 0;
+       struct ust_app_token_event_rule *ua_token;
+
+       ua_token = alloc_ust_app_token_event_rule(rule, token);
+       if (ua_token == NULL) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       /* Create it on the tracer side */
+       ret = create_ust_token_event_rule(app, ua_token);
+       if (ret < 0) {
+               /*
+                * Not found previously means that it does not exist on the
+                * tracer. If the application reports that the event existed,
+                * it means there is a bug in the sessiond or lttng-ust
+                * (or corruption, etc.)
+                */
+               if (ret == -LTTNG_UST_ERR_EXIST) {
+                       ERR("Tracer for application reported that a token event rule being created already existed: "
+                                       "token = \"%" PRIu64 "\", pid = %d, ppid = %d, uid = %d, gid = %d",
+                                       token,
+                                       app->pid, app->ppid, app->uid,
+                                       app->gid);
+               }
+               goto error;
+       }
+
+       lttng_ht_add_unique_u64(app->tokens_ht, &ua_token->node);
+
+       DBG2("UST app create token event rule %" PRIu64 " for PID %d completed", token,
+                       app->pid);
+
+end:
+       return ret;
+
+error:
+       /* Valid. Calling here is already in a read side lock */
+       delete_ust_app_token_event_rule(-1, ua_token, app);
+       return ret;
+}
+
 /*
  * Create UST metadata and open it on the tracer side.
  *
@@ -3319,6 +3590,7 @@ error:
 struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock)
 {
        struct ust_app *lta = NULL;
+       struct lttng_pipe *trigger_event_source_pipe = NULL;
 
        assert(msg);
        assert(sock >= 0);
@@ -3335,12 +3607,20 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock)
                goto error;
        }
 
+       trigger_event_source_pipe = lttng_pipe_open(FD_CLOEXEC);
+       if (!trigger_event_source_pipe) {
+               PERROR("Open trigger pipe");
+               goto error;
+       }
+
        lta = zmalloc(sizeof(struct ust_app));
        if (lta == NULL) {
                PERROR("malloc");
                goto error;
        }
 
+       lta->token_communication.trigger_event_pipe = trigger_event_source_pipe;
+
        lta->ppid = msg->ppid;
        lta->uid = msg->uid;
        lta->gid = msg->gid;
@@ -3359,6 +3639,7 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock)
        lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
        lta->ust_sessions_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
        lta->notify_sock = -1;
+       lta->tokens_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
 
        /* Copy name and make sure it's NULL terminated. */
        strncpy(lta->name, msg->name, sizeof(lta->name));
@@ -3445,6 +3726,47 @@ int ust_app_version(struct ust_app *app)
        return ret;
 }
 
+/*
+ * Setup the base trigger group.
+ *
+ * Return 0 on success else a negative value either an errno code or a
+ * LTTng-UST error code.
+ */
+int ust_app_setup_trigger_group(struct ust_app *app)
+{
+       int ret;
+       int writefd;
+       struct lttng_ust_object_data *group = NULL;
+       enum lttng_error_code lttng_ret;
+
+       assert(app);
+
+       /* Get the write side of the pipe */
+       writefd = lttng_pipe_get_writefd(app->token_communication.trigger_event_pipe);
+
+       pthread_mutex_lock(&app->sock_lock);
+       ret = ustctl_create_trigger_group(app->sock, writefd, &group);
+       pthread_mutex_unlock(&app->sock_lock);
+       if (ret < 0) {
+               ERR("UST app %d create_trigger_group failed with ret %d", app->sock, ret);
+               goto end;
+       }
+
+       app->token_communication.handle = group;
+
+       lttng_ret = notification_thread_command_add_application(
+                       notification_thread_handle, app->token_communication.trigger_event_pipe);
+       if (lttng_ret != LTTNG_OK) {
+               /* TODO: error */
+               ret = - 1;
+               ERR("Failed to add channel to notification thread");
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
 /*
  * Unregister app by removing it from the global traceable app list and freeing
  * the data struct.
@@ -3453,6 +3775,7 @@ int ust_app_version(struct ust_app *app)
  */
 void ust_app_unregister(int sock)
 {
+       enum lttng_error_code ret_code;
        struct ust_app *lta;
        struct lttng_ht_node_ulong *node;
        struct lttng_ht_iter ust_app_sock_iter;
@@ -3558,6 +3881,13 @@ void ust_app_unregister(int sock)
                                lta->pid);
        }
 
+       ret_code = notification_thread_command_remove_application(
+                       notification_thread_handle,
+                       lta->token_communication.trigger_event_pipe);
+       if (ret_code != LTTNG_OK) {
+               ERR("Failed to remove application from notification thread");
+       }
+
        /* Free memory */
        call_rcu(&lta->pid_n.head, delete_ust_app_rcu);
 
@@ -4994,6 +5324,123 @@ end:
        return ret;
 }
 
+static
+void ust_app_synchronize_tokens(struct ust_app *app)
+{
+       int ret = 0;
+       enum lttng_error_code ret_code;
+       enum lttng_trigger_status t_status;
+       struct lttng_ht_iter app_trigger_iter;
+       struct lttng_triggers *triggers;
+       struct ust_app_token_event_rule *token_event_rule_element;
+       unsigned int count;
+
+       rcu_read_lock();
+       /* TODO: is this necessary to protect against new trigger being added ?
+        * notification_trigger_tokens_ht is still the backing data structure
+        * for this listing. Leave it there for now.
+        */
+       pthread_mutex_lock(&notification_trigger_tokens_ht_lock);
+       ret_code = notification_thread_command_get_tokens(
+                       notification_thread_handle, &triggers);
+       if (ret_code != LTTNG_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       assert(triggers);
+
+       t_status = lttng_triggers_get_count(triggers, &count);
+       if (t_status != LTTNG_TRIGGER_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       for (unsigned int i = 0; i < count; i++) {
+               struct lttng_condition *condition;
+               struct lttng_event_rule *event_rule;
+               struct lttng_trigger *trigger;
+               struct ust_app_token_event_rule *ua_token;
+               uint64_t token;
+
+               trigger = lttng_triggers_get_pointer_of_index(triggers, i);
+               assert(trigger);
+
+               /* TODO: error checking and type checking */
+               token = lttng_trigger_get_key(trigger);
+               condition = lttng_trigger_get_condition(trigger);
+               (void) lttng_condition_event_rule_get_rule_no_const(condition, &event_rule);
+
+               if (lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL) {
+                       /* Skip kernel related trigger */
+                       continue;
+               }
+
+               /* Iterate over all known token trigger */
+               ua_token = find_ust_app_token_event_rule(app->tokens_ht, token);
+               if (!ua_token) {
+                       ret = create_ust_app_token_event_rule(event_rule, app, token);
+                       if (ret < 0) {
+                               goto end;
+                       }
+               }
+       }
+
+       /* Remove all unknown trigger from the app
+        * TODO find a way better way then this, do it on the unregister command
+        * and be specific on the token to remove instead of going over all
+        * trigger known to the app. This is sub optimal.
+        */
+       cds_lfht_for_each_entry (app->tokens_ht->ht, &app_trigger_iter.iter,
+                       token_event_rule_element, node.node) {
+               uint64_t token;
+               bool found = false;
+
+               token = token_event_rule_element->token;
+
+               /*
+                * Check if the app event trigger still exists on the
+                * notification side.
+                * TODO: might want to change the backing data struct of the
+                * lttng_triggers object to allow quick lookup?
+                * For kernel mostly all of this can be removed once we delete
+                * on a per trigger basis.
+                */
+
+               for (unsigned int i = 0; i < count; i++) {
+                       struct lttng_trigger *trigger;
+                       uint64_t inner_token;
+
+                       trigger = lttng_triggers_get_pointer_of_index(
+                                       triggers, i);
+                       assert(trigger);
+
+                       inner_token = lttng_trigger_get_key(trigger);
+
+                       if (inner_token == token) {
+                               found = true;
+                               break;
+                       }
+               }
+
+               if (found) {
+                       /* Still valid */
+                       continue;
+               }
+
+               /* TODO: This is fucking ugly API for fuck sake */
+               assert(!lttng_ht_del(app->tokens_ht, &app_trigger_iter));
+
+               (void) disable_ust_object(app, token_event_rule_element->obj);
+
+               delete_ust_app_token_event_rule(app->sock, token_event_rule_element, app);
+       }
+end:
+       rcu_read_unlock();
+       pthread_mutex_unlock(&notification_trigger_tokens_ht_lock);
+       return;
+}
+
 /*
  * The caller must ensure that the application is compatible and is tracked
  * by the PID tracker.
@@ -5132,6 +5579,16 @@ void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app)
        }
 }
 
+void ust_app_global_update_tokens(struct ust_app *app)
+{
+       DBG2("UST app global update token for app sock %d", app->sock);
+
+       if (!app->compatible) {
+               return;
+       }
+       ust_app_synchronize_tokens(app);
+}
+
 /*
  * Called with session lock held.
  */
@@ -5147,6 +5604,18 @@ void ust_app_global_update_all(struct ltt_ust_session *usess)
        rcu_read_unlock();
 }
 
+void ust_app_global_update_all_tokens(void)
+{
+       struct lttng_ht_iter iter;
+       struct ust_app *app;
+
+       rcu_read_lock();
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+               ust_app_global_update_tokens(app);
+       }
+       rcu_read_unlock();
+}
+
 /*
  * Add context to a specific channel for global UST domain.
  */
index 6f3588a54f6930f9f4a105b4ef84084420d939dd..a24b42ffd2b40f4a6574151a53e45942ea043a9c 100644 (file)
@@ -112,6 +112,19 @@ struct ust_app_event {
        struct lttng_event_exclusion *exclusion;
 };
 
+struct ust_app_token_event_rule {
+       int enabled;
+       int handle;
+       struct lttng_ust_object_data *obj;
+       struct lttng_event_rule *event_rule;
+       uint64_t token;
+       struct lttng_ht_node_u64 node;
+       /* The event_rule object own this pointer */
+       const struct lttng_filter_bytecode *filter;
+       /* The event_rule object own this pointer */
+       struct lttng_event_exclusion *exclusion;
+};
+
 struct ust_app_stream {
        int handle;
        char pathname[PATH_MAX];
@@ -292,6 +305,14 @@ struct ust_app {
         * Used for path creation
         */
        time_t registration_time;
+       /*
+        * Trigger
+        */
+       struct {
+               struct lttng_ust_object_data *handle;
+               struct lttng_pipe *trigger_event_pipe;
+       } token_communication;
+       struct lttng_ht *tokens_ht;
 };
 
 #ifdef HAVE_LIBLTTNG_UST_CTL
@@ -319,6 +340,8 @@ int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx);
 void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app);
 void ust_app_global_update_all(struct ltt_ust_session *usess);
+void ust_app_global_update_tokens(struct ust_app *app);
+void ust_app_global_update_all_tokens(void);
 
 void ust_app_clean_list(void);
 int ust_app_ht_alloc(void);
@@ -355,6 +378,8 @@ int ust_app_release_object(struct ust_app *app,
                struct lttng_ust_object_data *data);
 enum lttng_error_code ust_app_clear_session(struct ltt_session *session);
 
+int ust_app_setup_trigger_group(struct ust_app *app);
+
 static inline
 int ust_app_supported(void)
 {
@@ -443,6 +468,17 @@ static inline
 void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app)
 {}
 static inline
+void ust_app_global_update_tokens(struct ust_app *app)
+{}
+static inline
+void ust_app_global_update_all_tokens(void)
+{}
+static inline
+int ust_app_setup_trigger_group(struct ust_app *app)
+{
+    return 0;
+}
+static inline
 int ust_app_disable_channel_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan)
 {
index 96099f8aa08926aee758293f6ccc3e30652780f9..acb0613826f94a24e618ffcbb57fa1a32b0fcf94 100644 (file)
@@ -85,6 +85,14 @@ int ustctl_disable(int sock, struct lttng_ust_object_data *object);
 int ustctl_start_session(int sock, int handle);
 int ustctl_stop_session(int sock, int handle);
 
+int ustctl_create_trigger_group(int sock,
+               int pipe_fd,
+               struct lttng_ust_object_data **trigger_group_handle);
+int ustctl_create_trigger(int sock,
+               struct lttng_ust_trigger *trigger,
+               struct lttng_ust_object_data *trigger_group_handle,
+               struct lttng_ust_object_data **trigger_data);
+
 /*
  * ustctl_tracepoint_list returns a tracepoint list handle, or negative
  * error value.
index f84859fc621645aea1939d7c04648c7ac729682a..2ae586b512a7117c4ed5d761c84a93283ad679c2 100644 (file)
@@ -98,3 +98,26 @@ const char *consumer_output_get_base_path(const struct consumer_output *output)
                        output->dst.session_root_path :
                        output->dst.net.base_dir;
 }
+
+/*
+ * Allocate a filter and copy the given original filter.
+ *
+ * Return allocated filter or NULL on error.
+ */
+struct lttng_filter_bytecode *copy_filter_bytecode(
+               const struct lttng_filter_bytecode *orig_f)
+{
+       struct lttng_filter_bytecode *filter = NULL;
+
+       /* Copy filter bytecode */
+       filter = zmalloc(sizeof(*filter) + orig_f->len);
+       if (!filter) {
+               PERROR("zmalloc alloc filter bytecode");
+               goto error;
+       }
+
+       memcpy(filter, orig_f, sizeof(*filter) + orig_f->len);
+
+error:
+       return filter;
+}
index ff8deaec45597b64ab88a366b1c7cec39a89ec4d..d5e506610d2bf244a6b641a17376b4957658b16f 100644 (file)
@@ -19,5 +19,7 @@ int loglevels_match(int a_loglevel_type, int a_loglevel_value,
        int b_loglevel_type, int b_loglevel_value, int loglevel_all_type);
 const char *session_get_base_path(const struct ltt_session *session);
 const char *consumer_output_get_base_path(const struct consumer_output *output);
+struct lttng_filter_bytecode *copy_filter_bytecode(
+               const struct lttng_filter_bytecode *orig_f);
 
 #endif /* _LTT_UTILS_H */
index d094c5aafc30a146e27514fbe992fcc1b5078f40..96d7114a21b02bdb2b74a556dc8d3a13df17bfbf 100644 (file)
@@ -29,7 +29,11 @@ lttng_SOURCES = command.h conf.c conf.h commands/start.c \
                                commands/enable_rotation.c \
                                commands/disable_rotation.c \
                                commands/clear.c \
-                               utils.c utils.h lttng.c
+                               commands/add_trigger.c \
+                               commands/list_triggers.c \
+                               commands/remove_trigger.c \
+                               utils.c utils.h lttng.c \
+                               uprobe.c uprobe.h
 
 lttng_CFLAGS = $(AM_CFLAGS) $(POPT_CFLAGS)
 
@@ -37,4 +41,6 @@ lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
                        $(top_builddir)/src/common/libcommon.la \
                        $(top_builddir)/src/common/config/libconfig.la \
                        $(top_builddir)/src/common/string-utils/libstring-utils.la \
+                       $(top_builddir)/src/common/filter/libfilter.la \
+                       $(top_builddir)/src/common/argpar/libargpar.la \
                        $(POPT_LIBS)
index 8f1c7be408667822d35b45821c965f7841ab3670..bf004521003787acb910b9d29ea3a1b519c9af9a 100644 (file)
@@ -77,6 +77,9 @@ DECL_COMMAND(rotate);
 DECL_COMMAND(enable_rotation);
 DECL_COMMAND(disable_rotation);
 DECL_COMMAND(clear);
+DECL_COMMAND(add_trigger);
+DECL_COMMAND(list_triggers);
+DECL_COMMAND(remove_trigger);
 
 extern int cmd_help(int argc, const char **argv,
                const struct cmd_struct commands[]);
index 4122025728255114662c74f3d431165a01e4a41c..994cd49eb32037b898dc3fcac9853c3681ebd9a9 100644 (file)
@@ -184,19 +184,19 @@ static struct poptOption long_options[] = {
  */
 #define PERF_HW(optstr, name, type, hide)                              \
        {                                                               \
-               optstr, type, hide,                                     \
+               (char *) optstr, type, hide,                            \
                .u.perf = { PERF_TYPE_HARDWARE, PERF_COUNT_HW_##name, },\
        }
 
 #define PERF_SW(optstr, name, type, hide)                              \
        {                                                               \
-               optstr, type, hide,                                     \
+               (char *) optstr, type, hide,                            \
                .u.perf = { PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##name, },\
        }
 
 #define _PERF_HW_CACHE(optstr, name, type, op, result, hide)           \
        {                                                               \
-               optstr, type, hide,                                     \
+               (char *) optstr, type, hide,                            \
                .u.perf = {                                             \
                        PERF_TYPE_HW_CACHE,                             \
                        (uint64_t) PERF_COUNT_HW_CACHE_##name           \
@@ -235,45 +235,49 @@ const struct ctx_opts {
                } app_ctx;
        } u;
 } ctx_opts[] = {
-       { "pid", CONTEXT_PID },
-       { "procname", CONTEXT_PROCNAME },
-       { "prio", CONTEXT_PRIO },
-       { "nice", CONTEXT_NICE },
-       { "vpid", CONTEXT_VPID },
-       { "tid", CONTEXT_TID },
-       { "pthread_id", CONTEXT_PTHREAD_ID },
-       { "vtid", CONTEXT_VTID },
-       { "ppid", CONTEXT_PPID },
-       { "vppid", CONTEXT_VPPID },
-       { "hostname", CONTEXT_HOSTNAME },
-       { "ip", CONTEXT_IP },
-       { "interruptible", CONTEXT_INTERRUPTIBLE },
-       { "preemptible", CONTEXT_PREEMPTIBLE },
-       { "need_reschedule", CONTEXT_NEED_RESCHEDULE },
-       { "migratable", CONTEXT_MIGRATABLE },
-       { "callstack-kernel", CONTEXT_CALLSTACK_KERNEL },
+       /*
+        * These (char *) casts (as well as those in the PERF_* macros) are
+        * safe because we never free these instances of `struct ctx_opts`.
+        */
+       { (char *) "pid", CONTEXT_PID },
+       { (char *) "procname", CONTEXT_PROCNAME },
+       { (char *) "prio", CONTEXT_PRIO },
+       { (char *) "nice", CONTEXT_NICE },
+       { (char *) "vpid", CONTEXT_VPID },
+       { (char *) "tid", CONTEXT_TID },
+       { (char *) "pthread_id", CONTEXT_PTHREAD_ID },
+       { (char *) "vtid", CONTEXT_VTID },
+       { (char *) "ppid", CONTEXT_PPID },
+       { (char *) "vppid", CONTEXT_VPPID },
+       { (char *) "hostname", CONTEXT_HOSTNAME },
+       { (char *) "ip", CONTEXT_IP },
+       { (char *) "interruptible", CONTEXT_INTERRUPTIBLE },
+       { (char *) "preemptible", CONTEXT_PREEMPTIBLE },
+       { (char *) "need_reschedule", CONTEXT_NEED_RESCHEDULE },
+       { (char *) "migratable", CONTEXT_MIGRATABLE },
+       { (char *) "callstack-kernel", CONTEXT_CALLSTACK_KERNEL },
 #if HAVE_MODULES_USERSPACE_CALLSTACK_CONTEXT
-       { "callstack-user", CONTEXT_CALLSTACK_USER },
+       { (char *) "callstack-user", CONTEXT_CALLSTACK_USER },
 #endif
-       { "cgroup_ns", CONTEXT_CGROUP_NS },
-       { "ipc_ns", CONTEXT_IPC_NS },
-       { "mnt_ns", CONTEXT_MNT_NS },
-       { "net_ns", CONTEXT_NET_NS },
-       { "pid_ns", CONTEXT_PID_NS },
-       { "user_ns", CONTEXT_USER_NS },
-       { "uts_ns", CONTEXT_UTS_NS },
-       { "uid", CONTEXT_UID },
-       { "euid", CONTEXT_EUID },
-       { "suid", CONTEXT_SUID },
-       { "gid", CONTEXT_GID },
-       { "egid", CONTEXT_EGID },
-       { "sgid", CONTEXT_SGID },
-       { "vuid", CONTEXT_VUID },
-       { "veuid", CONTEXT_VEUID },
-       { "vsuid", CONTEXT_VSUID },
-       { "vgid", CONTEXT_VGID },
-       { "vegid", CONTEXT_VEGID },
-       { "vsgid", CONTEXT_VSGID },
+       { (char *) "cgroup_ns", CONTEXT_CGROUP_NS },
+       { (char *) "ipc_ns", CONTEXT_IPC_NS },
+       { (char *) "mnt_ns", CONTEXT_MNT_NS },
+       { (char *) "net_ns", CONTEXT_NET_NS },
+       { (char *) "pid_ns", CONTEXT_PID_NS },
+       { (char *) "user_ns", CONTEXT_USER_NS },
+       { (char *) "uts_ns", CONTEXT_UTS_NS },
+       { (char *) "uid", CONTEXT_UID },
+       { (char *) "euid", CONTEXT_EUID },
+       { (char *) "suid", CONTEXT_SUID },
+       { (char *) "gid", CONTEXT_GID },
+       { (char *) "egid", CONTEXT_EGID },
+       { (char *) "sgid", CONTEXT_SGID },
+       { (char *) "vuid", CONTEXT_VUID },
+       { (char *) "veuid", CONTEXT_VEUID },
+       { (char *) "vsuid", CONTEXT_VSUID },
+       { (char *) "vgid", CONTEXT_VGID },
+       { (char *) "vegid", CONTEXT_VEGID },
+       { (char *) "vsgid", CONTEXT_VSGID },
 
        /* Perf options */
 
diff --git a/src/bin/lttng/commands/add_trigger.c b/src/bin/lttng/commands/add_trigger.c
new file mode 100644 (file)
index 0000000..e0546e1
--- /dev/null
@@ -0,0 +1,1727 @@
+#include <stdio.h>
+
+#include "../command.h"
+#include "../uprobe.h"
+
+#include "common/argpar/argpar.h"
+#include "common/dynamic-array.h"
+#include "common/string-utils/string-utils.h"
+#include "common/utils.h"
+#include "lttng/condition/event-rule.h"
+#include "lttng/event-internal.h"
+#include <lttng/event-rule/event-rule-internal.h>
+#include "lttng/event-rule/kprobe.h"
+#include "lttng/event-rule/syscall.h"
+#include <lttng/event-rule/tracepoint.h>
+#include "lttng/event-rule/uprobe.h"
+
+#ifdef LTTNG_EMBED_HELP
+static const char help_msg[] =
+#include <lttng-add-trigger.1.h>
+;
+#endif
+
+enum {
+       OPT_HELP,
+       OPT_LIST_OPTIONS,
+
+       OPT_CONDITION,
+       OPT_ACTION,
+       OPT_ID,
+       OPT_FIRE_ONCE_AFTER,
+       OPT_FIRE_EVERY,
+
+       OPT_ALL,
+       OPT_FILTER,
+       OPT_EXCLUDE,
+       OPT_LOGLEVEL,
+       OPT_LOGLEVEL_ONLY,
+
+       OPT_USERSPACE,
+       OPT_KERNEL,
+       OPT_LOG4J,
+       OPT_JUL,
+       OPT_PYTHON,
+
+       OPT_FUNCTION,
+       OPT_PROBE,
+       OPT_USERSPACE_PROBE,
+       OPT_SYSCALL,
+       OPT_TRACEPOINT,
+
+       OPT_NAME,
+       OPT_MAX_SIZE,
+       OPT_DATA_URL,
+       OPT_CTRL_URL,
+};
+
+static const struct argpar_opt_descr event_rule_opt_descrs[] = {
+       { OPT_ALL, 'a', "all", false },
+       { OPT_FILTER, 'f', "filter", true },
+       { OPT_EXCLUDE, 'x', "exclude", true },
+       { OPT_LOGLEVEL, '\0', "loglevel", true },
+       { OPT_LOGLEVEL_ONLY, '\0', "loglevel-only", true },
+
+       /* Domains */
+       { OPT_USERSPACE, 'u', "userspace", false },
+       { OPT_KERNEL, 'k', "kernel", false },
+       { OPT_LOG4J, 'l', "log4j", false },
+       { OPT_JUL, 'j', "jul", false },
+       { OPT_PYTHON, 'p', "python", false },
+
+       /* Event rule types */
+       { OPT_FUNCTION, '\0', "function", true },
+       { OPT_PROBE, '\0', "probe", true },
+       { OPT_USERSPACE_PROBE, '\0', "userspace-probe", true },
+       { OPT_SYSCALL, '\0', "syscall" },
+       { OPT_TRACEPOINT, '\0', "tracepoint" },
+
+       ARGPAR_OPT_DESCR_SENTINEL
+};
+
+static
+bool assign_domain_type(enum lttng_domain_type *dest,
+               enum lttng_domain_type src)
+{
+       bool ret;
+
+       if (*dest == LTTNG_DOMAIN_NONE || *dest == src) {
+               *dest = src;
+               ret = true;
+       } else {
+               fprintf(stderr, "Error: Multiple domains specified.\n");
+               ret = false;
+       }
+
+       return ret;
+}
+
+static
+bool assign_event_rule_type(enum lttng_event_rule_type *dest,
+               enum lttng_event_rule_type src)
+{
+       bool ret;
+
+       if (*dest == LTTNG_EVENT_RULE_TYPE_UNKNOWN || *dest == src) {
+               *dest = src;
+               ret = true;
+       } else {
+               fprintf(stderr, "Error: Multiple event type not supported.\n");
+               ret = false;
+       }
+
+       return ret;
+}
+
+static
+bool assign_string(char **dest, const char *src, const char *opt_name)
+{
+       bool ret;
+
+       if (*dest) {
+               fprintf(stderr,
+                       "Duplicate %s given.\n", opt_name);
+               goto error;
+       }
+
+       *dest = strdup(src);
+       if (!*dest) {
+               fprintf(stderr, "Failed to allocate %s string.\n", opt_name);
+               goto error;
+       }
+
+       ret = true;
+       goto end;
+
+error:
+       ret = false;
+
+end:
+       return ret;
+}
+
+/* This is defined in enable_events.c. */
+LTTNG_HIDDEN
+int create_exclusion_list_and_validate(const char *event_name,
+               const char *exclusions_arg,
+               char ***exclusion_list);
+
+/*
+ * Parse `str` as a log level in domain `domain_type`.  Return -1 if the string
+ * is not recognized as a valid log level.
+ */
+static
+int parse_loglevel_string(const char *str, enum lttng_domain_type domain_type)
+{
+
+       switch (domain_type) {
+       case LTTNG_DOMAIN_UST:
+               return loglevel_str_to_value(str);
+
+       case LTTNG_DOMAIN_LOG4J:
+               return loglevel_log4j_str_to_value(str);
+
+       case LTTNG_DOMAIN_JUL:
+               return loglevel_jul_str_to_value(str);
+
+       case LTTNG_DOMAIN_PYTHON:
+               return loglevel_python_str_to_value(str);
+
+       default:
+               /* Invalid domain type. */
+               abort();
+       }
+}
+
+static
+struct lttng_event_rule *parse_event_rule(int *argc, const char ***argv)
+{
+       struct lttng_event_rule *er = NULL;
+       enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
+       enum lttng_event_rule_type event_rule_type = LTTNG_EVENT_RULE_TYPE_UNKNOWN;
+       struct argpar_state *state;
+       struct argpar_item *item = NULL;
+       char *error = NULL;
+       int consumed_args = -1;
+       struct lttng_userspace_probe_location *userspace_probe_location = NULL;
+
+       /* Was the -a/--all flag provided? */
+       bool all_events = false;
+
+       /* Tracepoint name (non-option argument) */
+       const char *tracepoint_name = NULL;
+
+       /* Holds the argument of --probe / --userspace-probe. */
+       char *source = NULL;
+
+       /* Filter */
+       char *filter = NULL;
+
+       /* Exclude */
+       char *exclude = NULL;
+       char **exclusion_list = NULL;
+
+       /* Log level */
+       char *loglevel_str = NULL;
+       bool loglevel_only = false;
+
+       state = argpar_state_create(*argc, *argv, event_rule_opt_descrs);
+       if (!state) {
+               fprintf(stderr, "Failed to allocate an argpar state.\n");
+               goto error;
+       }
+
+       while (true) {
+               enum argpar_state_parse_next_status status;
+
+               ARGPAR_ITEM_DESTROY_AND_RESET(item);
+               status = argpar_state_parse_next(state, &item, &error);
+               if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+                       fprintf(stderr, "Error: %s\n", error);
+                       goto error;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+                       /* Just stop parsing here. */
+                       break;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+                       break;
+               }
+
+               assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+               if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+                       struct argpar_item_opt *item_opt =
+                               (struct argpar_item_opt *) item;
+
+                       switch (item_opt->descr->id) {
+                       /* Domains */
+                       case OPT_USERSPACE:
+                               if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_UST)) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_KERNEL:
+                               if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_KERNEL)) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_LOG4J:
+                               if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_LOG4J)) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_JUL:
+                               if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_JUL)) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_PYTHON:
+                               if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_PYTHON)) {
+                                       goto error;
+                               }
+                               break;
+
+                       /* Event rule types */
+                       case OPT_FUNCTION:
+                               if (!assign_event_rule_type(&event_rule_type,
+                                               LTTNG_EVENT_RULE_TYPE_KRETPROBE)) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_PROBE:
+                               if (!assign_event_rule_type(&event_rule_type,
+                                               LTTNG_EVENT_RULE_TYPE_KPROBE)) {
+                                       goto error;
+                               }
+
+                               if (!assign_string(&source, item_opt->arg, "source")) {
+                                       goto error;
+                               }
+
+                               break;
+
+                       case OPT_USERSPACE_PROBE:
+                               if (!assign_event_rule_type(&event_rule_type,
+                                               LTTNG_EVENT_RULE_TYPE_UPROBE)) {
+                                       goto error;
+                               }
+
+                               if (!assign_string(&source, item_opt->arg, "source")) {
+                                               goto error;
+                               }
+                               break;
+
+                       case OPT_SYSCALL:
+                               if (!assign_event_rule_type(&event_rule_type,
+                                               LTTNG_EVENT_RULE_TYPE_SYSCALL)) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_TRACEPOINT:
+                               if (!assign_event_rule_type(&event_rule_type,
+                                               LTTNG_EVENT_RULE_TYPE_TRACEPOINT)) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_ALL:
+                               all_events = true;
+                               break;
+
+                       case OPT_FILTER:
+                               if (!assign_string(&filter, item_opt->arg, "--filter/-f")) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_EXCLUDE:
+                               if (!assign_string(&exclude, item_opt->arg, "--exclude/-x")) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_LOGLEVEL:
+                       case OPT_LOGLEVEL_ONLY:
+                               if (!assign_string(&loglevel_str, item_opt->arg, "--loglevel/--loglevel-only")) {
+                                       goto error;
+                               }
+
+                               loglevel_only = item_opt->descr->id == OPT_LOGLEVEL_ONLY;
+                               break;
+
+                       default:
+                               abort();
+                       }
+               } else {
+                       struct argpar_item_non_opt *item_non_opt =
+                               (struct argpar_item_non_opt *) item;
+
+                       /*
+                        * Don't accept two non-option arguments/tracepoint
+                        * names.
+                        */
+                       if (tracepoint_name) {
+                               fprintf(stderr,
+                                       "Error: Unexpected argument: %s\n",
+                                       item_non_opt->arg);
+                               goto error;
+                       }
+
+                       tracepoint_name = item_non_opt->arg;
+               }
+       }
+
+       if (event_rule_type == LTTNG_EVENT_RULE_TYPE_UNKNOWN) {
+               event_rule_type = LTTNG_EVENT_RULE_TYPE_TRACEPOINT;
+       }
+
+       /*
+        * Option -a is applicable to event rules of type tracepoint and
+        * syscall, and it is equivalent to using "*" as the tracepoint name.
+        */
+       if (all_events) {
+               switch (event_rule_type) {
+               case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+               case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+                       break;
+               default:
+                       fprintf(stderr, "Error: Can't use -a/--all with event rule of type %s.\n",
+                               lttng_event_rule_type_str(event_rule_type));
+                       goto error;
+               }
+
+               if (tracepoint_name) {
+                       fprintf(stderr, "Error: Can't provide a tracepoint name with -a/--all.\n");
+                       goto error;
+               }
+
+               /* In which case, it's equivalent to tracepoint name "*". */
+               tracepoint_name = "*";
+       }
+
+       /*
+        * A tracepoint name (or -a, for the event rule types that accept it)
+        * is required.
+        */
+       if (!tracepoint_name) {
+               fprintf(stderr, "Error: Need to provide either a tracepoint name or -a/--all.\n");
+               goto error;
+       }
+
+       /*
+        * We don't support multiple tracepoint names for now.
+        */
+       if (strchr(tracepoint_name, ',')) {
+               fprintf(stderr, "Error: multiple tracepoint names are not supported at the moment.\n");
+               goto error;
+       }
+
+       /*
+        * Update *argc and *argv so our caller can keep parsing what follows.
+        */
+       consumed_args = argpar_state_get_ingested_orig_args(state);
+       assert(consumed_args >= 0);
+       *argc -= consumed_args;
+       *argv += consumed_args;
+
+       /* Need to specify a domain. */
+       if (domain_type == LTTNG_DOMAIN_NONE) {
+               fprintf(stderr, "Error: Please specify a domain (-k/-u/-j).\n");
+               goto error;
+       }
+
+       /* Validate event rule type against domain. */
+       switch (event_rule_type) {
+       case LTTNG_EVENT_RULE_TYPE_KPROBE:
+       case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+       case LTTNG_EVENT_RULE_TYPE_UPROBE:
+       case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+               if (domain_type != LTTNG_DOMAIN_KERNEL) {
+                       fprintf(stderr, "Error: Event type not available for user-space tracing\n");
+                       goto error;
+               }
+               break;
+
+       case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+               break;
+
+       default:
+               abort();
+       }
+
+       /*
+        * Adding a filter to a probe, function or userspace-probe would be
+        * denied by the kernel tracer as it's not supported at the moment. We
+        * do an early check here to warn the user.
+        */
+       if (filter && domain_type == LTTNG_DOMAIN_KERNEL) {
+               switch (event_rule_type) {
+               case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+               case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+                       break;
+               default:
+                       ERR("Filter expressions are not supported for %s events",
+                                       lttng_event_rule_type_str(event_rule_type));
+                       goto error;
+               }
+       }
+
+       /* If --exclude/-x was passed, split it into an exclusion list. */
+       if (exclude) {
+               if (domain_type != LTTNG_DOMAIN_UST) {
+                       fprintf(stderr, "Event name exclusions are not yet implemented for %s events",
+                                               get_domain_str(domain_type));
+                       goto error;
+               }
+
+
+               if (create_exclusion_list_and_validate(tracepoint_name, exclude,
+                               &exclusion_list) != 0) {
+                       fprintf(stderr, "Failed to create exclusion list.\n");
+                       goto error;
+               }
+       }
+
+       if (loglevel_str && event_rule_type != LTTNG_EVENT_RULE_TYPE_TRACEPOINT) {
+               fprintf(stderr, "Log levels are only application to tracepoint event rules.\n");
+               goto error;
+       }
+
+       /* Finally, create the event rule object. */
+       switch (event_rule_type) {
+       case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+       {
+               enum lttng_event_rule_status event_rule_status;
+
+               er = lttng_event_rule_tracepoint_create(domain_type);
+               if (!er) {
+                       fprintf(stderr, "Failed to create tracepoint event rule.\n");
+                       goto error;
+               }
+
+               /* Set pattern. */
+               event_rule_status =
+                       lttng_event_rule_tracepoint_set_pattern(er, tracepoint_name);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       fprintf(stderr, "Failed to set tracepoint pattern.\n");
+                       goto error;
+               }
+
+               /* Set filter. */
+               if (filter) {
+                       event_rule_status =
+                               lttng_event_rule_tracepoint_set_filter(
+                                       er, filter);
+                       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                               fprintf(stderr, "Failed to set tracepoint filter expression.\n");
+                               goto error;
+                       }
+               }
+
+               /* Set exclusion list. */
+               if (exclusion_list) {
+                       int n;
+
+                       /* Count number of items in exclusion list. */
+                       for (n = 0; exclusion_list[n]; n++);
+
+                       event_rule_status =
+                               lttng_event_rule_tracepoint_set_exclusions(er,
+                                       n, (const char **) exclusion_list);
+                       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                               fprintf(stderr, "Failed to set tracepoint exclusion list.\n");
+                               goto error;
+                       }
+               }
+
+               if (loglevel_str) {
+                       int loglevel;
+
+                       if (domain_type == LTTNG_DOMAIN_KERNEL) {
+                               fprintf(stderr, "Log levels are not supported by the kernel tracer.\n");
+                               goto error;
+                       }
+
+                       loglevel = parse_loglevel_string(
+                               loglevel_str, domain_type);
+                       if (loglevel < 0) {
+                               fprintf(stderr, "Failed to parse `%s` as a log level.\n", loglevel_str);
+                               goto error;
+                       }
+
+                       if (loglevel_only) {
+                               event_rule_status =
+                                       lttng_event_rule_tracepoint_set_loglevel(
+                                               er, loglevel);
+                       } else {
+                               event_rule_status =
+                                       lttng_event_rule_tracepoint_set_loglevel_range(
+                                               er, loglevel);
+                       }
+
+                       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                               fprintf(stderr, "Failed to set log level.\n");
+                               goto error;
+                       }
+               }
+
+               break;
+       }
+
+       case LTTNG_EVENT_RULE_TYPE_KPROBE:
+       {
+               enum lttng_event_rule_status event_rule_status;
+
+               er = lttng_event_rule_kprobe_create();
+               if (!er) {
+                       fprintf(stderr, "Failed to create kprobe event rule.\n");
+                       goto error;
+               }
+
+               event_rule_status = lttng_event_rule_kprobe_set_name(er, tracepoint_name);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       fprintf(stderr, "Failed to set kprobe event rule's name.\n");
+                       goto error;
+               }
+
+               assert(source);
+               event_rule_status = lttng_event_rule_kprobe_set_source(er, source);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       fprintf(stderr, "Failed to set kprobe event rule's source.\n");
+                       goto error;
+               }
+
+               break;
+       }
+
+       case LTTNG_EVENT_RULE_TYPE_UPROBE:
+       {
+               int ret;
+               enum lttng_event_rule_status event_rule_status;
+
+               ret = parse_userspace_probe_opts(source, &userspace_probe_location);
+               if (ret) {
+                       fprintf(stderr, "Failed to parse userspace probe location.\n");
+                       goto error;
+               }
+
+               er = lttng_event_rule_uprobe_create();
+               if (!er) {
+                       fprintf(stderr, "Failed to create userspace probe event rule.\n");
+                       goto error;
+               }
+
+               event_rule_status = lttng_event_rule_uprobe_set_location(er, userspace_probe_location);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       fprintf(stderr, "Failed to set userspace probe event rule's location.\n");
+                       goto error;
+               }
+
+               event_rule_status = lttng_event_rule_uprobe_set_name(er, tracepoint_name);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       fprintf(stderr, "Failed to set userspace probe event rule's name.\n");
+                       goto error;
+               }
+
+               break;
+       }
+
+       case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+       {
+               enum lttng_event_rule_status event_rule_status;
+
+               er = lttng_event_rule_syscall_create();
+               if (!er) {
+                       fprintf(stderr, "Failed to create syscall event rule.\n");
+                       goto error;
+               }
+
+               event_rule_status = lttng_event_rule_syscall_set_pattern(er, tracepoint_name);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       fprintf(stderr, "Failed to set syscall event rule's pattern.\n");
+                       goto error;
+               }
+
+               if (filter) {
+                       event_rule_status = lttng_event_rule_syscall_set_filter(
+                                       er, filter);
+                       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                               fprintf(stderr, "Failed to set syscall event rule's filter expression.\n");
+                               goto error;
+                       }
+               }
+
+               break;
+       }
+
+       default:
+               fprintf(stderr, "%s: I don't support event rules of type `%s` at the moment.\n", __func__,
+                       lttng_event_rule_type_str(event_rule_type));
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_event_rule_destroy(er);
+       er = NULL;
+
+end:
+       argpar_item_destroy(item);
+       free(error);
+       argpar_state_destroy(state);
+       free(filter);
+       free(exclude);
+       free(loglevel_str);
+       strutils_free_null_terminated_array_of_strings(exclusion_list);
+       lttng_userspace_probe_location_destroy(userspace_probe_location);
+       return er;
+}
+
+static
+struct lttng_condition *handle_condition_event(int *argc, const char ***argv)
+{
+       struct lttng_event_rule *er;
+       struct lttng_condition *c;
+
+       er = parse_event_rule(argc, argv);
+       if (!er) {
+               c = NULL;
+               goto end;
+       }
+
+       c = lttng_condition_event_rule_create(er);
+       if (!c) {
+               goto end;
+       }
+
+end:
+       return c;
+}
+
+static
+struct lttng_condition *handle_condition_session_consumed_size(int *argc, const char ***argv)
+{
+       struct lttng_condition *cond = NULL;
+       struct argpar_state *state = NULL;
+       struct argpar_item *item = NULL;
+       const char *threshold_arg = NULL;
+       const char *session_name_arg = NULL;
+       uint64_t threshold;
+       char *error = NULL;
+       enum lttng_condition_status condition_status;
+
+       state = argpar_state_create(*argc, *argv, event_rule_opt_descrs);
+       if (!state) {
+               fprintf(stderr, "Failed to allocate an argpar state.\n");
+               goto error;
+       }
+
+       while (true) {
+               enum argpar_state_parse_next_status status;
+
+               ARGPAR_ITEM_DESTROY_AND_RESET(item);
+               status = argpar_state_parse_next(state, &item, &error);
+               if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+                       fprintf(stderr, "Error: %s\n", error);
+                       goto error;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+                       /* Just stop parsing here. */
+                       break;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+                       break;
+               }
+
+               assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+               if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+                       struct argpar_item_opt *item_opt =
+                               (struct argpar_item_opt *) item;
+
+                       switch (item_opt->descr->id) {
+                       default:
+                               abort();
+                       }
+               } else {
+                       struct argpar_item_non_opt *item_non_opt;
+
+                       assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
+
+                       item_non_opt = (struct argpar_item_non_opt *) item;
+
+                       switch (item_non_opt->non_opt_index) {
+                       case 0:
+                               session_name_arg = item_non_opt->arg;
+                               break;
+                       case 1:
+                               threshold_arg = item_non_opt->arg;
+                               break;
+                       default:
+                               fprintf(stderr, "Unexpected argument `%s`.\n",
+                                       item_non_opt->arg);
+                               goto error;
+                       }
+               }
+       }
+
+       *argc -= argpar_state_get_ingested_orig_args(state);
+       *argv += argpar_state_get_ingested_orig_args(state);
+
+       if (!session_name_arg) {
+               fprintf(stderr, "Missing session name argument.\n");
+               goto error;
+       }
+
+       if (!threshold_arg) {
+               fprintf(stderr, "Missing threshold argument.\n");
+               goto error;
+       }
+
+       if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) {
+               fprintf(stderr, "Failed to parse `%s` as a size.\n", threshold_arg);
+               goto error;
+       }
+
+       cond = lttng_condition_session_consumed_size_create();
+       if (!cond) {
+               fprintf(stderr, "Failed to allocate a session consumed size condition.\n");
+               goto error;
+       }
+
+       condition_status = lttng_condition_session_consumed_size_set_session_name(
+               cond, session_name_arg);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               fprintf(stderr, "Failed to set session consumed size condition session name.\n");
+               goto error;
+       }
+
+
+       condition_status = lttng_condition_session_consumed_size_set_threshold(
+               cond, threshold);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               fprintf(stderr, "Failed to set session consumed size condition threshold.\n");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_condition_destroy(cond);
+       cond = NULL;
+
+end:
+       argpar_state_destroy(state);
+       argpar_item_destroy(item);
+       free(error);
+       return cond;
+}
+
+static
+struct lttng_condition *handle_condition_buffer_usage_high(int *argc, const char ***argv)
+{
+       struct lttng_condition *cond = NULL;
+       struct argpar_state *state = NULL;
+       struct argpar_item *item = NULL;
+       const char *threshold_arg = NULL;
+       const char *session_name_arg = NULL;
+       uint64_t threshold;
+       char *error = NULL;
+       enum lttng_condition_status condition_status;
+
+       state = argpar_state_create(*argc, *argv, event_rule_opt_descrs);
+       if (!state) {
+               fprintf(stderr, "Failed to allocate an argpar state.\n");
+               goto error;
+       }
+
+       while (true) {
+               enum argpar_state_parse_next_status status;
+
+               ARGPAR_ITEM_DESTROY_AND_RESET(item);
+               status = argpar_state_parse_next(state, &item, &error);
+               if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+                       fprintf(stderr, "Error: %s\n", error);
+                       goto error;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+                       /* Just stop parsing here. */
+                       break;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+                       break;
+               }
+
+               assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+               if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+                       struct argpar_item_opt *item_opt =
+                               (struct argpar_item_opt *) item;
+
+                       switch (item_opt->descr->id) {
+                       default:
+                               abort();
+                       }
+               } else {
+                       struct argpar_item_non_opt *item_non_opt;
+
+                       assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
+
+                       item_non_opt = (struct argpar_item_non_opt *) item;
+
+                       switch (item_non_opt->non_opt_index) {
+                       case 0:
+                               session_name_arg = item_non_opt->arg;
+                               break;
+                       case 1:
+                               threshold_arg = item_non_opt->arg;
+                               break;
+                       default:
+                               fprintf(stderr, "Unexpected argument `%s`.\n",
+                                       item_non_opt->arg);
+                               goto error;
+                       }
+               }
+       }
+
+       *argc -= argpar_state_get_ingested_orig_args(state);
+       *argv += argpar_state_get_ingested_orig_args(state);
+
+       if (!session_name_arg) {
+               fprintf(stderr, "Missing session name argument.\n");
+               goto error;
+       }
+
+       if (!threshold_arg) {
+               fprintf(stderr, "Missing threshold argument.\n");
+               goto error;
+       }
+
+       if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) {
+               fprintf(stderr, "Failed to parse `%s` as a size.\n", threshold_arg);
+               goto error;
+       }
+
+       cond = lttng_condition_session_consumed_size_create();
+       if (!cond) {
+               fprintf(stderr, "Failed to allocate a session consumed size condition.\n");
+               goto error;
+       }
+
+       condition_status = lttng_condition_session_consumed_size_set_session_name(
+               cond, session_name_arg);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               fprintf(stderr, "Failed to set session consumed size condition session name.\n");
+               goto error;
+       }
+
+       condition_status = lttng_condition_session_consumed_size_set_threshold(
+               cond, threshold);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               fprintf(stderr, "Failed to set session consumed size condition threshold.\n");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_condition_destroy(cond);
+       cond = NULL;
+
+end:
+       argpar_state_destroy(state);
+       argpar_item_destroy(item);
+       free(error);
+       return cond;
+}
+
+static
+struct lttng_condition *handle_condition_buffer_usage_low(int *argc, const char ***argv)
+{
+       return NULL;
+}
+
+static
+struct lttng_condition *handle_condition_session_rotation_ongoing(int *argc, const char ***argv)
+{
+       return NULL;
+}
+
+static
+struct lttng_condition *handle_condition_session_rotation_completed(int *argc, const char ***argv)
+{
+       return NULL;
+}
+
+struct condition_descr {
+       const char *name;
+       struct lttng_condition *(*handler) (int *argc, const char ***argv);
+};
+
+static const
+struct condition_descr condition_descrs[] = {
+       { "on-event", handle_condition_event },
+       { "on-session-consumed-size", handle_condition_session_consumed_size },
+       { "on-buffer-usage-high", handle_condition_buffer_usage_high },
+       { "on-buffer-usage-low", handle_condition_buffer_usage_low },
+       { "on-session-rotation-ongoing", handle_condition_session_rotation_ongoing },
+       { "on-session-rotation-completed", handle_condition_session_rotation_completed },
+};
+
+static
+struct lttng_condition *parse_condition(int *argc, const char ***argv)
+{
+       int i;
+       struct lttng_condition *cond;
+       const char *condition_name;
+       const struct condition_descr *descr = NULL;
+
+       if (*argc == 0) {
+               fprintf(stderr, "Missing condition name.\n");
+               goto error;
+       }
+
+       condition_name = (*argv)[0];
+
+       (*argc)--;
+       (*argv)++;
+
+       for (i = 0; i < ARRAY_SIZE(condition_descrs); i++) {
+               if (strcmp(condition_name, condition_descrs[i].name) == 0) {
+                       descr = &condition_descrs[i];
+                       break;
+               }
+       }
+
+       if (!descr) {
+               fprintf(stderr, "Unknown condition name: %s\n", condition_name);
+               goto error;
+       }
+
+       cond = descr->handler(argc, argv);
+       if (!cond) {
+               /* The handler has already printed an error message. */
+               goto error;
+       }
+
+       goto end;
+error:
+       cond = NULL;
+end:
+       return cond;
+}
+
+
+static
+struct lttng_action *handle_action_notify(int *argc, const char ***argv)
+{
+       return lttng_action_notify_create();
+}
+
+static const struct argpar_opt_descr no_opt_descrs[] = {
+       ARGPAR_OPT_DESCR_SENTINEL
+};
+
+/*
+ * Generic handler for a kind of action that takes a session name as its sole
+ * argument.
+ */
+
+static
+struct lttng_action *handle_action_simple_session(
+               int *argc, const char ***argv,
+               struct lttng_action *(*create_action_cb)(void),
+               enum lttng_action_status (*set_session_name_cb)(struct lttng_action *, const char *),
+               const char *action_name)
+{
+       struct lttng_action *action = NULL;
+       struct argpar_state *state = NULL;
+       struct argpar_item *item = NULL;
+       const char *session_name_arg = NULL;
+       char *error = NULL;
+       enum lttng_action_status action_status;
+
+       state = argpar_state_create(*argc, *argv, no_opt_descrs);
+       if (!state) {
+               fprintf(stderr, "Failed to allocate an argpar state.\n");
+               goto error;
+       }
+
+       while (true) {
+               enum argpar_state_parse_next_status status;
+               struct argpar_item_non_opt *item_non_opt;
+
+               ARGPAR_ITEM_DESTROY_AND_RESET(item);
+               status = argpar_state_parse_next(state, &item, &error);
+               if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+                       fprintf(stderr, "Error: %s\n", error);
+                       goto error;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+                       /* Just stop parsing here. */
+                       break;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+                       break;
+               }
+
+               assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+               assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
+
+               item_non_opt = (struct argpar_item_non_opt *) item;
+
+               switch (item_non_opt->non_opt_index) {
+               case 0:
+                       session_name_arg = item_non_opt->arg;
+                       break;
+               default:
+                       fprintf(stderr, "Unexpected argument `%s`.\n",
+                               item_non_opt->arg);
+                       goto error;
+               }
+       }
+
+       *argc -= argpar_state_get_ingested_orig_args(state);
+       *argv += argpar_state_get_ingested_orig_args(state);
+
+       if (!session_name_arg) {
+               fprintf(stderr, "Missing session name.\n");
+               goto error;
+       }
+
+       action = create_action_cb();
+       if (!action) {
+               fprintf(stderr,
+                       "Failed to allocate %s session action.\n", action_name);
+               goto error;
+       }
+
+       action_status = set_session_name_cb(action, session_name_arg);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               fprintf(stderr,
+                       "Failed to set action %s session's session name.\n",
+                       action_name);
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_action_destroy(action);
+       action = NULL;
+
+end:
+       return action;
+}
+
+static
+struct lttng_action *handle_action_start_session(int *argc,
+               const char ***argv)
+{
+       return handle_action_simple_session(argc, argv,
+               lttng_action_start_session_create,
+               lttng_action_start_session_set_session_name,
+               "start");
+}
+
+static
+struct lttng_action *handle_action_stop_session(int *argc,
+               const char ***argv)
+{
+       return handle_action_simple_session(argc, argv,
+               lttng_action_stop_session_create,
+               lttng_action_stop_session_set_session_name,
+               "stop");
+}
+
+static
+struct lttng_action *handle_action_rotate_session(int *argc,
+               const char ***argv)
+{
+       return handle_action_simple_session(argc, argv,
+               lttng_action_rotate_session_create,
+               lttng_action_rotate_session_set_session_name,
+               "rotate");
+}
+
+static const struct argpar_opt_descr snapshot_action_opt_descrs[] = {
+       { OPT_NAME, 'n', "name", true },
+       { OPT_MAX_SIZE, 'm', "max-size", true },
+       { OPT_CTRL_URL, '\0', "ctrl-url", true },
+       { OPT_DATA_URL, '\0', "data-url", true },
+       ARGPAR_OPT_DESCR_SENTINEL
+};
+
+static
+struct lttng_action *handle_action_snapshot_session(int *argc,
+               const char ***argv)
+{
+       struct lttng_action *action = NULL;
+       struct argpar_state *state = NULL;
+       struct argpar_item *item = NULL;
+       const char *session_name_arg = NULL;
+       char *snapshot_name_arg = NULL;
+       char *ctrl_url_arg = NULL;
+       char *data_url_arg = NULL;
+       char *max_size_arg = NULL;
+       const char *url_arg = NULL;
+       char *error = NULL;
+       enum lttng_action_status action_status;
+       struct lttng_snapshot_output *snapshot_output = NULL;
+       int ret;
+
+       state = argpar_state_create(*argc, *argv, snapshot_action_opt_descrs);
+       if (!state) {
+               fprintf(stderr, "Failed to allocate an argpar state.\n");
+               goto error;
+       }
+
+       while (true) {
+               enum argpar_state_parse_next_status status;
+
+               ARGPAR_ITEM_DESTROY_AND_RESET(item);
+               status = argpar_state_parse_next(state, &item, &error);
+               if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+                       fprintf(stderr, "Error: %s\n", error);
+                       goto error;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+                       /* Just stop parsing here. */
+                       break;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+                       break;
+               }
+
+               assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+               if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+                       struct argpar_item_opt *item_opt =
+                               (struct argpar_item_opt *) item;
+
+                       switch (item_opt->descr->id) {
+                       case OPT_NAME:
+                               if (!assign_string(&snapshot_name_arg, item_opt->arg, "--name/-n")) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_MAX_SIZE:
+                               if (!assign_string(&max_size_arg, item_opt->arg, "--max-size/-m")) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_CTRL_URL:
+                               if (!assign_string(&ctrl_url_arg, item_opt->arg, "--ctrl-url")) {
+                                       goto error;
+                               }
+                               break;
+
+                       case OPT_DATA_URL:
+                               if (!assign_string(&data_url_arg, item_opt->arg, "--data-url")) {
+                                       goto error;
+                               }
+                               break;
+
+                       default:
+                               abort();
+                       }
+               } else {
+                       struct argpar_item_non_opt *item_non_opt;
+
+                       assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
+
+                       item_non_opt = (struct argpar_item_non_opt *) item;
+
+                       switch (item_non_opt->non_opt_index) {
+                       case 0:
+                               session_name_arg = item_non_opt->arg;
+                               break;
+
+                       // FIXME: the use of a non-option argument for this is to
+                       // follow the syntax of `lttng snapshot record`.  But otherwise,
+                       // I think an option argument would be best.
+                       case 1:
+                               url_arg = item_non_opt->arg;
+                               break;
+
+                       default:
+                               fprintf(stderr, "Unexpected argument `%s`.\n",
+                                       item_non_opt->arg);
+                               goto error;
+                       }
+               }
+       }
+
+       *argc -= argpar_state_get_ingested_orig_args(state);
+       *argv += argpar_state_get_ingested_orig_args(state);
+
+       if (!session_name_arg) {
+               fprintf(stderr, "Missing session name.\n");
+               goto error;
+       }
+
+       /* --ctrl-url and --data-url must come in pair. */
+       if (ctrl_url_arg && !data_url_arg) {
+               fprintf(stderr, "--ctrl-url is specified, but --data-url is missing.\n");
+               goto error;
+       }
+
+       if (!ctrl_url_arg && data_url_arg) {
+               fprintf(stderr, "--data-url is specified, but --ctrl-url is missing.\n");
+               goto error;
+       }
+
+       /* --ctrl-url/--data-url and the non-option URL are mutually exclusive. */
+       if (ctrl_url_arg && url_arg) {
+               fprintf(stderr, "Both --ctrl-url/--data-url and the non-option URL argument "
+                               "can't be used together.\n");
+               goto error;
+       }
+
+       /*
+        * Did the user specify an option that implies using a
+        * custom/unregistered output?
+        */
+       if (url_arg || ctrl_url_arg) {
+               snapshot_output = lttng_snapshot_output_create();
+               if (!snapshot_output) {
+                       fprintf(stderr, "Failed to allocate a snapshot output.\n");
+                       goto error;
+               }
+       }
+
+       action = lttng_action_snapshot_session_create();
+       if (!action) {
+               fprintf(stderr,
+                       "Failed to allocate snapshot session action.\n");
+               goto error;
+       }
+
+       action_status = lttng_action_snapshot_session_set_session_name(
+               action, session_name_arg);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               fprintf(stderr,
+                       "Failed to set action snapshot session's session name.\n");
+               goto error;
+       }
+
+       if (snapshot_name_arg) {
+               if (!snapshot_output) {
+                       fprintf(stderr, "Can't provide a snapshot output name without a snapshot output destination.\n");
+                       goto error;
+               }
+
+               ret = lttng_snapshot_output_set_name(snapshot_name_arg, snapshot_output);
+               if (ret != 0) {
+                       fprintf(stderr, "Failed to set name of snapshot output.\n");
+                       goto error;
+               }
+       }
+
+       if (max_size_arg) {
+               uint64_t max_size;
+
+               if (!snapshot_output) {
+                       fprintf(stderr, "Can't provide a snapshot output max size without a snapshot output destination.\n");
+                       goto error;
+               }
+
+               ret = utils_parse_size_suffix(max_size_arg, &max_size);
+               if (ret != 0) {
+                       fprintf(stderr, "Failed to parse `%s` as a size.\n", max_size_arg);
+                       goto error;
+               }
+
+               ret = lttng_snapshot_output_set_size(max_size, snapshot_output);
+               if (ret != 0) {
+                       fprintf(stderr, "Failed to set snapshot output's max size.\n");
+                       goto error;
+               }
+       }
+
+       if (url_arg) {
+               /* One argument form, either net:// / net6:// or a local file path. */
+
+               if (strncmp(url_arg, "net://", strlen("net://")) == 0 ||
+                               strncmp(url_arg, "net6://", strlen("net6://")) == 0) {
+                       ret = lttng_snapshot_output_set_network_url(
+                               url_arg, snapshot_output);
+                       if (ret != 0) {
+                               fprintf(stderr, "Failed to parse %s as a network URL.\n", url_arg);
+                               goto error;
+                       }
+               } else {
+                       ret = lttng_snapshot_output_set_local_path(
+                               url_arg, snapshot_output);
+                       if (ret != 0) {
+                               fprintf(stderr, "Failed to parse %s as a local path.\n", url_arg);
+                               goto error;
+                       }
+               }
+       }
+
+       if (ctrl_url_arg) {
+               /*
+                * Two argument form, network output with separate control and
+                * data URLs.
+                */
+               ret = lttng_snapshot_output_set_network_urls(
+                       ctrl_url_arg, data_url_arg, snapshot_output);
+               if (ret != 0) {
+                       fprintf(stderr, "Failed to parse `%s` and `%s` as control and data URLs.\n",
+                               ctrl_url_arg, data_url_arg);
+                       goto error;
+               }
+       }
+
+       if (snapshot_output) {
+               action_status = lttng_action_snapshot_session_set_output(
+                       action, snapshot_output);
+               if (action_status != LTTNG_ACTION_STATUS_OK) {
+                       fprintf(stderr, "Failed to set snapshot session action's output.\n");
+                       goto error;
+               }
+
+               /* Ownership of `snapshot_output` has been transferred to the action. */
+               snapshot_output = NULL;
+       }
+
+       goto end;
+
+error:
+       lttng_action_destroy(action);
+       action = NULL;
+
+end:
+       free(snapshot_name_arg);
+       free(ctrl_url_arg);
+       free(data_url_arg);
+       free(snapshot_output);
+       return action;
+}
+
+struct action_descr {
+       const char *name;
+       struct lttng_action *(*handler) (int *argc, const char ***argv);
+};
+
+static const
+struct action_descr action_descrs[] = {
+       { "notify", handle_action_notify },
+       { "start-session", handle_action_start_session },
+       { "stop-session", handle_action_stop_session },
+       { "rotate-session", handle_action_rotate_session },
+       { "snapshot-session", handle_action_snapshot_session },
+};
+
+static
+struct lttng_action *parse_action(int *argc, const char ***argv)
+{
+       int i;
+       struct lttng_action *action;
+       const char *action_name;
+       const struct action_descr *descr = NULL;
+
+       if (*argc == 0) {
+               fprintf(stderr, "Missing action name.\n");
+               goto error;
+       }
+
+       action_name = (*argv)[0];
+
+       (*argc)--;
+       (*argv)++;
+
+       for (i = 0; i < ARRAY_SIZE(action_descrs); i++) {
+               if (strcmp(action_name, action_descrs[i].name) == 0) {
+                       descr = &action_descrs[i];
+                       break;
+               }
+       }
+
+       if (!descr) {
+               fprintf(stderr, "Unknown action name: %s\n", action_name);
+               goto error;
+       }
+
+       action = descr->handler(argc, argv);
+       if (!action) {
+               /* The handler has already printed an error message. */
+               goto error;
+       }
+
+       goto end;
+error:
+       action = NULL;
+end:
+       return action;
+}
+
+static const
+struct argpar_opt_descr add_trigger_options[] = {
+       { OPT_HELP, 'h', "help", false },
+       { OPT_LIST_OPTIONS, '\0', "list-options", false },
+       { OPT_CONDITION, '\0', "condition", false },
+       { OPT_ACTION, '\0', "action", false },
+       { OPT_ID, '\0', "id", true },
+       { OPT_FIRE_ONCE_AFTER, '\0', "fire-once-after", true },
+       { OPT_FIRE_EVERY, '\0', "fire-every", true },
+       ARGPAR_OPT_DESCR_SENTINEL,
+};
+
+static
+void lttng_actions_destructor(void *p)
+{
+       struct lttng_action *action = p;
+
+       lttng_action_destroy(action);
+}
+
+int cmd_add_trigger(int argc, const char **argv)
+{
+       int ret;
+       int my_argc = argc - 1;
+       const char **my_argv = argv + 1;
+       struct lttng_condition *condition = NULL;
+       struct lttng_dynamic_pointer_array actions;
+       struct argpar_state *argpar_state = NULL;
+       struct argpar_item *argpar_item = NULL;
+       struct lttng_action *action_group = NULL;
+       struct lttng_action *action = NULL;
+       struct lttng_trigger *trigger = NULL;
+       char *error = NULL;
+       char *id = NULL;
+       int i;
+       char *fire_once_after_str = NULL;
+       char *fire_every_str = NULL;
+
+       lttng_dynamic_pointer_array_init(&actions, lttng_actions_destructor);
+
+       while (true) {
+               enum argpar_state_parse_next_status status;
+               struct argpar_item_opt *item_opt;
+               int ingested_args;
+
+               argpar_state_destroy(argpar_state);
+               argpar_state = argpar_state_create(my_argc, my_argv,
+                       add_trigger_options);
+               if (!argpar_state) {
+                       fprintf(stderr, "Failed to create argpar state.\n");
+                       goto error;
+               }
+
+               status = argpar_state_parse_next(argpar_state, &argpar_item, &error);
+
+               if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+                       fprintf(stderr, "Error: %s\n", error);
+                       goto error;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+                       fprintf(stderr, "%s\n", error);
+                       goto error;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+                       break;
+               }
+
+               assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+               if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) {
+                       struct argpar_item_non_opt *item_non_opt =
+                               (struct argpar_item_non_opt *) argpar_item;
+
+                       fprintf(stderr, "Unexpected argument `%s`.\n",
+                               item_non_opt->arg);
+                       goto error;
+               }
+
+               item_opt = (struct argpar_item_opt *) argpar_item;
+
+               ingested_args = argpar_state_get_ingested_orig_args(
+                       argpar_state);
+
+               my_argc -= ingested_args;
+               my_argv += ingested_args;
+
+               switch (item_opt->descr->id) {
+               case OPT_HELP:
+                       SHOW_HELP();
+                       ret = 0;
+                       goto end;
+
+               case OPT_LIST_OPTIONS:
+                       list_cmd_options_argpar(stdout, add_trigger_options);
+                       ret = 0;
+                       goto end;
+
+               case OPT_CONDITION:
+               {
+                       if (condition) {
+                               fprintf(stderr, "A --condition was already given.\n");
+                               goto error;
+                       }
+
+                       condition = parse_condition(&my_argc, &my_argv);
+                       if (!condition) {
+                               /*
+                                * An error message was already printed by
+                                * parse_condition.
+                                */
+                               goto error;
+                       }
+
+                       break;
+               }
+
+               case OPT_ACTION:
+               {
+                       action = parse_action(&my_argc, &my_argv);
+                       if (!action) {
+                               /*
+                                * An error message was already printed by
+                                * parse_condition.
+                                */
+                               goto error;
+                       }
+
+                       ret = lttng_dynamic_pointer_array_add_pointer(
+                               &actions, action);
+                       if (ret) {
+                               fprintf(stderr, "Failed to add pointer to pointer array.\n");
+                               goto error;
+                       }
+
+                       /* Ownership of the action was transferred to the group. */
+                       action = NULL;
+
+                       break;
+               }
+
+               case OPT_ID:
+               {
+                       if (!assign_string(&id, item_opt->arg, "--id")) {
+                               goto error;
+                       }
+
+                       break;
+               }
+
+               case OPT_FIRE_ONCE_AFTER:
+               {
+                       if (!assign_string(&fire_once_after_str, item_opt->arg,
+                                       "--fire-once-after")) {
+                               goto error;
+                       }
+                       break;
+               }
+
+               case OPT_FIRE_EVERY:
+               {
+                       if (!assign_string(&fire_every_str, item_opt->arg,
+                                       "--fire-every")) {
+                               goto error;
+                       }
+                       break;
+               }
+
+               default:
+                       abort();
+               }
+       }
+
+       if (!condition) {
+               fprintf(stderr, "Missing --condition.\n");
+               goto error;
+       }
+
+       if (lttng_dynamic_pointer_array_get_count(&actions) == 0) {
+               fprintf(stderr, "Need at least one --action.\n");
+               goto error;
+       }
+
+       if (fire_every_str && fire_once_after_str) {
+               fprintf(stderr, "Can't specify both --fire-once-after and --fire-every.\n");
+               goto error;
+       }
+
+       action_group = lttng_action_group_create();
+       if (!action_group) {
+               goto error;
+       }
+
+       for (i = 0; i < lttng_dynamic_pointer_array_get_count(&actions); i++) {
+               enum lttng_action_status status;
+
+               action = lttng_dynamic_pointer_array_steal_pointer(&actions, i);
+
+               status = lttng_action_group_add_action(
+                       action_group, action);
+               if (status != LTTNG_ACTION_STATUS_OK) {
+                       goto error;
+               }
+
+               /* Ownership of the action was transferred to the group. */
+               action = NULL;
+       }
+
+
+       trigger = lttng_trigger_create(condition, action_group);
+       if (!trigger) {
+               goto error;
+       }
+
+       /*
+        * Ownership of the condition and action group was transferred to the
+        * trigger.
+        */
+       condition = NULL;
+       action_group = NULL;
+
+       if (id) {
+               enum lttng_trigger_status trigger_status =
+                       lttng_trigger_set_name(trigger, id);
+               if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+                       fprintf(stderr, "Failed to set trigger id.\n");
+                       goto error;
+               }
+       }
+
+       if (fire_once_after_str) {
+               unsigned long long threshold;
+               enum lttng_trigger_status trigger_status;
+
+               if (utils_parse_unsigned_long_long(fire_once_after_str, &threshold) != 0) {
+                       fprintf(stderr, "Failed to parse `%s` as an integer.\n", fire_once_after_str);
+                       goto error;
+               }
+
+               trigger_status = lttng_trigger_set_firing_policy(trigger,
+                       LTTNG_TRIGGER_FIRE_ONCE_AFTER_N, threshold);
+               if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+                       fprintf(stderr, "Failed to set trigger's firing policy.\n");
+                       goto error;
+               }
+       }
+
+       if (fire_every_str) {
+               unsigned long long threshold;
+               enum lttng_trigger_status trigger_status;
+
+               if (utils_parse_unsigned_long_long(fire_every_str, &threshold) != 0) {
+                       fprintf(stderr, "Failed to parse `%s` as an integer.\n", fire_every_str);
+                       goto error;
+               }
+
+               trigger_status = lttng_trigger_set_firing_policy(trigger,
+                       LTTNG_TRIGGER_FIRE_EVERY_N, threshold);
+               if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+                       fprintf(stderr, "Failed to set trigger's firing policy.\n");
+                       goto error;
+               }
+       }
+
+       ret = lttng_register_trigger(trigger);
+       if (ret) {
+               fprintf(stderr, "Failed to register trigger: %s.\n",
+                       lttng_strerror(ret));
+               goto error;
+       }
+
+       printf("Trigger registered successfully.\n");
+
+       goto end;
+
+error:
+       ret = 1;
+
+end:
+       argpar_state_destroy(argpar_state);
+       lttng_dynamic_pointer_array_reset(&actions);
+       lttng_condition_destroy(condition);
+       lttng_action_destroy(action_group);
+       lttng_trigger_destroy(trigger);
+       free(id);
+       free(fire_once_after_str);
+       free(fire_every_str);
+       // TODO: check what else to free
+
+       return ret;
+}
index 406bfb62ac32e0a64b6b98628a4948e8f4f83c87..379ca0a660908c1e3454fafd3d193b83e029e081 100644 (file)
@@ -501,7 +501,7 @@ error:
  *
  *  Spawn a session daemon by forking and execv.
  */
-static int spawn_sessiond(char *pathname)
+static int spawn_sessiond(const char *pathname)
 {
        int ret = 0;
        pid_t pid;
@@ -580,7 +580,7 @@ end:
 static int launch_sessiond(void)
 {
        int ret;
-       char *pathname = NULL;
+       const char *pathname = NULL;
 
        ret = lttng_session_daemon_alive();
        if (ret) {
index 9909fec39c7ca49a7b9d6f75c1a34ffe09a955a6..501c3cf3aa7bc31caecf8792a68a5966c4086594 100644 (file)
@@ -106,7 +106,7 @@ const char *print_event_type(const enum lttng_event_type ev_type)
  * enabled is 0 or 1
  * success is 0 or 1
  */
-static int mi_print_event(char *event_name, int enabled, int success)
+static int mi_print_event(const char *event_name, int enabled, int success)
 {
        int ret;
 
index 0de6a0cd4e42d42d9d058a8c12c6cfa66e5b6c8c..570294e8f5a8f11ee62b0aa82490e3df653af7be 100644 (file)
@@ -27,6 +27,7 @@
 #include <common/mi-lttng.h>
 
 #include "../command.h"
+#include "../uprobe.h"
 
 #if (LTTNG_SYMBOL_NAME_LEN == 256)
 #define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API    "255"
@@ -174,397 +175,11 @@ end:
        return ret;
 }
 
-/*
- * Walk the directories in the PATH environment variable to find the target
- * binary passed as parameter.
- *
- * On success, the full path of the binary is copied in binary_full_path out
- * parameter. This buffer is allocated by the caller and must be at least
- * LTTNG_PATH_MAX bytes long.
- * On failure, returns -1;
- */
-static int walk_command_search_path(const char *binary, char *binary_full_path)
-{
-       char *tentative_binary_path = NULL;
-       char *command_search_path = NULL;
-       char *curr_search_dir_end = NULL;
-       char *curr_search_dir = NULL;
-       struct stat stat_output;
-       int ret = 0;
-
-       command_search_path = lttng_secure_getenv("PATH");
-       if (!command_search_path) {
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * Duplicate the $PATH string as the char pointer returned by getenv() should
-        * not be modified.
-        */
-       command_search_path = strdup(command_search_path);
-       if (!command_search_path) {
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * This char array is used to concatenate path to binary to look for
-        * the binary.
-        */
-       tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
-       if (!tentative_binary_path) {
-               ret = -1;
-               goto alloc_error;
-       }
-
-       curr_search_dir = command_search_path;
-       do {
-               /*
-                * Split on ':'. The return value of this call points to the
-                * matching character.
-                */
-               curr_search_dir_end = strchr(curr_search_dir, ':');
-               if (curr_search_dir_end != NULL) {
-                       /*
-                        * Add a NULL byte to the end of the first token so it
-                        * can be used as a string.
-                        */
-                       curr_search_dir_end[0] = '\0';
-               }
-
-               /* Empty the tentative path */
-               memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char));
-
-               /*
-                * Build the tentative path to the binary using the current
-                * search directory and the name of the binary.
-                */
-               ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s",
-                               curr_search_dir, binary);
-               if (ret < 0) {
-                       goto free_binary_path;
-               }
-               if (ret < LTTNG_PATH_MAX) {
-                        /*
-                         * Use STAT(2) to see if the file exists.
-                        */
-                       ret = stat(tentative_binary_path, &stat_output);
-                       if (ret == 0) {
-                               /*
-                                * Verify that it is a regular file or a
-                                * symlink and not a special file (e.g.
-                                * device).
-                                */
-                               if (S_ISREG(stat_output.st_mode)
-                                               || S_ISLNK(stat_output.st_mode)) {
-                                       /*
-                                        * Found a match, set the out parameter
-                                        * and return success.
-                                        */
-                                       ret = lttng_strncpy(binary_full_path,
-                                                       tentative_binary_path,
-                                                       LTTNG_PATH_MAX);
-                                       if (ret == -1) {
-                                               ERR("Source path does not fit "
-                                                       "in destination buffer.");
-                                       }
-                                       goto free_binary_path;
-                               }
-                       }
-               }
-               /* Go to the next entry in the $PATH variable. */
-               curr_search_dir = curr_search_dir_end + 1;
-       } while (curr_search_dir_end != NULL);
-
-free_binary_path:
-       free(tentative_binary_path);
-alloc_error:
-       free(command_search_path);
-end:
-       return ret;
-}
-
-/*
- * Check if the symbol field passed by the user is in fact an address or an
- * offset from a symbol. Those two instrumentation types are not supported yet.
- * It's expected to be a common mistake because of the existing --probe option
- * that does support these formats.
- *
- * Here are examples of these unsupported formats for the --userspace-probe
- * option:
- * elf:/path/to/binary:0x400430
- * elf:/path/to/binary:4194364
- * elf:/path/to/binary:my_symbol+0x323
- * elf:/path/to/binary:my_symbol+43
- */
-static int warn_userspace_probe_syntax(const char *symbol)
-{
-       int ret;
-
-       /* Check if the symbol field is an hex address. */
-       ret = sscanf(symbol, "0x%*x");
-       if (ret > 0) {
-               /* If there is a match, print a warning and return an error. */
-               ERR("Userspace probe on address not supported yet.");
-               ret = CMD_UNSUPPORTED;
-               goto error;
-       }
-
-       /* Check if the symbol field is an decimal address. */
-       ret = sscanf(symbol, "%*u");
-       if (ret > 0) {
-               /* If there is a match, print a warning and return an error. */
-               ERR("Userspace probe on address not supported yet.");
-               ret = CMD_UNSUPPORTED;
-               goto error;
-       }
-
-       /* Check if the symbol field is symbol+hex_offset. */
-       ret = sscanf(symbol, "%*[^+]+0x%*x");
-       if (ret > 0) {
-               /* If there is a match, print a warning and return an error. */
-               ERR("Userspace probe on symbol+offset not supported yet.");
-               ret = CMD_UNSUPPORTED;
-               goto error;
-       }
-
-       /* Check if the symbol field is symbol+decimal_offset. */
-       ret = sscanf(symbol, "%*[^+]+%*u");
-       if (ret > 0) {
-               /* If there is a match, print a warning and return an error. */
-               ERR("Userspace probe on symbol+offset not supported yet.");
-               ret = CMD_UNSUPPORTED;
-               goto error;
-       }
-
-       ret = 0;
-
-error:
-       return ret;
-}
-
-/*
- * Parse userspace probe options
- * Set the userspace probe fields in the lttng_event struct and set the
- * target_path to the path to the binary.
- */
-static int parse_userspace_probe_opts(struct lttng_event *ev, char *opt)
-{
-       int ret = CMD_SUCCESS;
-       int num_token;
-       char **tokens;
-       char *target_path = NULL;
-       char *unescaped_target_path = NULL;
-       char *real_target_path = NULL;
-       char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL;
-       struct lttng_userspace_probe_location *probe_location = NULL;
-       struct lttng_userspace_probe_location_lookup_method *lookup_method =
-                       NULL;
-
-       if (opt == NULL) {
-               ret = CMD_ERROR;
-               goto end;
-       }
-
-       switch (ev->type) {
-       case LTTNG_EVENT_USERSPACE_PROBE:
-               break;
-       default:
-               assert(0);
-       }
-
-       /*
-        * userspace probe fields are separated by ':'.
-        */
-       tokens = strutils_split(opt, ':', 1);
-       num_token = strutils_array_of_strings_len(tokens);
-
-       /*
-        * Early sanity check that the number of parameter is between 2 and 4
-        * inclusively.
-        * elf:PATH:SYMBOL
-        * std:PATH:PROVIDER_NAME:PROBE_NAME
-        * PATH:SYMBOL (same behavior as ELF)
-        */
-       if (num_token < 2 || num_token > 4) {
-               ret = CMD_ERROR;
-               goto end_string;
-       }
-
-       /*
-        * Looking up the first parameter will tell the technique to use to
-        * interpret the userspace probe/function description.
-        */
-       switch (num_token) {
-       case 2:
-               /* When the probe type is omitted we assume ELF for now. */
-       case 3:
-               if (num_token == 3 && strcmp(tokens[0], "elf") == 0) {
-                       target_path = tokens[1];
-                       symbol_name = tokens[2];
-               } else if (num_token == 2) {
-                       target_path = tokens[0];
-                       symbol_name = tokens[1];
-               } else {
-                       ret = CMD_ERROR;
-                       goto end_string;
-               }
-               lookup_method =
-                       lttng_userspace_probe_location_lookup_method_function_elf_create();
-               if (!lookup_method) {
-                       WARN("Failed to create ELF lookup method");
-                       ret = CMD_ERROR;
-                       goto end_string;
-               }
-               break;
-       case 4:
-               if (strcmp(tokens[0], "sdt") == 0) {
-                       target_path = tokens[1];
-                       provider_name = tokens[2];
-                       probe_name = tokens[3];
-               } else {
-                       ret = CMD_ERROR;
-                       goto end_string;
-               }
-               lookup_method =
-                       lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
-               if (!lookup_method) {
-                       WARN("Failed to create SDT lookup method");
-                       ret = CMD_ERROR;
-                       goto end_string;
-               }
-               break;
-       default:
-               ret = CMD_ERROR;
-               goto end_string;
-       }
-
-       /* strutils_unescape_string allocates a new char *. */
-       unescaped_target_path = strutils_unescape_string(target_path, 0);
-       if (!unescaped_target_path) {
-               ret = CMD_ERROR;
-               goto end_destroy_lookup_method;
-       }
-
-       /*
-        * If there is not forward slash in the path. Walk the $PATH else
-        * expand.
-        */
-       if (strchr(unescaped_target_path, '/') == NULL) {
-               /* Walk the $PATH variable to find the targeted binary. */
-               real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
-               if (!real_target_path) {
-                       PERROR("Error allocating path buffer");
-                       ret = CMD_ERROR;
-                       goto end_destroy_lookup_method;
-               }
-               ret = walk_command_search_path(unescaped_target_path, real_target_path);
-               if (ret) {
-                       ERR("Binary not found.");
-                       ret = CMD_ERROR;
-                       goto end_destroy_lookup_method;
-               }
-       } else {
-               /*
-                * Expand references to `/./` and `/../`. This function does not check
-                * if the file exists. This call returns an allocated buffer on
-                * success.
-                */
-               real_target_path = utils_expand_path_keep_symlink(unescaped_target_path);
-               if (!real_target_path) {
-                       ERR("Error expanding the path to binary.");
-                       ret = CMD_ERROR;
-                       goto end_destroy_lookup_method;
-               }
-
-               /*
-                * Check if the file exists using access(2), If it does not,
-                * return an error.
-                */
-               ret = access(real_target_path, F_OK);
-               if (ret) {
-                       ERR("Cannot find binary at path: %s.", real_target_path);
-                       ret = CMD_ERROR;
-                       goto end_destroy_lookup_method;
-               }
-       }
-
-       switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
-               /*
-                * Check for common mistakes in userspace probe description syntax.
-                */
-               ret = warn_userspace_probe_syntax(symbol_name);
-               if (ret) {
-                       goto end_destroy_lookup_method;
-               }
-
-               probe_location = lttng_userspace_probe_location_function_create(
-                               real_target_path, symbol_name, lookup_method);
-               if (!probe_location) {
-                       WARN("Failed to create function probe location");
-                       ret = CMD_ERROR;
-                       goto end_destroy_lookup_method;
-               }
-
-               /* Ownership transferred to probe_location. */
-               lookup_method = NULL;
-
-               ret = lttng_event_set_userspace_probe_location(ev, probe_location);
-               if (ret) {
-                       WARN("Failed to set probe location on event");
-                       ret = CMD_ERROR;
-                       goto end_destroy_location;
-               }
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
-               probe_location = lttng_userspace_probe_location_tracepoint_create(
-                               real_target_path, provider_name, probe_name, lookup_method);
-               if (!probe_location) {
-                       WARN("Failed to create function probe location");
-                       ret = CMD_ERROR;
-                       goto end_destroy_lookup_method;
-               }
-
-               /* Ownership transferred to probe_location. */
-               lookup_method = NULL;
-
-               ret = lttng_event_set_userspace_probe_location(ev, probe_location);
-               if (ret) {
-                       WARN("Failed to set probe location on event");
-                       ret = CMD_ERROR;
-                       goto end_destroy_location;
-               }
-               break;
-       default:
-               ret = CMD_ERROR;
-               goto end_destroy_lookup_method;
-       }
-
-       /* Successful parsing, now clean up everything and return. */
-       goto end_string;
-
-end_destroy_location:
-       lttng_userspace_probe_location_destroy(probe_location);
-end_destroy_lookup_method:
-       lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
-end_string:
-       strutils_free_null_terminated_array_of_strings(tokens);
-       /*
-        * Freeing both char * here makes the error handling simplier. free()
-        * performs not action if the pointer is NULL.
-        */
-       free(real_target_path);
-       free(unescaped_target_path);
-end:
-       return ret;
-}
-
 /*
  * Maps LOG4j loglevel from string to value
  */
-static int loglevel_log4j_str_to_value(const char *inputstr)
+LTTNG_HIDDEN
+int loglevel_log4j_str_to_value(const char *inputstr)
 {
        int i = 0;
        char str[LTTNG_SYMBOL_NAME_LEN];
@@ -607,7 +222,8 @@ static int loglevel_log4j_str_to_value(const char *inputstr)
 /*
  * Maps JUL loglevel from string to value
  */
-static int loglevel_jul_str_to_value(const char *inputstr)
+LTTNG_HIDDEN
+int loglevel_jul_str_to_value(const char *inputstr)
 {
        int i = 0;
        char str[LTTNG_SYMBOL_NAME_LEN];
@@ -652,7 +268,8 @@ static int loglevel_jul_str_to_value(const char *inputstr)
 /*
  * Maps Python loglevel from string to value
  */
-static int loglevel_python_str_to_value(const char *inputstr)
+LTTNG_HIDDEN
+int loglevel_python_str_to_value(const char *inputstr)
 {
        int i = 0;
        char str[LTTNG_SYMBOL_NAME_LEN];
@@ -691,7 +308,7 @@ static int loglevel_python_str_to_value(const char *inputstr)
 /*
  * Maps loglevel from string to value
  */
-static
+LTTNG_HIDDEN
 int loglevel_str_to_value(const char *inputstr)
 {
        int i = 0;
@@ -891,7 +508,7 @@ end:
        return ret;
 }
 
-static
+LTTNG_HIDDEN
 int create_exclusion_list_and_validate(const char *event_name,
                const char *exclusions_arg,
                char ***exclusion_list)
@@ -969,6 +586,7 @@ static int enable_events(char *session_name)
        struct lttng_event *ev;
        struct lttng_domain dom;
        char **exclusion_list = NULL;
+       struct lttng_userspace_probe_location *uprobe_loc = NULL;
 
        memset(&dom, 0, sizeof(dom));
 
@@ -1356,7 +974,9 @@ static int enable_events(char *session_name)
                                }
                                break;
                        case LTTNG_EVENT_USERSPACE_PROBE:
-                               ret = parse_userspace_probe_opts(ev, opt_userspace_probe);
+                               assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE);
+
+                               ret = parse_userspace_probe_opts(opt_userspace_probe, &uprobe_loc);
                                if (ret) {
                                        switch (ret) {
                                        case CMD_UNSUPPORTED:
@@ -1373,6 +993,16 @@ static int enable_events(char *session_name)
                                        }
                                        goto error;
                                }
+
+                               ret = lttng_event_set_userspace_probe_location(ev, uprobe_loc);
+                               if (ret) {
+                                       WARN("Failed to set probe location on event");
+                                       ret = CMD_ERROR;
+                                       goto error;
+                               }
+
+                               /* Ownership of the uprobe location was transferred to the event. */
+                               uprobe_loc = NULL;
                                break;
                        case LTTNG_EVENT_FUNCTION:
                                ret = parse_probe_opts(ev, opt_function);
@@ -1689,6 +1319,7 @@ error:
        }
        lttng_destroy_handle(handle);
        strutils_free_null_terminated_array_of_strings(exclusion_list);
+       lttng_userspace_probe_location_destroy(uprobe_loc);
 
        /* Overwrite ret with error_holder if there was an actual error with
         * enabling an event.
diff --git a/src/bin/lttng/commands/list_triggers.c b/src/bin/lttng/commands/list_triggers.c
new file mode 100644 (file)
index 0000000..89296c7
--- /dev/null
@@ -0,0 +1,541 @@
+#include <stdio.h>
+
+#include "../command.h"
+
+#include "common/argpar/argpar.h"
+#include "common/mi-lttng.h"
+#include "lttng/condition/condition-internal.h"
+#include "lttng/condition/event-rule.h"
+#include "lttng/domain-internal.h"
+#include "lttng/event-rule/event-rule-internal.h"
+#include "lttng/event-rule/kprobe.h"
+#include "lttng/event-rule/kprobe-internal.h"
+#include "lttng/event-rule/syscall.h"
+#include "lttng/event-rule/tracepoint.h"
+#include "lttng/event-rule/uprobe.h"
+#include "lttng/trigger/trigger-internal.h"
+
+#ifdef LTTNG_EMBED_HELP
+static const char help_msg[] =
+#include <lttng-list-trigger.1.h>
+;
+#endif
+
+enum {
+       OPT_HELP,
+       OPT_LIST_OPTIONS,
+};
+
+static const
+struct argpar_opt_descr list_trigger_options[] = {
+       { OPT_HELP, 'h', "help", false },
+       { OPT_LIST_OPTIONS, '\0', "list-options", false },
+       ARGPAR_OPT_DESCR_SENTINEL,
+};
+
+static
+void print_event_rule_tracepoint(const struct lttng_event_rule *event_rule)
+{
+       enum lttng_event_rule_status event_rule_status;
+       enum lttng_domain_type domain_type;
+       const char *pattern;
+       const char *filter;
+       int loglevel;
+       unsigned int exclusions_count;
+       int i;
+
+       event_rule_status = lttng_event_rule_tracepoint_get_pattern(
+               event_rule, &pattern);
+       assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       event_rule_status = lttng_event_rule_tracepoint_get_domain_type(
+               event_rule, &domain_type);
+       assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       printf("    rule: %s (type: tracepoint, domain: %s", pattern,
+               lttng_domain_type_str(domain_type));
+
+       event_rule_status = lttng_event_rule_tracepoint_get_filter(
+               event_rule, &filter);
+       if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) {
+               printf(", filter: %s", filter);
+       } else {
+               assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET);
+       }
+
+       event_rule_status = lttng_event_rule_tracepoint_get_loglevel(
+               event_rule, &loglevel);
+       if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) {
+               enum lttng_loglevel_type loglevel_type;
+               const char *loglevel_op;
+
+               event_rule_status = lttng_event_rule_tracepoint_get_loglevel_type(
+                       event_rule, &loglevel_type);
+               assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+               assert(loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE ||
+                       loglevel_type == LTTNG_EVENT_LOGLEVEL_SINGLE);
+
+               loglevel_op = (loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE ? "<=" : "==");
+
+               printf(", log level %s %s", loglevel_op,
+                       mi_lttng_loglevel_string(loglevel, domain_type));
+       } else {
+               assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET);
+       }
+
+       event_rule_status = lttng_event_rule_tracepoint_get_exclusions_count(
+               event_rule, &exclusions_count);
+       assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+       if (exclusions_count > 0) {
+               printf(", exclusions: ");
+               for (i = 0; i < exclusions_count; i++) {
+                       const char *exclusion;
+
+                       event_rule_status = lttng_event_rule_tracepoint_get_exclusion_at_index(
+                               event_rule, i, &exclusion);
+                       assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+                       printf("%s%s", i > 0 ? "," : "", exclusion);
+               }
+       }
+
+
+       printf(")\n");
+}
+
+static
+void print_event_rule_kprobe(const struct lttng_event_rule *event_rule)
+{
+       enum lttng_event_rule_status event_rule_status;
+       const char *name, *symbol_name;
+       uint64_t offset;
+
+       event_rule_status = lttng_event_rule_kprobe_get_name(event_rule, &name);
+       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+               fprintf(stderr, "Failed to get kprobe event rule's name.\n");
+               goto end;
+       }
+
+       assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KPROBE);
+
+       printf("    rule: %s (type: probe, location: ", name);
+
+       // FIXME: When the location has been specified by address, this field
+       // contains the address as a string.  The only downside is that we are
+       // missing a `0x` prefix.
+       symbol_name = lttng_event_rule_kprobe_get_symbol_name(event_rule);
+       printf("%s", symbol_name);
+
+       offset = lttng_event_rule_kprobe_get_offset(event_rule);
+       if (offset > 0) {
+               printf("+0x%" PRIx64, offset);
+       }
+
+       printf(")\n");
+
+end:
+       return;
+}
+
+static
+void print_event_rule_uprobe(const struct lttng_event_rule *event_rule)
+{
+       enum lttng_event_rule_status event_rule_status;
+       const char *name;
+       const struct lttng_userspace_probe_location *location;
+       enum lttng_userspace_probe_location_type userspace_probe_location_type;
+
+       assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_UPROBE);
+
+       event_rule_status = lttng_event_rule_uprobe_get_name(event_rule, &name);
+       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+               fprintf(stderr, "Failed to get uprobe event rule's name.\n");
+               goto end;
+       }
+
+       event_rule_status = lttng_event_rule_uprobe_get_location(event_rule, &location);
+       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+               fprintf(stderr, "Failed to get uprobe event rule's location.\n");
+               goto end;
+       }
+
+       printf("    rule: %s (type: userspace probe, location: ", name);
+
+       userspace_probe_location_type =
+               lttng_userspace_probe_location_get_type(location);
+
+       switch (userspace_probe_location_type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+       {
+               const char *binary_path, *function_name;
+
+               binary_path = lttng_userspace_probe_location_function_get_binary_path(location);
+               function_name = lttng_userspace_probe_location_function_get_function_name(location);
+
+               printf("%s:%s", binary_path, function_name);
+               break;
+       }
+
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+               printf("SDT not implemented yet");
+               break;
+
+       default:
+               abort();
+       }
+
+       printf(")\n");
+
+end:
+       return;
+}
+
+static
+void print_event_rule_syscall(const struct lttng_event_rule *event_rule)
+{
+       const char *pattern, *filter;
+       enum lttng_event_rule_status event_rule_status;
+
+       assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_SYSCALL);
+
+       event_rule_status = lttng_event_rule_syscall_get_pattern(event_rule, &pattern);
+       assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       printf("  - rule: %s (type: syscall", pattern);
+
+       event_rule_status = lttng_event_rule_syscall_get_filter(
+               event_rule, &filter);
+       if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) {
+               printf(", filter: %s", filter);
+       } else {
+               assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET);
+       }
+
+       printf(")\n");
+}
+
+static
+void print_event_rule(const struct lttng_event_rule *event_rule)
+{
+       enum lttng_event_rule_type event_rule_type =
+               lttng_event_rule_get_type(event_rule);
+
+       switch (event_rule_type) {
+       case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+               print_event_rule_tracepoint(event_rule);
+               break;
+
+       case LTTNG_EVENT_RULE_TYPE_KPROBE:
+               print_event_rule_kprobe(event_rule);
+               break;
+
+       case LTTNG_EVENT_RULE_TYPE_UPROBE:
+               print_event_rule_uprobe(event_rule);
+               break;
+
+       case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+               print_event_rule_syscall(event_rule);
+               break;
+
+       default:
+               abort();
+       }
+}
+
+static
+void print_condition_event_rule_hit(const struct lttng_condition *condition)
+{
+       const struct lttng_event_rule *event_rule;
+       enum lttng_condition_status condition_status;
+
+       condition_status =
+               lttng_condition_event_rule_get_rule(condition, &event_rule);
+       assert(condition_status == LTTNG_CONDITION_STATUS_OK);
+
+       print_event_rule(event_rule);
+}
+
+static
+void print_one_action(const struct lttng_action *action)
+{
+       enum lttng_action_type action_type;
+       enum lttng_action_status action_status;
+       const char *value;
+
+       action_type = lttng_action_get_type(action);
+       assert(action_type != LTTNG_ACTION_TYPE_GROUP);
+
+       switch (action_type) {
+       case LTTNG_ACTION_TYPE_NOTIFY:
+               printf("notify\n");
+               break;
+
+       case LTTNG_ACTION_TYPE_START_SESSION:
+               action_status = lttng_action_start_session_get_session_name(
+                       action, &value);
+               assert(action_status == LTTNG_ACTION_STATUS_OK);
+               printf("start session `%s`\n", value);
+               break;
+
+       case LTTNG_ACTION_TYPE_STOP_SESSION:
+               action_status = lttng_action_stop_session_get_session_name(
+                       action, &value);
+               assert(action_status == LTTNG_ACTION_STATUS_OK);
+               printf("stop session `%s`\n", value);
+               break;
+
+       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+               action_status = lttng_action_rotate_session_get_session_name(
+                       action, &value);
+               assert(action_status == LTTNG_ACTION_STATUS_OK);
+               printf("rotate session `%s`\n", value);
+               break;
+
+       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+       {
+               const struct lttng_snapshot_output *output;
+
+               action_status = lttng_action_snapshot_session_get_session_name(
+                       action, &value);
+               assert(action_status == LTTNG_ACTION_STATUS_OK);
+               printf("snapshot session `%s`", value);
+
+               action_status = lttng_action_snapshot_session_get_output_const(
+                       action, &output);
+               if (action_status == LTTNG_ACTION_STATUS_OK) {
+                       const char *name;
+                       uint64_t max_size;
+                       const char *ctrl_url, *data_url;
+                       bool starts_with_file, starts_with_net, starts_with_net6;
+
+                       ctrl_url = lttng_snapshot_output_get_ctrl_url(output);
+                       assert(ctrl_url && strlen(ctrl_url) > 0);
+
+                       data_url = lttng_snapshot_output_get_data_url(output);
+                       assert(data_url);
+
+                       starts_with_file = strncmp(ctrl_url, "file://", strlen("file://")) == 0;
+                       starts_with_net = strncmp(ctrl_url, "net://", strlen("net://")) == 0;
+                       starts_with_net6 = strncmp(ctrl_url, "net6://", strlen("net6://")) == 0;
+
+                       if (ctrl_url[0] == '/' || starts_with_file) {
+                               if (starts_with_file) {
+                                       ctrl_url += strlen("file://");
+                               }
+
+                               printf(", path: %s", ctrl_url);
+                       } else if (starts_with_net || starts_with_net6) {
+                               printf(", url: %s", ctrl_url);
+                       } else {
+                               assert(strlen(data_url) > 0);
+
+                               printf(", control url: %s, data url: %s", ctrl_url, data_url);
+                       }
+
+                       name = lttng_snapshot_output_get_name(output);
+                       assert(name);
+                       if (strlen(name) > 0) {
+                               printf(", name: %s", name);
+                       }
+
+                       max_size = lttng_snapshot_output_get_maxsize(output);
+                       if (max_size != -1ULL) {
+                               printf(", max size: %" PRIu64, max_size);
+                       }
+               }
+
+               printf("\n");
+               break;
+       }
+
+       default:
+               abort();
+       }
+}
+
+static
+void print_one_trigger(const struct lttng_trigger *trigger)
+{
+       const struct lttng_condition *condition;
+       enum lttng_condition_type condition_type;
+       const struct lttng_action *action;
+       enum lttng_action_type action_type;
+       enum lttng_trigger_status trigger_status;
+       const char *name;
+       enum lttng_trigger_firing_policy_type policy_type;
+       unsigned long long threshold;
+
+       trigger_status = lttng_trigger_get_name(trigger, &name);
+       assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+       printf("- id: %s\n", name);
+
+       trigger_status = lttng_trigger_get_firing_policy(trigger,
+                       &policy_type, &threshold);
+       if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+               fprintf(stderr, "Failed to get trigger's firing policy.\n");
+               goto end;
+       }
+
+       switch (policy_type) {
+       case LTTNG_TRIGGER_FIRE_EVERY_N:
+               if (threshold > 1) {
+                       printf("  firing policy: after every %llu occurences\n", threshold);
+               }
+               break;
+
+       case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N:
+               printf("  firing policy: once after %llu occurences\n", threshold);
+               break;
+
+       default:
+               abort();
+       }
+
+       condition = lttng_trigger_get_const_condition(trigger);
+       condition_type = lttng_condition_get_type(condition);
+       printf("  condition: %s\n",
+               lttng_condition_type_str(condition_type));
+
+       switch (condition_type) {
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               print_condition_event_rule_hit(condition);
+               break;
+
+       default:
+               printf("  (condition type not handled in %s)\n", __func__);
+               break;
+       }
+
+       action = lttng_trigger_get_const_action(trigger);
+       action_type = lttng_action_get_type(action);
+       if (action_type == LTTNG_ACTION_TYPE_GROUP) {
+               enum lttng_action_status action_status;
+               unsigned int count, i;
+
+               printf("  actions:\n");
+
+               action_status = lttng_action_group_get_count(action, &count);
+               assert(action_status == LTTNG_ACTION_STATUS_OK);
+
+               for (i = 0; i < count; i++) {
+                       const struct lttng_action *subaction =
+                               lttng_action_group_get_at_index_const(action, i);
+
+                       printf("    ");
+                       print_one_action(subaction);
+               }
+       } else {
+               printf(" action:");
+               print_one_action(action);
+       }
+
+end:
+       return;
+}
+
+static
+int compare_triggers_by_name(const void *a, const void *b)
+{
+       const struct lttng_trigger *trigger_a = *((const struct lttng_trigger **) a);
+       const struct lttng_trigger *trigger_b = *((const struct lttng_trigger **) b);
+       const char *name_a, *name_b;
+       enum lttng_trigger_status trigger_status;
+
+       trigger_status = lttng_trigger_get_name(trigger_a, &name_a);
+       assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       trigger_status = lttng_trigger_get_name(trigger_b, &name_b);
+       assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       return strcmp(name_a, name_b);
+}
+
+int cmd_list_triggers(int argc, const char **argv)
+{
+       int ret;
+       struct argpar_parse_ret argpar_parse_ret = { 0 };
+       struct lttng_triggers *triggers = NULL;
+       int i;
+       const struct lttng_trigger **sorted_triggers = NULL;
+       enum lttng_trigger_status trigger_status;
+       unsigned int num_triggers;
+
+       argpar_parse_ret = argpar_parse(argc - 1, argv + 1,
+               list_trigger_options, true);
+       if (!argpar_parse_ret.items) {
+               fprintf(stderr, "Error: %s\n", argpar_parse_ret.error);
+               goto error;
+       }
+
+       for (i = 0; i < argpar_parse_ret.items->n_items; i++) {
+               struct argpar_item *item = argpar_parse_ret.items->items[i];
+
+               if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+                       struct argpar_item_opt *item_opt =
+                               (struct argpar_item_opt *) item;
+
+                       switch (item_opt->descr->id) {
+                       case OPT_HELP:
+                               SHOW_HELP();
+                               ret = 0;
+                               goto end;
+
+                       case OPT_LIST_OPTIONS:
+                               list_cmd_options_argpar(stdout,
+                                       list_trigger_options);
+                               ret = 0;
+                               goto end;
+
+                       default:
+                               abort();
+                       }
+
+               } else {
+                       struct argpar_item_non_opt *item_non_opt =
+                               (struct argpar_item_non_opt *) item;
+
+                       fprintf(stderr, "Unexpected argument: %s\n", item_non_opt->arg);
+               }
+       }
+
+       ret = lttng_list_triggers(&triggers);
+       if (ret != 0) {
+               fprintf(stderr, "Error listing triggers: %s.\n",
+                       lttng_strerror(ret));
+               goto error;
+       }
+
+       trigger_status = lttng_triggers_get_count(triggers, &num_triggers);
+       if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+               fprintf(stderr, "Failed to get trigger count.\n");
+               goto error;
+       }
+
+       sorted_triggers = calloc(num_triggers, sizeof(struct lttng_trigger *));
+       if (!sorted_triggers) {
+               fprintf(stderr, "Failed to allocate array of struct lttng_trigger *.\n");
+               goto error;
+       }
+
+       for (i = 0; i < num_triggers; i++) {
+               sorted_triggers[i] = lttng_triggers_get_at_index(triggers, i);
+       }
+
+       qsort(sorted_triggers, num_triggers, sizeof(struct lttng_trigger *),
+               compare_triggers_by_name);
+
+       for (i = 0; i < num_triggers; i++) {
+               print_one_trigger(sorted_triggers[i]);
+       }
+
+       goto end;
+
+error:
+       ret = 1;
+
+end:
+       argpar_parse_ret_fini(&argpar_parse_ret);
+       lttng_triggers_destroy(triggers);
+
+       return ret;
+}
diff --git a/src/bin/lttng/commands/remove_trigger.c b/src/bin/lttng/commands/remove_trigger.c
new file mode 100644 (file)
index 0000000..6d34c00
--- /dev/null
@@ -0,0 +1,131 @@
+#include <stdio.h>
+
+#include "../command.h"
+
+#include "common/argpar/argpar.h"
+
+#ifdef LTTNG_EMBED_HELP
+static const char help_msg[] =
+#include <lttng-remove-trigger.1.h>
+;
+#endif
+
+enum {
+       OPT_HELP,
+       OPT_LIST_OPTIONS,
+};
+
+static const
+struct argpar_opt_descr remove_trigger_options[] = {
+       { OPT_HELP, 'h', "help", false },
+       { OPT_LIST_OPTIONS, '\0', "list-options", false },
+       ARGPAR_OPT_DESCR_SENTINEL,
+};
+
+int cmd_remove_trigger(int argc, const char **argv)
+{
+       int ret;
+       struct argpar_parse_ret argpar_parse_ret = { 0 };
+       const char *id = NULL;
+       int i;
+       struct lttng_triggers *triggers = NULL;
+       unsigned int triggers_count;
+       enum lttng_trigger_status trigger_status;
+       const struct lttng_trigger *trigger_to_remove = NULL;
+
+       argpar_parse_ret = argpar_parse(argc - 1, argv + 1,
+               remove_trigger_options, true);
+       if (!argpar_parse_ret.items) {
+               fprintf(stderr, "Error: %s\n", argpar_parse_ret.error);
+               goto error;
+       }
+
+       for (i = 0; i < argpar_parse_ret.items->n_items; i++) {
+               struct argpar_item *item = argpar_parse_ret.items->items[i];
+
+               if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+                       struct argpar_item_opt *item_opt =
+                               (struct argpar_item_opt *) item;
+
+                       switch (item_opt->descr->id) {
+                       case OPT_HELP:
+                               SHOW_HELP();
+                               ret = 0;
+                               goto end;
+
+                       case OPT_LIST_OPTIONS:
+                               list_cmd_options_argpar(stdout,
+                                       remove_trigger_options);
+                               ret = 0;
+                               goto end;
+
+                       default:
+                               abort();
+                       }
+
+               } else {
+                       struct argpar_item_non_opt *item_non_opt =
+                               (struct argpar_item_non_opt *) item;
+
+                       if (id) {
+                               fprintf(stderr, "Unexpected argument: %s\n", item_non_opt->arg);
+                               goto error;
+                       }
+
+                       id = item_non_opt->arg;
+               }
+       }
+
+       if (!id) {
+               fprintf(stderr, "Missing `id` argument.\n");
+               goto error;
+       }
+
+       ret = lttng_list_triggers(&triggers);
+       if (ret != 0) {
+               fprintf(stderr, "Failed to get the list of triggers.\n");
+               goto error;
+       }
+
+       trigger_status = lttng_triggers_get_count(triggers, &triggers_count);
+       assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       for (i = 0; i < triggers_count; i++) {
+               const struct lttng_trigger *trigger;
+               const char *trigger_name;
+
+               trigger = lttng_triggers_get_at_index(triggers, i);
+               trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
+               assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+               if (strcmp(trigger_name, id) == 0) {
+                       trigger_to_remove = trigger;
+                       break;
+               }
+       }
+
+       if (!trigger_to_remove) {
+               fprintf(stderr, "Couldn't find trigger with id `%s`.\n", id);
+               goto error;
+       }
+
+       ret = lttng_unregister_trigger(trigger_to_remove);
+       if (ret != 0) {
+               fprintf(stderr, "Failed to unregister trigger `%s`.\n", id);
+               goto error;
+       }
+
+       printf("Removed trigger `%s`.\n", id);
+
+       ret = 0;
+       goto end;
+
+error:
+       ret = 1;
+
+end:
+       argpar_parse_ret_fini(&argpar_parse_ret);
+       lttng_triggers_destroy(triggers);
+
+       return ret;
+}
index 7f8007dab67a5b5c0bb5c5315f2c47c262305549..24e2d02aeb50e1f6ffb9c5eb3864913b84547715 100644 (file)
@@ -451,7 +451,7 @@ static enum cmd_error_code track_untrack_id(enum cmd_type cmd_type,
 
                ret = cmd_func(handle, tracker_type, item);
                if (ret) {
-                       char *msg = NULL;
+                       const char *msg = NULL;
 
                        switch (-ret) {
                        case LTTNG_ERR_ID_TRACKED:
index 0dd1623ea1f7dd5db575a60e550a482d583f411c..b9f3553b32511fd142de47f71a1e6468a24a105e 100644 (file)
@@ -180,8 +180,8 @@ static char **alloc_argv_from_local_opts(const char **opts, size_t opts_len,
        memcpy(argv, opts, sizeof(char *) * opts_len);
 
        if (session_live_mode) {
-               argv[opts_len] = "-i";
-               argv[opts_len + 1] = "lttng-live";
+               argv[opts_len] = (char *) "-i";
+               argv[opts_len + 1] = (char *) "lttng-live";
                argv[opts_len + 2] = (char *) trace_path;
                argv[opts_len + 3] = NULL;
        } else {
index 9e1773bb4b90aa39da30a6a0b3fb2de865ea6a5d..8e703b05841b0a7c19302d8a51f8635e97de6250 100644 (file)
@@ -266,7 +266,7 @@ char *config_read_session_name_quiet(const char *path)
 int config_add_session_name(const char *path, const char *name)
 {
        int ret;
-       char *attr = "session=";
+       const char *attr = "session=";
        /* Max name len accepted plus attribute's len and the NULL byte. */
        char session_name[NAME_MAX + strlen(attr) + 1];
 
index 84dc3a748f6d49596866f72d245e819eb63f5c39..437a9cc4c2e00367ec5d10f2d0061ae18f4d5c49 100644 (file)
@@ -33,7 +33,7 @@ NULL
 ;
 
 /* Variables */
-static char *progname;
+static const char *progname;
 int opt_no_sessiond;
 char *opt_sessiond_path;
 
@@ -65,6 +65,7 @@ static struct option long_options[] = {
 /* First level command */
 static struct cmd_struct commands[] =  {
        { "add-context", cmd_add_context},
+       { "add-trigger", cmd_add_trigger},
        { "create", cmd_create},
        { "clear", cmd_clear},
        { "destroy", cmd_destroy},
@@ -74,9 +75,11 @@ static struct cmd_struct commands[] =  {
        { "enable-event", cmd_enable_events},
        { "help", NULL},
        { "list", cmd_list},
+       { "list-triggers", cmd_list_triggers},
        { "load", cmd_load},
        { "metadata", cmd_metadata},
        { "regenerate", cmd_regenerate},
+       { "remove-trigger", cmd_remove_trigger},
        { "rotate", cmd_rotate},
        { "enable-rotation", cmd_enable_rotation},
        { "disable-rotation", cmd_disable_rotation},
diff --git a/src/bin/lttng/uprobe.c b/src/bin/lttng/uprobe.c
new file mode 100644 (file)
index 0000000..c51c44a
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "uprobe.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "common/compat/getenv.h"
+#include "common/string-utils/string-utils.h"
+#include "common/utils.h"
+#include "lttng/constant.h"
+
+#include "command.h"
+
+/*
+ * Walk the directories in the PATH environment variable to find the target
+ * binary passed as parameter.
+ *
+ * On success, the full path of the binary is copied in binary_full_path out
+ * parameter. This buffer is allocated by the caller and must be at least
+ * LTTNG_PATH_MAX bytes long.
+ * On failure, returns -1;
+ */
+static
+int walk_command_search_path(const char *binary, char *binary_full_path)
+{
+       char *tentative_binary_path = NULL;
+       char *command_search_path = NULL;
+       char *curr_search_dir_end = NULL;
+       char *curr_search_dir = NULL;
+       struct stat stat_output;
+       int ret = 0;
+
+       command_search_path = lttng_secure_getenv("PATH");
+       if (!command_search_path) {
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * Duplicate the $PATH string as the char pointer returned by getenv() should
+        * not be modified.
+        */
+       command_search_path = strdup(command_search_path);
+       if (!command_search_path) {
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * This char array is used to concatenate path to binary to look for
+        * the binary.
+        */
+       tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
+       if (!tentative_binary_path) {
+               ret = -1;
+               goto alloc_error;
+       }
+
+       curr_search_dir = command_search_path;
+       do {
+               /*
+                * Split on ':'. The return value of this call points to the
+                * matching character.
+                */
+               curr_search_dir_end = strchr(curr_search_dir, ':');
+               if (curr_search_dir_end != NULL) {
+                       /*
+                        * Add a NULL byte to the end of the first token so it
+                        * can be used as a string.
+                        */
+                       curr_search_dir_end[0] = '\0';
+               }
+
+               /* Empty the tentative path */
+               memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char));
+
+               /*
+                * Build the tentative path to the binary using the current
+                * search directory and the name of the binary.
+                */
+               ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s",
+                               curr_search_dir, binary);
+               if (ret < 0) {
+                       goto free_binary_path;
+               }
+               if (ret < LTTNG_PATH_MAX) {
+                        /*
+                         * Use STAT(2) to see if the file exists.
+                        */
+                       ret = stat(tentative_binary_path, &stat_output);
+                       if (ret == 0) {
+                               /*
+                                * Verify that it is a regular file or a
+                                * symlink and not a special file (e.g.
+                                * device).
+                                */
+                               if (S_ISREG(stat_output.st_mode)
+                                               || S_ISLNK(stat_output.st_mode)) {
+                                       /*
+                                        * Found a match, set the out parameter
+                                        * and return success.
+                                        */
+                                       ret = lttng_strncpy(binary_full_path,
+                                                       tentative_binary_path,
+                                                       LTTNG_PATH_MAX);
+                                       if (ret == -1) {
+                                               ERR("Source path does not fit "
+                                                       "in destination buffer.");
+                                       }
+                                       goto free_binary_path;
+                               }
+                       }
+               }
+               /* Go to the next entry in the $PATH variable. */
+               curr_search_dir = curr_search_dir_end + 1;
+       } while (curr_search_dir_end != NULL);
+
+free_binary_path:
+       free(tentative_binary_path);
+alloc_error:
+       free(command_search_path);
+end:
+       return ret;
+}
+
+/*
+ * Check if the symbol field passed by the user is in fact an address or an
+ * offset from a symbol. Those two instrumentation types are not supported yet.
+ * It's expected to be a common mistake because of the existing --probe option
+ * that does support these formats.
+ *
+ * Here are examples of these unsupported formats for the --userspace-probe
+ * option:
+ * elf:/path/to/binary:0x400430
+ * elf:/path/to/binary:4194364
+ * elf:/path/to/binary:my_symbol+0x323
+ * elf:/path/to/binary:my_symbol+43
+ */
+static
+int warn_userspace_probe_syntax(const char *symbol)
+{
+       int ret;
+
+       /* Check if the symbol field is an hex address. */
+       ret = sscanf(symbol, "0x%*x");
+       if (ret > 0) {
+               /* If there is a match, print a warning and return an error. */
+               ERR("Userspace probe on address not supported yet.");
+               ret = CMD_UNSUPPORTED;
+               goto error;
+       }
+
+       /* Check if the symbol field is an decimal address. */
+       ret = sscanf(symbol, "%*u");
+       if (ret > 0) {
+               /* If there is a match, print a warning and return an error. */
+               ERR("Userspace probe on address not supported yet.");
+               ret = CMD_UNSUPPORTED;
+               goto error;
+       }
+
+       /* Check if the symbol field is symbol+hex_offset. */
+       ret = sscanf(symbol, "%*[^+]+0x%*x");
+       if (ret > 0) {
+               /* If there is a match, print a warning and return an error. */
+               ERR("Userspace probe on symbol+offset not supported yet.");
+               ret = CMD_UNSUPPORTED;
+               goto error;
+       }
+
+       /* Check if the symbol field is symbol+decimal_offset. */
+       ret = sscanf(symbol, "%*[^+]+%*u");
+       if (ret > 0) {
+               /* If there is a match, print a warning and return an error. */
+               ERR("Userspace probe on symbol+offset not supported yet.");
+               ret = CMD_UNSUPPORTED;
+               goto error;
+       }
+
+       ret = 0;
+
+error:
+       return ret;
+}
+
+/*
+ * Parse userspace probe options
+ * Set the userspace probe fields in the lttng_event struct and set the
+ * target_path to the path to the binary.
+ */
+LTTNG_HIDDEN
+int parse_userspace_probe_opts(const char *opt,
+               struct lttng_userspace_probe_location **probe_location)
+{
+       int ret = CMD_SUCCESS;
+       int num_token;
+       char **tokens = NULL;
+       char *target_path = NULL;
+       char *unescaped_target_path = NULL;
+       char *real_target_path = NULL;
+       char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL;
+       struct lttng_userspace_probe_location *probe_location_local = NULL;
+       struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
+
+       assert(opt);
+
+       /*
+        * userspace probe fields are separated by ':'.
+        */
+       tokens = strutils_split(opt, ':', 1);
+       num_token = strutils_array_of_strings_len(tokens);
+
+       /*
+        * Early sanity check that the number of parameter is between 2 and 4
+        * inclusively.
+        * elf:PATH:SYMBOL
+        * std:PATH:PROVIDER_NAME:PROBE_NAME
+        * PATH:SYMBOL (same behavior as ELF)
+        */
+       if (num_token < 2 || num_token > 4) {
+               ret = CMD_ERROR;
+               goto end;
+       }
+
+       /*
+        * Looking up the first parameter will tell the technique to use to
+        * interpret the userspace probe/function description.
+        */
+       switch (num_token) {
+       case 2:
+               /* When the probe type is omitted we assume ELF for now. */
+       case 3:
+               if (num_token == 3 && strcmp(tokens[0], "elf") == 0) {
+                       target_path = tokens[1];
+                       symbol_name = tokens[2];
+               } else if (num_token == 2) {
+                       target_path = tokens[0];
+                       symbol_name = tokens[1];
+               } else {
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+               lookup_method =
+                       lttng_userspace_probe_location_lookup_method_function_elf_create();
+               if (!lookup_method) {
+                       WARN("Failed to create ELF lookup method");
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+               break;
+       case 4:
+               if (strcmp(tokens[0], "sdt") == 0) {
+                       target_path = tokens[1];
+                       provider_name = tokens[2];
+                       probe_name = tokens[3];
+               } else {
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+               lookup_method =
+                       lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
+               if (!lookup_method) {
+                       WARN("Failed to create SDT lookup method");
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+               break;
+       default:
+               ret = CMD_ERROR;
+               goto end;
+       }
+
+       /* strutils_unescape_string allocates a new char *. */
+       unescaped_target_path = strutils_unescape_string(target_path, 0);
+       if (!unescaped_target_path) {
+               ret = CMD_ERROR;
+               goto end;
+       }
+
+       /*
+        * If there is not forward slash in the path. Walk the $PATH else
+        * expand.
+        */
+       if (strchr(unescaped_target_path, '/') == NULL) {
+               /* Walk the $PATH variable to find the targeted binary. */
+               real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
+               if (!real_target_path) {
+                       PERROR("Error allocating path buffer");
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+               ret = walk_command_search_path(unescaped_target_path, real_target_path);
+               if (ret) {
+                       ERR("Binary not found.");
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+       } else {
+               /*
+                * Expand references to `/./` and `/../`. This function does not check
+                * if the file exists. This call returns an allocated buffer on
+                * success.
+                */
+               real_target_path = utils_expand_path_keep_symlink(unescaped_target_path);
+               if (!real_target_path) {
+                       ERR("Error expanding the path to binary.");
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+
+               /*
+                * Check if the file exists using access(2), If it does not,
+                * return an error.
+                */
+               ret = access(real_target_path, F_OK);
+               if (ret) {
+                       ERR("Cannot find binary at path: %s.", real_target_path);
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+       }
+
+       switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+               /*
+                * Check for common mistakes in userspace probe description syntax.
+                */
+               ret = warn_userspace_probe_syntax(symbol_name);
+               if (ret) {
+                       goto end;
+               }
+
+               probe_location_local = lttng_userspace_probe_location_function_create(
+                               real_target_path, symbol_name, lookup_method);
+               if (!probe_location_local) {
+                       WARN("Failed to create function probe location");
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+
+               /* Ownership transferred to probe_location. */
+               lookup_method = NULL;
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+               probe_location_local = lttng_userspace_probe_location_tracepoint_create(
+                               real_target_path, provider_name, probe_name, lookup_method);
+               if (!probe_location_local) {
+                       WARN("Failed to create function probe location");
+                       ret = CMD_ERROR;
+                       goto end;
+               }
+
+               /* Ownership transferred to probe_location. */
+               lookup_method = NULL;
+               break;
+       default:
+               ret = CMD_ERROR;
+               goto end;
+       }
+
+       /*
+        * Everything went fine, transfer ownership of probe location to
+        * caller.
+        */
+       *probe_location = probe_location_local;
+       probe_location_local = NULL;
+
+end:
+       lttng_userspace_probe_location_destroy(probe_location_local);
+       lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
+       strutils_free_null_terminated_array_of_strings(tokens);
+       /*
+        * Freeing both char * here makes the error handling simplier. free()
+        * performs not action if the pointer is NULL.
+        */
+       free(real_target_path);
+       free(unescaped_target_path);
+
+       return ret;
+}
diff --git a/src/bin/lttng/uprobe.h b/src/bin/lttng/uprobe.h
new file mode 100644 (file)
index 0000000..852f366
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef SRC_BIN_LTTNG_UPROBE_H
+#define SRC_BIN_LTTNG_UPROBE_H
+
+#include <common/macros.h>
+
+struct lttng_userspace_probe_location;
+
+LTTNG_HIDDEN
+int parse_userspace_probe_opts(const char *opt,
+               struct lttng_userspace_probe_location **uprobe_loc);
+
+#endif /* SRC_BIN_LTTNG_UPROBE_H */
index e88166a438a06f98177d7a722ce5561b111b0617..680d24134cf49d6d58ab6c43cdb4784ed6d2fe04 100644 (file)
@@ -128,6 +128,25 @@ void list_cmd_options(FILE *ofp, struct poptOption *options)
        }
 }
 
+/*
+ * Same as list_cmd_options, but for options specified for argpar.
+ */
+void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options)
+{
+       int i;
+       const struct argpar_opt_descr *option = NULL;
+
+       for (i = 0; options[i].long_name != NULL; i++) {
+               option = &options[i];
+
+               fprintf(ofp, "--%s\n", option->long_name);
+
+               if (isprint(option->short_name)) {
+                       fprintf(ofp, "-%c\n", option->short_name);
+               }
+       }
+}
+
 /*
  * fls: returns the position of the most significant bit.
  * Returns 0 if no bit is set, else returns the position of the most
index fe82364a04c284f03650bf8ab9e467c05c7f6333..ff520639708ae0fcb02e9fa79388bdfb118ea4e7 100644 (file)
@@ -9,6 +9,7 @@
 #define _LTTNG_UTILS_H
 
 #include <popt.h>
+#include "common/argpar/argpar.h"
 
 #include <lttng/lttng.h>
 
@@ -23,6 +24,7 @@ char *get_session_name(void);
 char *get_session_name_quiet(void);
 void list_commands(struct cmd_struct *commands, FILE *ofp);
 void list_cmd_options(FILE *ofp, struct poptOption *options);
+void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options);
 
 /*
  * Return the minimum order for which x <= (1UL << order).
index a50123989475fe1b4235032719ab60834599e185..de091b89fd576cc50fb5adad5e9f6660e24230c5 100644 (file)
@@ -2,7 +2,10 @@
 
 AUTOMAKE_OPTIONS = subdir-objects
 
-SUBDIRS = string-utils
+SUBDIRS = \
+       string-utils \
+       filter \
+       argpar
 
 # Make sure to always distribute all folders
 # since SUBDIRS is decided at configure time.
@@ -20,41 +23,55 @@ DIST_SUBDIRS = \
        config \
        consumer \
        string-utils \
-       fd-tracker
-
+       fd-tracker \
+       filter
 # Common library
 noinst_LTLIBRARIES = libcommon.la
 EXTRA_DIST = mi-lttng-4.0.xsd
 
 libcommon_la_SOURCES = \
-       action.c \
-       buffer-usage.c \
+       actions/action.c \
+       actions/group.c \
+       actions/notify.c \
+       actions/rotate-session.c \
+       actions/snapshot-session.c \
+       actions/start-session.c \
+       actions/stop-session.c \
        buffer-view.h buffer-view.c \
        common.h \
-       condition.c \
+       conditions/buffer-usage.c \
+       conditions/condition.c \
+       conditions/event-rule.c \
+       conditions/session-consumed-size.c \
+       conditions/session-rotation.c \
        context.c context.h \
-       credentials.h \
+       credentials.c credentials.h \
        daemonize.c daemonize.h \
        defaults.c \
+       domain.c \
        dynamic-array.c dynamic-array.h \
        dynamic-buffer.c dynamic-buffer.h \
        endpoint.c \
        error.c error.h \
        evaluation.c \
        event.c \
+       event-rule.c \
+       event-rule-kprobe.c \
+       event-rule-kretprobe.c \
+       event-rule-syscall.c \
+       event-rule-uprobe.c \
+       event-rule-tracepoint.c \
        filter.c filter.h \
        futex.c futex.h \
        location.c \
        mi-lttng.c mi-lttng.h \
        notification.c \
-       notify.c \
        optional.h \
        pipe.c pipe.h \
        readwrite.c readwrite.h \
        runas.c runas.h \
-       session-consumed-size.c \
        session-descriptor.c \
-       session-rotation.c \
+       snapshot.c snapshot.h \
        time.c \
        trace-chunk.c trace-chunk.h \
        trace-chunk-registry.h \
@@ -77,7 +94,8 @@ libcommon_la_LIBADD = \
        $(top_builddir)/src/common/config/libconfig.la \
        $(top_builddir)/src/common/compat/libcompat.la \
        $(top_builddir)/src/common/hashtable/libhashtable.la \
-       $(top_builddir)/src/common/fd-tracker/libfd-tracker.la
+       $(top_builddir)/src/common/fd-tracker/libfd-tracker.la \
+       $(top_builddir)/src/common/filter/libfilter.la
 
 if BUILD_LIB_COMPAT
 SUBDIRS += compat
diff --git a/src/common/action.c b/src/common/action.c
deleted file mode 100644 (file)
index dc72d37..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/action/action-internal.h>
-#include <lttng/action/notify-internal.h>
-#include <common/error.h>
-#include <assert.h>
-
-enum lttng_action_type lttng_action_get_type(struct lttng_action *action)
-{
-       return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN;
-}
-
-LTTNG_HIDDEN
-enum lttng_action_type lttng_action_get_type_const(
-               const struct lttng_action *action)
-{
-       return action->type;
-}
-
-void lttng_action_destroy(struct lttng_action *action)
-{
-       if (!action) {
-               return;
-       }
-
-       assert(action->destroy);
-       action->destroy(action);
-}
-
-LTTNG_HIDDEN
-bool lttng_action_validate(struct lttng_action *action)
-{
-       bool valid;
-
-       if (!action) {
-               valid = false;
-               goto end;
-       }
-
-       if (!action->validate) {
-               /* Sub-class guarantees that it can never be invalid. */
-               valid = true;
-               goto end;
-       }
-
-       valid = action->validate(action);
-end:
-       return valid;
-}
-
-LTTNG_HIDDEN
-int lttng_action_serialize(struct lttng_action *action,
-               struct lttng_dynamic_buffer *buf)
-{
-       int ret;
-       struct lttng_action_comm action_comm = {
-               .action_type = (int8_t) action->type,
-       };
-
-       ret = lttng_dynamic_buffer_append(buf, &action_comm,
-                       sizeof(action_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = action->serialize(action, buf);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_action_create_from_buffer(const struct lttng_buffer_view *view,
-               struct lttng_action **_action)
-{
-       ssize_t ret, action_size = sizeof(struct lttng_action_comm);
-       struct lttng_action *action;
-       const struct lttng_action_comm *action_comm;
-
-       if (!view || !_action) {
-               ret = -1;
-               goto end;
-       }
-
-       action_comm = (const struct lttng_action_comm *) view->data;
-       DBG("Deserializing action from buffer");
-       switch (action_comm->action_type) {
-       case LTTNG_ACTION_TYPE_NOTIFY:
-               action = lttng_action_notify_create();
-               break;
-       default:
-               ret = -1;
-               goto end;
-       }
-
-       if (!action) {
-               ret = -1;
-               goto end;
-       }
-       ret = action_size;
-       *_action = action;
-end:
-       return ret;
-}
diff --git a/src/common/actions/action.c b/src/common/actions/action.c
new file mode 100644 (file)
index 0000000..cab49ae
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/group-internal.h>
+#include <lttng/action/notify-internal.h>
+#include <lttng/action/rotate-session-internal.h>
+#include <lttng/action/snapshot-session-internal.h>
+#include <lttng/action/start-session-internal.h>
+#include <lttng/action/stop-session-internal.h>
+
+LTTNG_HIDDEN
+const char *lttng_action_type_string(enum lttng_action_type action_type)
+{
+       switch (action_type) {
+       case LTTNG_ACTION_TYPE_UNKNOWN:
+               return "UNKNOWN";
+
+       case LTTNG_ACTION_TYPE_GROUP:
+               return "GROUP";
+
+       case LTTNG_ACTION_TYPE_NOTIFY:
+               return "NOTIFY";
+
+       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+               return "ROTATE_SESSION";
+
+       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+               return "SNAPSHOT_SESSION";
+
+       case LTTNG_ACTION_TYPE_START_SESSION:
+               return "START_SESSION";
+
+       case LTTNG_ACTION_TYPE_STOP_SESSION:
+               return "STOP_SESSION";
+
+       default:
+               return "???";
+       }
+}
+
+enum lttng_action_type lttng_action_get_type(const struct lttng_action *action)
+{
+       return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN;
+}
+
+LTTNG_HIDDEN
+enum lttng_action_type lttng_action_get_type_const(
+               const struct lttng_action *action)
+{
+       return action->type;
+}
+
+LTTNG_HIDDEN
+void lttng_action_init(
+               struct lttng_action *action,
+               enum lttng_action_type type,
+               action_validate_cb validate,
+               action_serialize_cb serialize,
+               action_equal_cb equal,
+               action_destroy_cb destroy)
+{
+       urcu_ref_init(&action->ref);
+       action->type = type;
+       action->validate = validate;
+       action->serialize = serialize;
+       action->equal = equal;
+       action->destroy = destroy;
+}
+
+static
+void action_destroy_ref(struct urcu_ref *ref)
+{
+       struct lttng_action *action =
+                       container_of(ref, struct lttng_action, ref);
+
+       action->destroy(action);
+}
+
+LTTNG_HIDDEN
+void lttng_action_get(struct lttng_action *action)
+{
+       urcu_ref_get(&action->ref);
+}
+
+LTTNG_HIDDEN
+void lttng_action_put(struct lttng_action *action)
+{
+       if (!action) {
+               return;
+       }
+
+       assert(action->destroy);
+       urcu_ref_put(&action->ref, action_destroy_ref);
+}
+
+void lttng_action_destroy(struct lttng_action *action)
+{
+       lttng_action_put(action);
+}
+
+LTTNG_HIDDEN
+bool lttng_action_validate(struct lttng_action *action)
+{
+       bool valid;
+
+       if (!action) {
+               valid = false;
+               goto end;
+       }
+
+       if (!action->validate) {
+               /* Sub-class guarantees that it can never be invalid. */
+               valid = true;
+               goto end;
+       }
+
+       valid = action->validate(action);
+end:
+       return valid;
+}
+
+LTTNG_HIDDEN
+int lttng_action_serialize(struct lttng_action *action,
+               struct lttng_dynamic_buffer *buf)
+{
+       int ret;
+       struct lttng_action_comm action_comm = {
+               .action_type = (int8_t) action->type,
+       };
+
+       ret = lttng_dynamic_buffer_append(buf, &action_comm,
+                       sizeof(action_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = action->serialize(action, buf);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_action_create_from_buffer(const struct lttng_buffer_view *view,
+               struct lttng_action **action)
+{
+       ssize_t consumed_len, specific_action_consumed_len;
+       const struct lttng_action_comm *action_comm;
+       action_create_from_buffer_cb create_from_buffer_cb;
+       struct lttng_buffer_view specific_action_view;
+
+       if (!view || !action) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       action_comm = (const struct lttng_action_comm *) view->data;
+
+       DBG("Create action from buffer: action-type=%s",
+                       lttng_action_type_string(action_comm->action_type));
+
+       switch (action_comm->action_type) {
+       case LTTNG_ACTION_TYPE_NOTIFY:
+               create_from_buffer_cb = lttng_action_notify_create_from_buffer;
+               break;
+
+       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+               create_from_buffer_cb =
+                               lttng_action_rotate_session_create_from_buffer;
+               break;
+
+       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+               create_from_buffer_cb =
+                               lttng_action_snapshot_session_create_from_buffer;
+               break;
+
+       case LTTNG_ACTION_TYPE_START_SESSION:
+               create_from_buffer_cb =
+                               lttng_action_start_session_create_from_buffer;
+               break;
+
+       case LTTNG_ACTION_TYPE_STOP_SESSION:
+               create_from_buffer_cb =
+                               lttng_action_stop_session_create_from_buffer;
+               break;
+
+       case LTTNG_ACTION_TYPE_GROUP:
+               create_from_buffer_cb = lttng_action_group_create_from_buffer;
+               break;
+
+       default:
+               ERR("Failed to create action from buffer, unhandled action type: action-type=%u (%s)",
+                               action_comm->action_type,
+                               lttng_action_type_string(
+                                               action_comm->action_type));
+               consumed_len = -1;
+               goto end;
+       }
+
+       /* Create buffer view for the action-type-specific data. */
+       specific_action_view = lttng_buffer_view_from_view(view,
+                       sizeof(struct lttng_action_comm),
+                       view->size - sizeof(struct lttng_action_comm));
+
+       specific_action_consumed_len =
+                       create_from_buffer_cb(&specific_action_view, action);
+       if (specific_action_consumed_len < 0) {
+               ERR("Failed to create specific action from buffer.");
+               consumed_len = -1;
+               goto end;
+       }
+
+       assert(*action);
+
+       consumed_len = sizeof(struct lttng_action_comm) +
+                      specific_action_consumed_len;
+
+end:
+       return consumed_len;
+}
+
+LTTNG_HIDDEN
+bool lttng_action_is_equal(const struct lttng_action *a,
+               const struct lttng_action *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       is_equal = a->equal ? a->equal(a, b) : true;
+end:
+       return is_equal;
+}
+
diff --git a/src/common/actions/group.c b/src/common/actions/group.c
new file mode 100644 (file)
index 0000000..055bb5e
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/group-internal.h>
+#include <lttng/action/group.h>
+
+struct lttng_action_group {
+       struct lttng_action parent;
+
+       /* Owned by this. */
+       struct lttng_action **elements;
+       size_t n_elements;
+       size_t n_elements_allocated;
+};
+
+struct lttng_action_group_comm {
+       uint32_t n_elements;
+
+       /*
+        * Variable data: each element serialized sequentially.
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static struct lttng_action_group *action_group_from_action(
+               const struct lttng_action *action)
+{
+       assert(action);
+
+       return container_of(action, struct lttng_action_group, parent);
+}
+
+static bool lttng_action_group_validate(struct lttng_action *action)
+{
+       size_t i;
+       struct lttng_action_group *action_group;
+       bool valid;
+
+       assert(lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP);
+
+       action_group = action_group_from_action(action);
+
+       for (i = 0; i < action_group->n_elements; i++) {
+               struct lttng_action *child = action_group->elements[i];
+
+               assert(child);
+
+               if (!lttng_action_validate(child)) {
+                       valid = false;
+                       goto end;
+               }
+       }
+
+       valid = true;
+
+end:
+       return valid;
+}
+
+static bool lttng_action_group_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       struct lttng_action_group *a, *b;
+       size_t i;
+
+       assert(lttng_action_get_type_const(_a) == LTTNG_ACTION_TYPE_GROUP);
+       assert(lttng_action_get_type_const(_b) == LTTNG_ACTION_TYPE_GROUP);
+
+       a = action_group_from_action(_a);
+       b = action_group_from_action(_b);
+
+       if (a->n_elements != b->n_elements) {
+               goto end;
+       }
+
+       for (i = 0; i < a->n_elements; i++) {
+               struct lttng_action *child_a = a->elements[i];
+               struct lttng_action *child_b = b->elements[i];
+
+               assert(child_a);
+               assert(child_b);
+
+               if (!lttng_action_is_equal(child_a, child_b)) {
+                       goto end;
+               }
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static int lttng_action_group_serialize(
+               struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+       struct lttng_action_group *action_group;
+       struct lttng_action_group_comm comm;
+       int ret;
+       size_t i;
+
+       assert(action);
+       assert(buf);
+       assert(lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP);
+
+       action_group = action_group_from_action(action);
+
+       DBG("Serializing action group");
+
+       comm.n_elements = action_group->n_elements;
+
+       ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < action_group->n_elements; i++) {
+               struct lttng_action *child = action_group->elements[i];
+
+               assert(child);
+
+               ret = lttng_action_serialize(child, buf);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       ret = 0;
+
+end:
+       return ret;
+}
+
+static void lttng_action_group_destroy(struct lttng_action *action)
+{
+       struct lttng_action_group *action_group;
+       size_t i;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_group = action_group_from_action(action);
+
+       for (i = 0; i < action_group->n_elements; i++) {
+               struct lttng_action *child = action_group->elements[i];
+
+               assert(child);
+
+               lttng_action_destroy(child);
+       }
+
+       free(action_group->elements);
+       free(action_group);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_group_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len;
+       struct lttng_action_group_comm *comm;
+       struct lttng_action *group;
+       struct lttng_action *child_action = NULL;
+       enum lttng_action_status status;
+       size_t i;
+
+       group = lttng_action_group_create();
+       if (!group) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       comm = (struct lttng_action_group_comm *) view->data;
+
+       consumed_len = sizeof(struct lttng_action_group_comm);
+
+       for (i = 0; i < comm->n_elements; i++) {
+               ssize_t consumed_len_child;
+               struct lttng_buffer_view child_view;
+
+               child_view = lttng_buffer_view_from_view(
+                               view, consumed_len, view->size - consumed_len);
+               consumed_len_child = lttng_action_create_from_buffer(
+                               &child_view, &child_action);
+
+               status = lttng_action_group_add_action(group, child_action);
+               if (status != LTTNG_ACTION_STATUS_OK) {
+                       consumed_len = -1;
+                       goto end;
+               }
+               child_action = NULL;
+
+               consumed_len += consumed_len_child;
+       }
+
+       *p_action = group;
+       group = NULL;
+
+end:
+       lttng_action_group_destroy(group);
+       lttng_action_destroy(child_action);
+
+       return consumed_len;
+}
+
+struct lttng_action *lttng_action_group_create(void)
+{
+       struct lttng_action_group *action_group;
+       struct lttng_action *action;
+
+       action_group = zmalloc(sizeof(struct lttng_action_group));
+       if (!action_group) {
+               action = NULL;
+               goto end;
+       }
+
+       action = &action_group->parent;
+
+       lttng_action_init(action, LTTNG_ACTION_TYPE_GROUP,
+                       lttng_action_group_validate,
+                       lttng_action_group_serialize,
+                       lttng_action_group_is_equal,
+                       lttng_action_group_destroy);
+
+       action_group->n_elements = 0;
+       action_group->n_elements_allocated = 10;
+       action_group->elements = zmalloc(10 * sizeof(struct lttng_action *));
+       if (!action_group->elements) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_action_group_destroy(action);
+       action = NULL;
+
+end:
+       return action;
+}
+
+enum lttng_action_status lttng_action_group_add_action(
+               struct lttng_action *group, struct lttng_action *action)
+{
+       struct lttng_action_group *action_group;
+       enum lttng_action_status status;
+
+       if (!group ||
+                       (lttng_action_get_type(group) !=
+                                       LTTNG_ACTION_TYPE_GROUP) ||
+                       !action) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       /*
+        * Don't allow adding groups in groups for now, since we're afraid of
+        * cycles.
+        */
+       if (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_group = action_group_from_action(group);
+
+       if (action_group->n_elements == action_group->n_elements_allocated) {
+               // TODO: handle resize.
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       action_group->elements[action_group->n_elements] = action;
+       action_group->n_elements++;
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_group_get_count(
+               const struct lttng_action *group, unsigned int *count)
+{
+       struct lttng_action_group *action_group;
+       enum lttng_action_status status = LTTNG_ACTION_STATUS_OK;
+
+       if (!group || (lttng_action_get_type_const(group) !=
+                                     LTTNG_ACTION_TYPE_GROUP)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               *count = 0;
+               goto end;
+       }
+
+       action_group = action_group_from_action(group);
+
+
+       *count = action_group->n_elements;
+end:
+       return status;
+}
+
+const struct lttng_action *lttng_action_group_get_at_index_const(
+               const struct lttng_action *group,
+               unsigned int index)
+{
+       struct lttng_action_group *action_group;
+       const struct lttng_action *action = NULL;
+
+       if (!group || (lttng_action_get_type_const(group) !=
+                                     LTTNG_ACTION_TYPE_GROUP)) {
+               goto end;
+       }
+
+       action_group = action_group_from_action(group);
+
+       if (index >= action_group->n_elements) {
+               goto end;
+       }
+
+       action = action_group->elements[index];
+end:
+       return action;
+}
diff --git a/src/common/actions/notify.c b/src/common/actions/notify.c
new file mode 100644 (file)
index 0000000..03dcce7
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/action/action-internal.h>
+#include <lttng/action/notify-internal.h>
+#include <common/macros.h>
+#include <assert.h>
+
+static
+void lttng_action_notify_destroy(struct lttng_action *action)
+{
+       free(action);
+}
+
+static
+int lttng_action_notify_serialize(struct lttng_action *action,
+               struct lttng_dynamic_buffer *buf)
+{
+       return 0;
+}
+
+static
+bool lttng_action_notify_is_equal(const struct lttng_action *a,
+               const struct lttng_action *b)
+{
+       /* TODO check type ??? */
+       return true;
+}
+
+struct lttng_action *lttng_action_notify_create(void)
+{
+       struct lttng_action_notify *notify;
+
+       notify = zmalloc(sizeof(struct lttng_action_notify));
+       if (!notify) {
+               goto end;
+       }
+
+       lttng_action_init(&notify->parent, LTTNG_ACTION_TYPE_NOTIFY, NULL,
+                       lttng_action_notify_serialize,
+                       lttng_action_notify_is_equal,
+                       lttng_action_notify_destroy);
+end:
+       return &notify->parent;
+}
+
+ssize_t lttng_action_notify_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **action)
+{
+       ssize_t consumed_length;
+
+       *action = lttng_action_notify_create();
+       if (!*action) {
+               consumed_length = -1;
+               goto end;
+       }
+
+       consumed_length = 0;
+end:
+       return consumed_length;
+}
diff --git a/src/common/actions/rotate-session.c b/src/common/actions/rotate-session.c
new file mode 100644 (file)
index 0000000..4f81992
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/rotate-session-internal.h>
+#include <lttng/action/rotate-session.h>
+
+struct lttng_action_rotate_session {
+       struct lttng_action parent;
+
+       /* Owned by this. */
+       char *session_name;
+};
+
+struct lttng_action_rotate_session_comm {
+       /* Includes the trailing \0. */
+       uint32_t session_name_len;
+
+       /*
+        * Variable data:
+        *
+        *  - session name (null terminated)
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static struct lttng_action_rotate_session *action_rotate_session_from_action(
+               struct lttng_action *action)
+{
+       assert(action);
+
+       return container_of(action, struct lttng_action_rotate_session, parent);
+}
+
+static const struct lttng_action_rotate_session *action_rotate_session_from_action_const(
+               const struct lttng_action *action)
+{
+       assert(action);
+
+       return container_of(action, struct lttng_action_rotate_session, parent);
+}
+
+static bool lttng_action_rotate_session_validate(struct lttng_action *action)
+{
+       bool valid;
+       struct lttng_action_rotate_session *action_rotate_session;
+
+       if (!action) {
+               valid = false;
+               goto end;
+       }
+
+       action_rotate_session = action_rotate_session_from_action(action);
+
+       /* A non-empty session name is mandatory. */
+       if (!action_rotate_session->session_name ||
+                       strlen(action_rotate_session->session_name) == 0) {
+               valid = false;
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static bool lttng_action_rotate_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       const struct lttng_action_rotate_session *a, *b;
+
+       a = action_rotate_session_from_action_const(_a);
+       b = action_rotate_session_from_action_const(_b);
+
+       /* Action is not valid if this is not true. */
+       assert(a->session_name);
+       assert(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+static int lttng_action_rotate_session_serialize(
+               struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+       struct lttng_action_rotate_session *action_rotate_session;
+       struct lttng_action_rotate_session_comm comm;
+       size_t session_name_len;
+       int ret;
+
+       assert(action);
+       assert(buf);
+
+       action_rotate_session = action_rotate_session_from_action(action);
+
+       assert(action_rotate_session->session_name);
+
+       DBG("Serializing rotate session action: session-name: %s",
+                       action_rotate_session->session_name);
+
+       session_name_len = strlen(action_rotate_session->session_name) + 1;
+       comm.session_name_len = session_name_len;
+
+       ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(buf,
+                       action_rotate_session->session_name, session_name_len);
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
+static void lttng_action_rotate_session_destroy(struct lttng_action *action)
+{
+       struct lttng_action_rotate_session *action_rotate_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_rotate_session = action_rotate_session_from_action(action);
+
+       free(action_rotate_session->session_name);
+       free(action_rotate_session);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_rotate_session_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len;
+       struct lttng_action_rotate_session_comm *comm;
+       const char *session_name;
+       struct lttng_action *action;
+       enum lttng_action_status status;
+
+       action = lttng_action_rotate_session_create();
+       if (!action) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       comm = (struct lttng_action_rotate_session_comm *) view->data;
+       session_name = (const char *) &comm->data;
+
+       if (!lttng_buffer_view_validate_string(
+                           view, session_name, comm->session_name_len)) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       status = lttng_action_rotate_session_set_session_name(
+                       action, session_name);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       consumed_len = sizeof(struct lttng_action_rotate_session_comm) +
+                      comm->session_name_len;
+       *p_action = action;
+       action = NULL;
+
+end:
+       lttng_action_rotate_session_destroy(action);
+
+       return consumed_len;
+}
+
+struct lttng_action *lttng_action_rotate_session_create(void)
+{
+       struct lttng_action *action;
+
+       action = zmalloc(sizeof(struct lttng_action_rotate_session));
+       if (!action) {
+               goto end;
+       }
+
+       lttng_action_init(action, LTTNG_ACTION_TYPE_ROTATE_SESSION,
+                       lttng_action_rotate_session_validate,
+                       lttng_action_rotate_session_serialize,
+                       lttng_action_rotate_session_is_equal,
+                       lttng_action_rotate_session_destroy);
+
+end:
+       return action;
+}
+
+enum lttng_action_status lttng_action_rotate_session_set_session_name(
+               struct lttng_action *action, const char *session_name)
+{
+       struct lttng_action_rotate_session *action_rotate_session;
+       enum lttng_action_status status;
+
+       if (!action || !session_name || strlen(session_name) == 0) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_rotate_session = action_rotate_session_from_action(action);
+
+       free(action_rotate_session->session_name);
+
+       action_rotate_session->session_name = strdup(session_name);
+       if (!action_rotate_session->session_name) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_rotate_session_get_session_name(
+               const struct lttng_action *action, const char **session_name)
+{
+       const struct lttng_action_rotate_session *action_rotate_session;
+       enum lttng_action_status status;
+
+       if (!action || !session_name) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_rotate_session = action_rotate_session_from_action_const(action);
+
+       *session_name = action_rotate_session->session_name;
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
diff --git a/src/common/actions/snapshot-session.c b/src/common/actions/snapshot-session.c
new file mode 100644 (file)
index 0000000..a02cf80
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/snapshot.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/snapshot-session-internal.h>
+#include <lttng/action/snapshot-session.h>
+#include <lttng/snapshot.h>
+#include <lttng/snapshot-internal.h>
+#include <inttypes.h>
+
+struct lttng_action_snapshot_session {
+       struct lttng_action parent;
+
+       /* Owned by this. */
+       char *session_name;
+
+       /*
+        * When non-NULL, use this custom output when taking the snapshot,
+        * rather than the session's registered snapshot output.
+        *
+        * Owned by this.
+        */
+       struct lttng_snapshot_output *output;
+};
+
+struct lttng_action_snapshot_session_comm {
+       /* All string lengths include the trailing \0. */
+       uint32_t session_name_len;
+       uint32_t snapshot_output_len;
+
+       /*
+        * Variable data (all strings are null-terminated):
+        *
+        *  - session name string
+        *  - snapshot output object
+        *
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static struct lttng_action_snapshot_session *
+action_snapshot_session_from_action(struct lttng_action *action)
+{
+       assert(action);
+
+       return container_of(
+                       action, struct lttng_action_snapshot_session, parent);
+}
+
+static const struct lttng_action_snapshot_session *
+action_snapshot_session_from_action_const(const struct lttng_action *action)
+{
+       assert(action);
+
+       return container_of(
+                       action, struct lttng_action_snapshot_session, parent);
+}
+
+static bool lttng_action_snapshot_session_validate(struct lttng_action *action)
+{
+       bool valid = false;
+       struct lttng_action_snapshot_session *action_snapshot_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+
+       /* A non-empty session name is mandatory. */
+       if (!action_snapshot_session->session_name ||
+                       strlen(action_snapshot_session->session_name) == 0) {
+               goto end;
+       }
+
+       if (action_snapshot_session->output &&
+                       !lttng_snapshot_output_validate(action_snapshot_session->output)) {
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static bool lttng_action_snapshot_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       const struct lttng_action_snapshot_session *a, *b;
+
+       a = action_snapshot_session_from_action_const(_a);
+       b = action_snapshot_session_from_action_const(_b);
+
+       /* Action is not valid if this is not true. */
+       assert(a->session_name);
+       assert(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       if (a->output && b->output &&
+                       !lttng_snapshot_output_is_equal(a->output, b->output)) {
+               goto end;
+       } else if (!!a->output != !!b->output) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static size_t serialize_strlen(const char *s)
+{
+
+       size_t len = 0;
+
+       if (s) {
+               len = strlen(s) + 1;
+       }
+
+       return len;
+}
+
+static int lttng_action_snapshot_session_serialize(
+               struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+       struct lttng_action_snapshot_session *action_snapshot_session;
+       struct lttng_action_snapshot_session_comm comm;
+       struct lttng_dynamic_buffer snapshot_output_buf = { 0 };
+       int ret;
+
+       assert(action);
+       assert(buf);
+
+       lttng_dynamic_buffer_init(&snapshot_output_buf);
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+
+       assert(action_snapshot_session->session_name);
+       DBG("Serializing snapshot session action: session-name: %s",
+                       action_snapshot_session->session_name);
+
+       /* Serialize the snapshot output object first, so we know its length. */
+       if (action_snapshot_session->output) {
+               ret = lttng_snapshot_output_serialize(
+                       action_snapshot_session->output, &snapshot_output_buf);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       comm.session_name_len =
+               serialize_strlen(action_snapshot_session->session_name);
+       comm.snapshot_output_len = snapshot_output_buf.size;
+
+       ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(buf,
+                       action_snapshot_session->session_name,
+                       comm.session_name_len);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append_buffer(buf, &snapshot_output_buf);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       lttng_dynamic_buffer_reset(&snapshot_output_buf);
+       return ret;
+}
+
+static void lttng_action_snapshot_session_destroy(struct lttng_action *action)
+{
+       struct lttng_action_snapshot_session *action_snapshot_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+
+       free(action_snapshot_session->session_name);
+       lttng_snapshot_output_destroy(action_snapshot_session->output);
+       free(action_snapshot_session);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_snapshot_session_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len;
+       struct lttng_action_snapshot_session_comm *comm;
+       const char *variable_data;
+       struct lttng_action *action;
+       enum lttng_action_status status;
+       struct lttng_snapshot_output *snapshot_output = NULL;
+
+       action = lttng_action_snapshot_session_create();
+       if (!action) {
+               goto error;
+       }
+
+       comm = (struct lttng_action_snapshot_session_comm *) view->data;
+       variable_data = (const char *) &comm->data;
+
+       consumed_len = sizeof(struct lttng_action_snapshot_session_comm);
+
+       if (!lttng_buffer_view_validate_string(
+                           view, variable_data, comm->session_name_len)) {
+               goto error;
+       }
+
+       status = lttng_action_snapshot_session_set_session_name(
+                       action, variable_data);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               goto error;
+       }
+
+       variable_data += comm->session_name_len;
+       consumed_len += comm->session_name_len;
+
+       /* If there is a snapshot output object, deserialize it. */
+       if (comm->snapshot_output_len > 0) {
+               ssize_t snapshot_output_consumed_len;
+               enum lttng_action_status action_status;
+               struct lttng_buffer_view snapshot_output_buffer_view =
+                       lttng_buffer_view_from_view(view, consumed_len,
+                               comm->snapshot_output_len);
+               if (!snapshot_output_buffer_view.data) {
+                       fprintf(stderr, "Failed to create buffer view for snapshot output.\n");
+                       goto error;
+               }
+
+               snapshot_output_consumed_len =
+                       lttng_snapshot_output_create_from_buffer(
+                               &snapshot_output_buffer_view, &snapshot_output);
+               if (snapshot_output_consumed_len != comm->snapshot_output_len) {
+                       fprintf(stderr, "Failed to deserialize snapshot output object: "
+                               "consumed-len: %zd, expected-len: %" PRIu32,
+                               snapshot_output_consumed_len,
+                               comm->snapshot_output_len);
+                       goto error;
+               }
+
+               action_status = lttng_action_snapshot_session_set_output(
+                       action, snapshot_output);
+               if (action_status != LTTNG_ACTION_STATUS_OK) {
+                       goto error;
+               }
+
+               /* Ownership has been transferred to the action. */
+               snapshot_output = NULL;
+       }
+
+       variable_data += comm->snapshot_output_len;
+       consumed_len += comm->snapshot_output_len;
+
+       *p_action = action;
+       action = NULL;
+
+       goto end;
+
+error:
+       consumed_len = -1;
+
+end:
+       lttng_action_snapshot_session_destroy(action);
+       lttng_snapshot_output_destroy(snapshot_output);
+
+       return consumed_len;
+}
+
+struct lttng_action *lttng_action_snapshot_session_create(void)
+{
+       struct lttng_action *action;
+
+       action = zmalloc(sizeof(struct lttng_action_snapshot_session));
+       if (!action) {
+               goto end;
+       }
+
+       lttng_action_init(action, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION,
+                       lttng_action_snapshot_session_validate,
+                       lttng_action_snapshot_session_serialize,
+                       lttng_action_snapshot_session_is_equal,
+                       lttng_action_snapshot_session_destroy);
+
+end:
+       return action;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_set_session_name(
+               struct lttng_action *action, const char *session_name)
+{
+       struct lttng_action_snapshot_session *action_snapshot_session;
+       enum lttng_action_status status;
+
+       if (!action || !session_name || strlen(session_name) == 0) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+
+       free(action_snapshot_session->session_name);
+
+       action_snapshot_session->session_name = strdup(session_name);
+       if (!action_snapshot_session->session_name) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_get_session_name(
+               const struct lttng_action *action, const char **session_name)
+{
+       const struct lttng_action_snapshot_session *action_snapshot_session;
+       enum lttng_action_status status;
+
+       if (!action || !session_name) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action_const(action);
+
+       if (action_snapshot_session->session_name) {
+               *session_name = action_snapshot_session->session_name;
+               status = LTTNG_ACTION_STATUS_OK;
+       } else {
+               status = LTTNG_ACTION_STATUS_UNSET;
+       }
+
+end:
+
+       return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_set_output(
+               struct lttng_action *action,
+               struct lttng_snapshot_output *output)
+{
+       struct lttng_action_snapshot_session *action_snapshot_session;
+       enum lttng_action_status status;
+
+       if (!action || !output) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+
+       lttng_snapshot_output_destroy(action_snapshot_session->output);
+       action_snapshot_session->output = output;
+
+       status = LTTNG_ACTION_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_get_output_const(
+               const struct lttng_action *action,
+               const struct lttng_snapshot_output **output)
+{
+       const struct lttng_action_snapshot_session *action_snapshot_session;
+       enum lttng_action_status status;
+
+       if (!action || !output) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action_const(action);
+
+       if (action_snapshot_session->output) {
+               *output = action_snapshot_session->output;
+               status = LTTNG_ACTION_STATUS_OK;
+       } else {
+               status = LTTNG_ACTION_STATUS_UNSET;
+       }
+
+end:
+       return status;
+}
diff --git a/src/common/actions/start-session.c b/src/common/actions/start-session.c
new file mode 100644 (file)
index 0000000..7ff9e63
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/start-session-internal.h>
+#include <lttng/action/start-session.h>
+
+struct lttng_action_start_session {
+       struct lttng_action parent;
+
+       /* Owned by this. */
+       char *session_name;
+};
+
+struct lttng_action_start_session_comm {
+       /* Includes the trailing \0. */
+       uint32_t session_name_len;
+
+       /*
+        * Variable data:
+        *
+        *  - session name (null terminated)
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static struct lttng_action_start_session *action_start_session_from_action(
+               struct lttng_action *action)
+{
+       assert(action);
+
+       return container_of(action, struct lttng_action_start_session, parent);
+}
+
+static const struct lttng_action_start_session *action_start_session_from_action_const(
+               const struct lttng_action *action)
+{
+       assert(action);
+
+       return container_of(action, struct lttng_action_start_session, parent);
+}
+
+static bool lttng_action_start_session_validate(struct lttng_action *action)
+{
+       bool valid;
+       struct lttng_action_start_session *action_start_session;
+
+       if (!action) {
+               valid = false;
+               goto end;
+       }
+
+       action_start_session = action_start_session_from_action(action);
+
+       /* A non-empty session name is mandatory. */
+       if (!action_start_session->session_name ||
+                       strlen(action_start_session->session_name) == 0) {
+               valid = false;
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static bool lttng_action_start_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       struct lttng_action_start_session *a, *b;
+
+       a = container_of(_a, struct lttng_action_start_session, parent);
+       b = container_of(_b, struct lttng_action_start_session, parent);
+
+       /* Action is not valid if this is not true. */
+       assert(a->session_name);
+       assert(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static int lttng_action_start_session_serialize(
+               struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+       struct lttng_action_start_session *action_start_session;
+       struct lttng_action_start_session_comm comm;
+       size_t session_name_len;
+       int ret;
+
+       assert(action);
+       assert(buf);
+
+       action_start_session = action_start_session_from_action(action);
+
+       assert(action_start_session->session_name);
+
+       DBG("Serializing start session action: session-name: %s",
+                       action_start_session->session_name);
+
+       session_name_len = strlen(action_start_session->session_name) + 1;
+       comm.session_name_len = session_name_len;
+
+       ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(buf,
+                       action_start_session->session_name, session_name_len);
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
+static void lttng_action_start_session_destroy(struct lttng_action *action)
+{
+       struct lttng_action_start_session *action_start_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_start_session = action_start_session_from_action(action);
+
+       free(action_start_session->session_name);
+       free(action_start_session);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_start_session_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len;
+       struct lttng_action_start_session_comm *comm;
+       const char *session_name;
+       struct lttng_action *action;
+       enum lttng_action_status status;
+
+       action = lttng_action_start_session_create();
+       if (!action) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       comm = (struct lttng_action_start_session_comm *) view->data;
+       session_name = (const char *) &comm->data;
+
+       if (!lttng_buffer_view_validate_string(
+                           view, session_name, comm->session_name_len)) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       status = lttng_action_start_session_set_session_name(
+                       action, session_name);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       consumed_len = sizeof(struct lttng_action_start_session_comm) +
+                      comm->session_name_len;
+       *p_action = action;
+       action = NULL;
+
+end:
+       lttng_action_start_session_destroy(action);
+
+       return consumed_len;
+}
+
+struct lttng_action *lttng_action_start_session_create(void)
+{
+       struct lttng_action *action;
+
+       action = zmalloc(sizeof(struct lttng_action_start_session));
+       if (!action) {
+               goto end;
+       }
+
+       lttng_action_init(action, LTTNG_ACTION_TYPE_START_SESSION,
+                       lttng_action_start_session_validate,
+                       lttng_action_start_session_serialize,
+                       lttng_action_start_session_is_equal,
+                       lttng_action_start_session_destroy);
+
+end:
+       return action;
+}
+
+enum lttng_action_status lttng_action_start_session_set_session_name(
+               struct lttng_action *action, const char *session_name)
+{
+       struct lttng_action_start_session *action_start_session;
+       enum lttng_action_status status;
+
+       if (!action || !session_name || strlen(session_name) == 0) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_start_session = action_start_session_from_action(action);
+
+       free(action_start_session->session_name);
+
+       action_start_session->session_name = strdup(session_name);
+       if (!action_start_session->session_name) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_start_session_get_session_name(
+               const struct lttng_action *action, const char **session_name)
+{
+       const struct lttng_action_start_session *action_start_session;
+       enum lttng_action_status status;
+
+       if (!action || !session_name) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_start_session = action_start_session_from_action_const(action);
+
+       *session_name = action_start_session->session_name;
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
diff --git a/src/common/actions/stop-session.c b/src/common/actions/stop-session.c
new file mode 100644 (file)
index 0000000..6b94896
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/stop-session-internal.h>
+#include <lttng/action/stop-session.h>
+
+struct lttng_action_stop_session {
+       struct lttng_action parent;
+
+       /* Owned by this. */
+       char *session_name;
+};
+
+struct lttng_action_stop_session_comm {
+       /* Includes the trailing \0. */
+       uint32_t session_name_len;
+
+       /*
+        * Variable data:
+        *
+        *  - session name (null terminated)
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static struct lttng_action_stop_session *action_stop_session_from_action(
+               struct lttng_action *action)
+{
+       assert(action);
+
+       return container_of(action, struct lttng_action_stop_session, parent);
+}
+
+static const struct lttng_action_stop_session *action_stop_session_from_action_const(
+               const struct lttng_action *action)
+{
+       assert(action);
+
+       return container_of(action, struct lttng_action_stop_session, parent);
+}
+
+static bool lttng_action_stop_session_validate(struct lttng_action *action)
+{
+       bool valid;
+       struct lttng_action_stop_session *action_stop_session;
+
+       if (!action) {
+               valid = false;
+               goto end;
+       }
+
+       action_stop_session = action_stop_session_from_action(action);
+
+       /* A non-empty session name is mandatory. */
+       if (!action_stop_session->session_name ||
+                       strlen(action_stop_session->session_name) == 0) {
+               valid = false;
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static bool lttng_action_stop_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       const struct lttng_action_stop_session *a, *b;
+
+       a = action_stop_session_from_action_const(_a);
+       b = action_stop_session_from_action_const(_b);
+
+       /* Action is not valid if this is not true. */
+       assert(a->session_name);
+       assert(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static int lttng_action_stop_session_serialize(
+               struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+       struct lttng_action_stop_session *action_stop_session;
+       struct lttng_action_stop_session_comm comm;
+       size_t session_name_len;
+       int ret;
+
+       assert(action);
+       assert(buf);
+
+       action_stop_session = action_stop_session_from_action(action);
+
+       assert(action_stop_session->session_name);
+
+       DBG("Serializing stop session action: session-name: %s",
+                       action_stop_session->session_name);
+
+       session_name_len = strlen(action_stop_session->session_name) + 1;
+       comm.session_name_len = session_name_len;
+
+       ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(buf,
+                       action_stop_session->session_name, session_name_len);
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
+static void lttng_action_stop_session_destroy(struct lttng_action *action)
+{
+       struct lttng_action_stop_session *action_stop_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_stop_session = action_stop_session_from_action(action);
+
+       free(action_stop_session->session_name);
+       free(action_stop_session);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_stop_session_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len;
+       struct lttng_action_stop_session_comm *comm;
+       const char *session_name;
+       struct lttng_action *action;
+       enum lttng_action_status status;
+
+       action = lttng_action_stop_session_create();
+       if (!action) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       comm = (struct lttng_action_stop_session_comm *) view->data;
+       session_name = (const char *) &comm->data;
+
+       if (!lttng_buffer_view_validate_string(
+                           view, session_name, comm->session_name_len)) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       status = lttng_action_stop_session_set_session_name(
+                       action, session_name);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       consumed_len = sizeof(struct lttng_action_stop_session_comm) +
+                      comm->session_name_len;
+       *p_action = action;
+       action = NULL;
+
+end:
+       lttng_action_stop_session_destroy(action);
+
+       return consumed_len;
+}
+
+struct lttng_action *lttng_action_stop_session_create(void)
+{
+       struct lttng_action *action;
+
+       action = zmalloc(sizeof(struct lttng_action_stop_session));
+       if (!action) {
+               goto end;
+       }
+
+       lttng_action_init(action, LTTNG_ACTION_TYPE_STOP_SESSION,
+                       lttng_action_stop_session_validate,
+                       lttng_action_stop_session_serialize,
+                       lttng_action_stop_session_is_equal,
+                       lttng_action_stop_session_destroy);
+
+end:
+       return action;
+}
+
+enum lttng_action_status lttng_action_stop_session_set_session_name(
+               struct lttng_action *action, const char *session_name)
+{
+       struct lttng_action_stop_session *action_stop_session;
+       enum lttng_action_status status;
+
+       if (!action || !session_name || strlen(session_name) == 0) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_stop_session = action_stop_session_from_action(action);
+
+       free(action_stop_session->session_name);
+
+       action_stop_session->session_name = strdup(session_name);
+       if (!action_stop_session->session_name) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_stop_session_get_session_name(
+               const struct lttng_action *action, const char **session_name)
+{
+       const struct lttng_action_stop_session *action_stop_session;
+       enum lttng_action_status status;
+
+       if (!action || !session_name) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_stop_session = action_stop_session_from_action_const(action);
+
+       *session_name = action_stop_session->session_name;
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
diff --git a/src/common/argpar/Makefile.am b/src/common/argpar/Makefile.am
new file mode 100644 (file)
index 0000000..175526a
--- /dev/null
@@ -0,0 +1,3 @@
+noinst_LTLIBRARIES = libargpar.la
+
+libargpar_la_SOURCES = argpar.c argpar.h
diff --git a/src/common/argpar/argpar.c b/src/common/argpar/argpar.c
new file mode 100644 (file)
index 0000000..a8977ea
--- /dev/null
@@ -0,0 +1,746 @@
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@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 <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "argpar.h"
+
+#define argpar_realloc(_ptr, _type, _nmemb) ((_type *) realloc(_ptr, (_nmemb) * sizeof(_type)))
+#define argpar_calloc(_type, _nmemb) ((_type *) calloc((_nmemb), sizeof(_type)))
+#define argpar_zalloc(_type) argpar_calloc(_type, 1)
+
+#define ARGPAR_ASSERT(_cond) assert(_cond)
+
+/*
+ * Structure holding the argpar state between successive argpar_state_parse_next
+ * calls.
+ *
+ * Created with `argpar_state_create` and destroyed with `argpar_state_destroy`.
+ */
+struct argpar_state {
+       /*
+        * Data provided by the user in argpar_state_create, does not change
+        * afterwards.
+        */
+       unsigned int argc;
+       const char * const *argv;
+       const struct argpar_opt_descr *descrs;
+
+       /*
+        * Index of the argument to process in the next argpar_state_parse_next
+        * call.
+        */
+       unsigned int i;
+
+       /* Counter of non-option arguments. */
+       int non_opt_index;
+
+       /*
+        * Short option state: if set, we are in the middle of a short option
+        * group, so we should resume there at the next argpar_state_parse_next
+        * call.
+        */
+       const char *short_opt_ch;
+};
+
+static
+char *argpar_vasprintf(const char *fmt, va_list args)
+{
+       int len1, len2;
+       char *str;
+       va_list args2;
+
+       va_copy(args2, args);
+
+       len1 = vsnprintf(NULL, 0, fmt, args);
+       if (len1 < 0) {
+               str = NULL;
+               goto end;
+       }
+
+       str = malloc(len1 + 1);
+       if (!str) {
+               goto end;
+       }
+
+       len2 = vsnprintf(str, len1 + 1, fmt, args2);
+
+       ARGPAR_ASSERT(len1 == len2);
+
+end:
+       va_end(args2);
+       return str;
+}
+
+
+static
+char *argpar_asprintf(const char *fmt, ...)
+{
+       va_list args;
+       char *str;
+
+       va_start(args, fmt);
+       str = argpar_vasprintf(fmt, args);
+       va_end(args);
+
+       return str;
+}
+
+static
+bool argpar_string_append_printf(char **str, const char *fmt, ...)
+{
+       char *new_str = NULL;
+       char *addendum;
+       bool success;
+       va_list args;
+
+       ARGPAR_ASSERT(str);
+
+       va_start(args, fmt);
+       addendum = argpar_vasprintf(fmt, args);
+       va_end(args);
+
+       if (!addendum) {
+               success = false;
+               goto end;
+       }
+
+       new_str = argpar_asprintf("%s%s", *str ? *str : "", addendum);
+       if (!new_str) {
+               success = false;
+               goto end;
+       }
+
+       free(*str);
+       *str = new_str;
+
+       success = true;
+
+end:
+       free(addendum);
+
+       return success;
+}
+
+ARGPAR_HIDDEN
+void argpar_item_destroy(struct argpar_item *item)
+{
+       if (!item) {
+               goto end;
+       }
+
+       if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+               struct argpar_item_opt * const opt_item = (void *) item;
+
+               free((void *) opt_item->arg);
+       }
+
+       free(item);
+
+end:
+       return;
+}
+
+static
+bool push_item(struct argpar_item_array * const array,
+               struct argpar_item * const item)
+{
+       bool success;
+
+       ARGPAR_ASSERT(array);
+       ARGPAR_ASSERT(item);
+
+       if (array->n_items == array->n_alloc) {
+               unsigned int new_n_alloc = array->n_alloc * 2;
+               struct argpar_item **new_items;
+
+               new_items = argpar_realloc(array->items,
+                       struct argpar_item *, new_n_alloc);
+               if (!new_items) {
+                       success = false;
+                       goto end;
+               }
+
+               array->n_alloc = new_n_alloc;
+               array->items = new_items;
+       }
+
+       array->items[array->n_items] = item;
+       array->n_items++;
+
+       success = true;
+
+end:
+       return success;
+}
+
+static
+void destroy_item_array(struct argpar_item_array * const array)
+{
+       if (array) {
+               unsigned int i;
+
+               for (i = 0; i < array->n_items; i++) {
+                       argpar_item_destroy(array->items[i]);
+               }
+
+               free(array->items);
+               free(array);
+       }
+}
+
+static
+struct argpar_item_array *new_item_array(void)
+{
+       struct argpar_item_array *ret;
+       const int initial_size = 10;
+
+       ret = argpar_zalloc(struct argpar_item_array);
+       if (!ret) {
+               goto end;
+       }
+
+       ret->items = argpar_calloc(struct argpar_item *, initial_size);
+       if (!ret->items) {
+               goto error;
+       }
+
+       ret->n_alloc = initial_size;
+
+       goto end;
+
+error:
+       destroy_item_array(ret);
+       ret = NULL;
+
+end:
+       return ret;
+}
+
+static
+struct argpar_item_opt *create_opt_item(
+               const struct argpar_opt_descr * const descr,
+               const char * const arg)
+{
+       struct argpar_item_opt *opt_item =
+               argpar_zalloc(struct argpar_item_opt);
+
+       if (!opt_item) {
+               goto end;
+       }
+
+       opt_item->base.type = ARGPAR_ITEM_TYPE_OPT;
+       opt_item->descr = descr;
+
+       if (arg) {
+               opt_item->arg = strdup(arg);
+               if (!opt_item->arg) {
+                       goto error;
+               }
+       }
+
+       goto end;
+
+error:
+       argpar_item_destroy(&opt_item->base);
+       opt_item = NULL;
+
+end:
+       return opt_item;
+}
+
+static
+struct argpar_item_non_opt *create_non_opt_item(const char * const arg,
+               const unsigned int orig_index,
+               const unsigned int non_opt_index)
+{
+       struct argpar_item_non_opt * const non_opt_item =
+               argpar_zalloc(struct argpar_item_non_opt);
+
+       if (!non_opt_item) {
+               goto end;
+       }
+
+       non_opt_item->base.type = ARGPAR_ITEM_TYPE_NON_OPT;
+       non_opt_item->arg = arg;
+       non_opt_item->orig_index = orig_index;
+       non_opt_item->non_opt_index = non_opt_index;
+
+end:
+       return non_opt_item;
+}
+
+static
+const struct argpar_opt_descr *find_descr(
+               const struct argpar_opt_descr * const descrs,
+               const char short_name, const char * const long_name)
+{
+       const struct argpar_opt_descr *descr;
+
+       for (descr = descrs; descr->short_name || descr->long_name; descr++) {
+               if (short_name && descr->short_name &&
+                               short_name == descr->short_name) {
+                       goto end;
+               }
+
+               if (long_name && descr->long_name &&
+                               strcmp(long_name, descr->long_name) == 0) {
+                       goto end;
+               }
+       }
+
+end:
+       return !descr->short_name && !descr->long_name ? NULL : descr;
+}
+
+enum parse_orig_arg_opt_ret {
+       PARSE_ORIG_ARG_OPT_RET_OK,
+       PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT = -2,
+       PARSE_ORIG_ARG_OPT_RET_ERROR = -1,
+};
+
+static
+enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts,
+               const char * const next_orig_arg,
+               const struct argpar_opt_descr * const descrs,
+               struct argpar_state *state,
+               char **error,
+               struct argpar_item **item)
+{
+       enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
+       bool used_next_orig_arg = false;
+
+       if (strlen(short_opts) == 0) {
+               argpar_string_append_printf(error, "Invalid argument");
+               goto error;
+       }
+
+       if (!state->short_opt_ch) {
+               state->short_opt_ch = short_opts;
+       }
+
+       const char *opt_arg = NULL;
+       const struct argpar_opt_descr *descr;
+       struct argpar_item_opt *opt_item;
+
+       /* Find corresponding option descriptor */
+       descr = find_descr(descrs, *state->short_opt_ch, NULL);
+       if (!descr) {
+               ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT;
+               argpar_string_append_printf(error,
+                       "Unknown option `-%c`", *state->short_opt_ch);
+               goto error;
+       }
+
+       if (descr->with_arg) {
+               if (state->short_opt_ch[1]) {
+                       /* `-oarg` form */
+                       opt_arg = &state->short_opt_ch[1];
+               } else {
+                       /* `-o arg` form */
+                       opt_arg = next_orig_arg;
+                       used_next_orig_arg = true;
+               }
+
+               /*
+                       * We accept `-o ''` (empty option's argument),
+                       * but not `-o` alone if an option's argument is
+                       * expected.
+                       */
+               if (!opt_arg || (state->short_opt_ch[1] && strlen(opt_arg) == 0)) {
+                       argpar_string_append_printf(error,
+                               "Missing required argument for option `-%c`",
+                               *state->short_opt_ch);
+                       used_next_orig_arg = false;
+                       goto error;
+               }
+       }
+
+       /* Create and append option argument */
+       opt_item = create_opt_item(descr, opt_arg);
+       if (!opt_item) {
+               goto error;
+       }
+
+       *item = &opt_item->base;
+
+       state->short_opt_ch++;
+
+       if (descr->with_arg || !*state->short_opt_ch) {
+               /* Option has an argument: no more options */
+               state->short_opt_ch = NULL;
+
+               if (used_next_orig_arg) {
+                       state->i += 2;
+               } else {
+                       state->i += 1;
+               }
+       }
+
+       goto end;
+
+error:
+       if (ret == PARSE_ORIG_ARG_OPT_RET_OK) {
+               ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
+       }
+
+end:
+       return ret;
+}
+
+static
+enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
+               const char * const next_orig_arg,
+               const struct argpar_opt_descr * const descrs,
+               struct argpar_state *state,
+               char **error,
+               struct argpar_item **item)
+{
+       const size_t max_len = 127;
+       enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
+       const struct argpar_opt_descr *descr;
+       struct argpar_item_opt *opt_item;
+       bool used_next_orig_arg = false;
+
+       /* Option's argument, if any */
+       const char *opt_arg = NULL;
+
+       /* Position of first `=`, if any */
+       const char *eq_pos;
+
+       /* Buffer holding option name when `long_opt_arg` contains `=` */
+       char buf[max_len + 1];
+
+       /* Option name */
+       const char *long_opt_name = long_opt_arg;
+
+       if (strlen(long_opt_arg) == 0) {
+               argpar_string_append_printf(error,
+                       "Invalid argument");
+               goto error;
+       }
+
+       /* Find the first `=` in original argument */
+       eq_pos = strchr(long_opt_arg, '=');
+       if (eq_pos) {
+               const size_t long_opt_name_size = eq_pos - long_opt_arg;
+
+               /* Isolate the option name */
+               if (long_opt_name_size > max_len) {
+                       argpar_string_append_printf(error,
+                               "Invalid argument `--%s`", long_opt_arg);
+                       goto error;
+               }
+
+               memcpy(buf, long_opt_arg, long_opt_name_size);
+               buf[long_opt_name_size] = '\0';
+               long_opt_name = buf;
+       }
+
+       /* Find corresponding option descriptor */
+       descr = find_descr(descrs, '\0', long_opt_name);
+       if (!descr) {
+               argpar_string_append_printf(error,
+                       "Unknown option `--%s`", long_opt_name);
+               ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT;
+               goto error;
+       }
+
+       /* Find option's argument if any */
+       if (descr->with_arg) {
+               if (eq_pos) {
+                       /* `--long-opt=arg` style */
+                       opt_arg = eq_pos + 1;
+               } else {
+                       /* `--long-opt arg` style */
+                       if (!next_orig_arg) {
+                               argpar_string_append_printf(error,
+                                       "Missing required argument for option `--%s`",
+                                       long_opt_name);
+                               goto error;
+                       }
+
+                       opt_arg = next_orig_arg;
+                       used_next_orig_arg = true;
+               }
+       }
+
+       /* Create and append option argument */
+       opt_item = create_opt_item(descr, opt_arg);
+       if (!opt_item) {
+               goto error;
+       }
+
+       if (used_next_orig_arg) {
+               state->i += 2;
+       } else {
+               state->i += 1;
+       }
+
+       *item = &opt_item->base;
+       goto end;
+
+error:
+       if (ret == PARSE_ORIG_ARG_OPT_RET_OK) {
+               ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
+       }
+
+end:
+       return ret;
+}
+
+static
+enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg,
+               const char * const next_orig_arg,
+               const struct argpar_opt_descr * const descrs,
+               struct argpar_state *state,
+               char **error,
+               struct argpar_item **item)
+{
+       enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
+
+       ARGPAR_ASSERT(orig_arg[0] == '-');
+
+       if (orig_arg[1] == '-') {
+               /* Long option */
+               ret = parse_long_opt(&orig_arg[2],
+                       next_orig_arg, descrs, state, error, item);
+       } else {
+               /* Short option */
+               ret = parse_short_opts(&orig_arg[1],
+                       next_orig_arg, descrs, state, error, item);
+       }
+
+       return ret;
+}
+
+static
+bool prepend_while_parsing_arg_to_error(char **error,
+               const unsigned int i, const char * const arg)
+{
+       char *new_error;
+       bool success;
+
+       ARGPAR_ASSERT(error);
+       ARGPAR_ASSERT(*error);
+
+       new_error = argpar_asprintf("While parsing argument #%u (`%s`): %s",
+               i + 1, arg, *error);
+       if (!new_error) {
+               success = false;
+               goto end;
+       }
+
+       free(*error);
+       *error = new_error;
+       success = true;
+
+end:
+       return success;
+}
+
+ARGPAR_HIDDEN
+struct argpar_state *argpar_state_create(
+               unsigned int argc,
+               const char * const *argv,
+               const struct argpar_opt_descr * const descrs)
+{
+       struct argpar_state *state;
+
+       state = argpar_zalloc(struct argpar_state);
+       if (!state) {
+               goto end;
+       }
+
+       state->argc = argc;
+       state->argv = argv;
+       state->descrs = descrs;
+
+end:
+       return state;
+}
+
+ARGPAR_HIDDEN
+void argpar_state_destroy(struct argpar_state *state)
+{
+       free(state);
+}
+
+ARGPAR_HIDDEN
+enum argpar_state_parse_next_status argpar_state_parse_next(
+               struct argpar_state *state,
+               struct argpar_item **item,
+               char **error)
+{
+       enum argpar_state_parse_next_status status;
+
+       ARGPAR_ASSERT(state->i <= state->argc);
+
+       *error = NULL;
+
+       if (state->i == state->argc) {
+               status = ARGPAR_STATE_PARSE_NEXT_STATUS_END;
+               goto end;
+       }
+
+       enum parse_orig_arg_opt_ret parse_orig_arg_opt_ret;
+       const char * const orig_arg = state->argv[state->i];
+       const char * const next_orig_arg =
+               state->i < (state->argc - 1) ? state->argv[state->i + 1] : NULL;
+
+       if (orig_arg[0] != '-') {
+               /* Non-option argument */
+               struct argpar_item_non_opt *non_opt_item =
+                       create_non_opt_item(orig_arg, state->i, state->non_opt_index);
+
+               if (!non_opt_item) {
+                       status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR;
+                       goto end;
+               }
+
+               state->non_opt_index++;
+               state->i++;
+
+               *item = &non_opt_item->base;
+               status = ARGPAR_STATE_PARSE_NEXT_STATUS_OK;
+               goto end;
+       }
+
+       /* Option argument */
+       parse_orig_arg_opt_ret = parse_orig_arg_opt(orig_arg,
+               next_orig_arg, state->descrs, state, error, item);
+       switch (parse_orig_arg_opt_ret) {
+       case PARSE_ORIG_ARG_OPT_RET_OK:
+               status = ARGPAR_STATE_PARSE_NEXT_STATUS_OK;
+               break;
+       case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT:
+               status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT;
+               break;;
+       case PARSE_ORIG_ARG_OPT_RET_ERROR:
+               prepend_while_parsing_arg_to_error(
+                       error, state->i, orig_arg);
+               status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR;
+               break;
+       default:
+               abort();
+       }
+
+end:
+       return status;
+}
+
+ARGPAR_HIDDEN
+int argpar_state_get_ingested_orig_args(struct argpar_state *state)
+{
+       return state->i;
+}
+
+ARGPAR_HIDDEN
+struct argpar_parse_ret argpar_parse(unsigned int argc,
+               const char * const *argv,
+               const struct argpar_opt_descr * const descrs,
+               bool fail_on_unknown_opt)
+{
+       struct argpar_parse_ret parse_ret = { 0 };
+       struct argpar_item *item = NULL;
+       struct argpar_state *state = NULL;
+
+       parse_ret.items = new_item_array();
+       if (!parse_ret.items) {
+               parse_ret.error = strdup("Failed to create items array.");
+               ARGPAR_ASSERT(parse_ret.error);
+               goto error;
+       }
+
+       state = argpar_state_create(argc, argv, descrs);
+       if (!state) {
+               parse_ret.error = strdup("Failed to create argpar state.");
+               ARGPAR_ASSERT(parse_ret.error);
+               goto error;
+       }
+
+       while (true) {
+               enum argpar_state_parse_next_status status;
+
+               status = argpar_state_parse_next(state, &item, &parse_ret.error);
+               if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+                       goto error;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+                       break;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+                       if (fail_on_unknown_opt) {
+                               parse_ret.ingested_orig_args =
+                                       argpar_state_get_ingested_orig_args(state);
+                               prepend_while_parsing_arg_to_error(
+                                       &parse_ret.error, parse_ret.ingested_orig_args,
+                                       argv[parse_ret.ingested_orig_args]);
+                               status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR;
+                               goto error;
+                       }
+
+                       free(parse_ret.error);
+                       parse_ret.error = NULL;
+                       break;
+               }
+
+               ARGPAR_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+               if (!push_item(parse_ret.items, item)) {
+                       goto error;
+               }
+               item = NULL;
+       }
+
+       ARGPAR_ASSERT(!parse_ret.error);
+       parse_ret.ingested_orig_args =
+               argpar_state_get_ingested_orig_args(state);
+       goto end;
+
+error:
+       ARGPAR_ASSERT(parse_ret.error);
+
+       /* That's how we indicate that an error occured */
+       destroy_item_array(parse_ret.items);
+       parse_ret.items = NULL;
+
+end:
+       argpar_state_destroy(state);
+       argpar_item_destroy(item);
+       return parse_ret;
+}
+
+ARGPAR_HIDDEN
+void argpar_parse_ret_fini(struct argpar_parse_ret *ret)
+{
+       ARGPAR_ASSERT(ret);
+
+       destroy_item_array(ret->items);
+       ret->items = NULL;
+
+       free(ret->error);
+       ret->error = NULL;
+}
diff --git a/src/common/argpar/argpar.h b/src/common/argpar/argpar.h
new file mode 100644 (file)
index 0000000..1442f51
--- /dev/null
@@ -0,0 +1,330 @@
+#ifndef BABELTRACE_ARGPAR_H
+#define BABELTRACE_ARGPAR_H
+
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@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.
+ */
+
+/*
+ * argpar is a library that provides facilities for argument parsing.
+ *
+ * Two APIs are available:
+ *
+ *   - The iterator-style API, where you initialize a state object with
+ *     `argpar_state_create`, then repeatedly call `argpar_state_parse_next` to
+ *     get the arguments, until (1) there are no more arguments, (2) the parser
+ *     encounters an error (e.g. unknown option) or (3) you get bored.  This
+ *     API gives you more control on when to stop parsing the arguments.
+ *
+ *   - The parse-everything-in-one-shot-API, where you call `argpar_parse`,
+ *     which parses the arguments until (1) there are not more arguments or
+ *     (2) it encounters a parser error.  It returns you a list of all the
+ *     arguments it was able to parse, which you can consult at your leisure.
+ *
+ * The following describes how arguments are parsed, and applies to both APIs.
+ *
+ * argpar parses the arguments `argv` of which the count is `argc` using the
+ * sentinel-terminated (use `ARGPAR_OPT_DESCR_SENTINEL`) option
+ * descriptor array `descrs`.
+ *
+ * argpar considers ALL the elements of `argv`, including the* first one, so
+ * that you would typically pass `argc - 1` and `&argv[1]` from what main()
+ * receives.
+ *
+ * This argument parser supports:
+ *
+ * * Short options without an argument, possibly tied together:
+ *
+ *       -f -auf -n
+ *
+ * * Short options with argument:
+ *
+ *       -b 45 -f/mein/file -xyzhello
+ *
+ * * Long options without an argument:
+ *
+ *       --five-guys --burger-king --pizza-hut --subway
+ *
+ * * Long options with arguments:
+ *
+ *       --security enable --time=18.56
+ *
+ * * Non-option arguments (anything else).
+ *
+ * This parser does not accept `-` or `--` as arguments. The latter
+ * means "end of options" for many command-line tools, but this function
+ * is all about keeping the order of the arguments, so it does not mean
+ * much to put them at the end. This has the side effect that a
+ * non-option argument cannot have the form of an option, for example if
+ * you need to pass the exact relative path `--component`. In that case,
+ * you would need to pass `./--component`. There's no generic way to
+ * escape `-` for the moment.
+ *
+ * This parser accepts duplicate options (it will output one item for each
+ * instance).
+ *
+ * The returned items are of the type `struct argpar_item *`.  Each item
+ * is to be casted to the appropriate type (`struct argpar_item_opt *` or
+ * `struct argpar_item_non_opt *`) depending on its type.
+ *
+ * The items are returned in the same order that the arguments were parsed,
+ * including non-option arguments. This means, for example, that for
+ *
+ *     --hello --meow=23 /path/to/file -b
+ *
+ * found items are returned in this order: option item (--hello), option item
+ * (--meow=23), non-option item (/path/to/file) and option item (-b).
+ */
+
+#include <stdbool.h>
+
+/* Sentinel for an option descriptor array */
+#define ARGPAR_OPT_DESCR_SENTINEL      { -1, '\0', NULL, false }
+
+/*
+ * ARGPAR_HIDDEN: if argpar is used in some shared library, we don't want them
+ * to be exported by that library, so mark them as "hidden".
+ *
+ * On Windows, symbols are local unless explicitly exported,
+ * see https://gcc.gnu.org/wiki/Visibility
+ */
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define ARGPAR_HIDDEN
+#else
+#define ARGPAR_HIDDEN __attribute__((visibility("hidden")))
+#endif
+
+/* Forward-declaration for the opaque type. */
+struct argpar_state;
+
+/* Option descriptor */
+struct argpar_opt_descr {
+       /* Numeric ID for this option */
+       const int id;
+
+       /* Short option character, or `\0` */
+       const char short_name;
+
+       /* Long option name (without `--`), or `NULL` */
+       const char * const long_name;
+
+       /* True if this option has an argument */
+       const bool with_arg;
+};
+
+/* Item type */
+enum argpar_item_type {
+       /* Option */
+       ARGPAR_ITEM_TYPE_OPT,
+
+       /* Non-option */
+       ARGPAR_ITEM_TYPE_NON_OPT,
+};
+
+/* Base item */
+struct argpar_item {
+       enum argpar_item_type type;
+};
+
+/* Option item */
+struct argpar_item_opt {
+       struct argpar_item base;
+
+       /* Corresponding descriptor */
+       const struct argpar_opt_descr *descr;
+
+       /* Argument, or `NULL` if none */
+       const char *arg;
+};
+
+/* Non-option item */
+struct argpar_item_non_opt {
+       struct argpar_item base;
+
+       /*
+        * Complete argument, pointing to one of the entries of the
+        * original arguments (`argv`).
+        */
+       const char *arg;
+
+       /* Index of this argument amongst all original arguments (`argv`) */
+       unsigned int orig_index;
+
+       /* Index of this argument amongst other non-option arguments */
+       unsigned int non_opt_index;
+};
+
+struct argpar_item_array {
+       /* Array of `struct argpar_item *`, or `NULL` on error */
+       struct argpar_item **items;
+
+       /* Number of used slots in `items`. */
+       unsigned int n_items;
+
+       /* Number of allocated slots in `items`. */
+       unsigned int n_alloc;
+};
+
+/* What is returned by argpar_parse() */
+struct argpar_parse_ret {
+       /* Array of `struct argpar_item *`, or `NULL` on error */
+       struct argpar_item_array *items;
+
+       /* Error string, or `NULL` if none */
+       char *error;
+
+       /* Number of original arguments (`argv`) ingested */
+       unsigned int ingested_orig_args;
+};
+
+/*
+ * Parses arguments in `argv` until the end is reached or an error is
+ * encountered.
+ *
+ * On success, this function returns an array of items
+ * (field `items` of `struct argpar_parse_ret`) corresponding to each parsed
+ * argument.
+ *
+ * In the returned structure, `ingested_orig_args` is the number of
+ * ingested arguments within `argv` to produce the resulting array of
+ * items.
+ *
+ * If `fail_on_unknown_opt` is true, then on success `ingested_orig_args` is
+ * equal to `argc`. Otherwise, `ingested_orig_args` contains the number of
+ * original arguments until an unknown _option_ occurs. For example, with
+ *
+ *     --great --white contact nuance --shark nuclear
+ *
+ * if `--shark` is not described within `descrs` and
+ * `fail_on_unknown_opt` is false, then `ingested_orig_args` is 4 (two
+ * options, two non-options), whereas `argc` is 6.
+ *
+ * This makes it possible to know where a command name is, for example.
+ * With those arguments:
+ *
+ *     --verbose --stuff=23 do-something --specific-opt -f -b
+ *
+ * and the descriptors for `--verbose` and `--stuff` only, the function
+ * returns the `--verbose` and `--stuff` option items, the
+ * `do-something` non-option item, and that three original arguments
+ * were ingested. This means you can start the next argument parsing
+ * stage, with option descriptors depending on the command name, at
+ * `&argv[3]`.
+ *
+ * Note that `ingested_orig_args` is not always equal to the number of
+ * returned items, as
+ *
+ *     --hello -fdw
+ *
+ * for example contains two ingested original arguments, but four
+ * resulting items.
+ *
+ * On failure, the returned structure's `items` member is `NULL`, and
+ * the `error` string member contains details about the error.
+ *
+ * You can finalize the returned structure with
+ * argpar_parse_ret_fini().
+ */
+ARGPAR_HIDDEN
+struct argpar_parse_ret argpar_parse(unsigned int argc,
+               const char * const *argv,
+               const struct argpar_opt_descr *descrs,
+               bool fail_on_unknown_opt);
+
+/*
+ * Finalizes what is returned by argpar_parse().
+ *
+ * It is safe to call argpar_parse() multiple times with the same
+ * structure.
+ */
+ARGPAR_HIDDEN
+void argpar_parse_ret_fini(struct argpar_parse_ret *ret);
+
+/*
+ * Creates an instance of `struct argpar_state`.
+ *
+ * This sets up the argpar_state structure, but does not actually
+ * start parsing the arguments.
+ *
+ * When you are done with it, the state must be freed with
+ * `argpar_state_destroy`.
+ */
+ARGPAR_HIDDEN
+struct argpar_state *argpar_state_create(
+               unsigned int argc,
+               const char * const *argv,
+               const struct argpar_opt_descr * const descrs);
+
+/*
+ * Destroys an instance of `struct argpar_state`.
+ */
+ARGPAR_HIDDEN
+void argpar_state_destroy(struct argpar_state *state);
+
+
+enum argpar_state_parse_next_status {
+       ARGPAR_STATE_PARSE_NEXT_STATUS_OK,
+       ARGPAR_STATE_PARSE_NEXT_STATUS_END,
+       ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT,
+       ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR,
+};
+
+/*
+ * Parses and returns the next argument from `state`.
+ *
+ * On success, an item describing the argument is returned in `*item` and
+ * ARGPAR_STATE_PARSE_NEXT_STATUS_OK is returned.  The item must be freed with
+ * `argpar_item_destroy`.
+ *
+ * If there are no more arguments to parse, ARGPAR_STATE_PARSE_NEXT_STATUS_END
+ * is returned.
+ *
+ * On failure (status codes ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT and
+ * ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR), an error string is returned in `*error`.
+ * This string must be freed with `free`.
+ */
+enum argpar_state_parse_next_status argpar_state_parse_next(
+               struct argpar_state *state,
+               struct argpar_item **item,
+               char **error);
+
+/*
+ * Return the number of ingested elements from argv that were required to
+ * produce the previously returned items.
+ */
+ARGPAR_HIDDEN
+int argpar_state_get_ingested_orig_args(struct argpar_state *state);
+
+/*
+ * Destroy an instance of `struct argpar_item`, as returned by
+ * argpar_state_parse_next.
+ */
+ARGPAR_HIDDEN
+void argpar_item_destroy(struct argpar_item *item);
+
+#define ARGPAR_ITEM_DESTROY_AND_RESET(_item)   \
+       {                                       \
+               argpar_item_destroy(_item);     \
+               _item = NULL;                   \
+       }
+
+
+#endif /* BABELTRACE_ARGPAR_H */
diff --git a/src/common/buffer-usage.c b/src/common/buffer-usage.c
deleted file mode 100644 (file)
index 92081c9..0000000
+++ /dev/null
@@ -1,819 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/buffer-usage-internal.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <assert.h>
-#include <math.h>
-#include <float.h>
-#include <time.h>
-
-#define IS_USAGE_CONDITION(condition) ( \
-       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || \
-       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH   \
-       )
-
-static
-double fixed_to_double(uint32_t val)
-{
-       return (double) val / (double) UINT32_MAX;
-}
-
-static
-uint64_t double_to_fixed(double val)
-{
-       return (val * (double) UINT32_MAX);
-}
-
-static
-bool is_usage_evaluation(const struct lttng_evaluation *evaluation)
-{
-       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
-
-       return type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW ||
-                       type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH;
-}
-
-static
-void lttng_condition_buffer_usage_destroy(struct lttng_condition *condition)
-{
-       struct lttng_condition_buffer_usage *usage;
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-
-       free(usage->session_name);
-       free(usage->channel_name);
-       free(usage);
-}
-
-static
-bool lttng_condition_buffer_usage_validate(
-               const struct lttng_condition *condition)
-{
-       bool valid = false;
-       struct lttng_condition_buffer_usage *usage;
-
-       if (!condition) {
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->session_name) {
-               ERR("Invalid buffer condition: a target session name must be set.");
-               goto end;
-       }
-       if (!usage->channel_name) {
-               ERR("Invalid buffer condition: a target channel name must be set.");
-               goto end;
-       }
-       if (!usage->threshold_ratio.set && !usage->threshold_bytes.set) {
-               ERR("Invalid buffer condition: a threshold must be set.");
-               goto end;
-       }
-       if (!usage->domain.set) {
-               ERR("Invalid buffer usage condition: a domain must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static
-int lttng_condition_buffer_usage_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_dynamic_buffer *buf)
-{
-       int ret;
-       struct lttng_condition_buffer_usage *usage;
-       size_t session_name_len, channel_name_len;
-       struct lttng_condition_buffer_usage_comm usage_comm;
-
-       if (!condition || !IS_USAGE_CONDITION(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing buffer usage condition");
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-
-       session_name_len = strlen(usage->session_name) + 1;
-       channel_name_len = strlen(usage->channel_name) + 1;
-       if (session_name_len > LTTNG_NAME_MAX ||
-                       channel_name_len > LTTNG_NAME_MAX) {
-               ret = -1;
-               goto end;
-       }
-
-       usage_comm.threshold_set_in_bytes = !!usage->threshold_bytes.set;
-       usage_comm.session_name_len = session_name_len;
-       usage_comm.channel_name_len = channel_name_len;
-       usage_comm.domain_type = (int8_t) usage->domain.type;
-
-       if (usage->threshold_bytes.set) {
-               usage_comm.threshold = usage->threshold_bytes.value;
-       } else {
-               uint64_t val = double_to_fixed(
-                               usage->threshold_ratio.value);
-
-               if (val > UINT32_MAX) {
-                       /* overflow. */
-                       ret = -1;
-                       goto end;
-               }
-               usage_comm.threshold = val;
-       }
-
-       ret = lttng_dynamic_buffer_append(buf, &usage_comm,
-                       sizeof(usage_comm));
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(buf, usage->session_name,
-                       session_name_len);
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(buf, usage->channel_name,
-                       channel_name_len);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static
-bool lttng_condition_buffer_usage_is_equal(const struct lttng_condition *_a,
-               const struct lttng_condition *_b)
-{
-       bool is_equal = false;
-       struct lttng_condition_buffer_usage *a, *b;
-
-       a = container_of(_a, struct lttng_condition_buffer_usage, parent);
-       b = container_of(_b, struct lttng_condition_buffer_usage, parent);
-
-       if ((a->threshold_ratio.set && !b->threshold_ratio.set) ||
-                       (a->threshold_bytes.set && !b->threshold_bytes.set)) {
-               goto end;
-       }
-
-       if (a->threshold_ratio.set && b->threshold_ratio.set) {
-               double a_value, b_value, diff;
-
-               a_value = a->threshold_ratio.value;
-               b_value = b->threshold_ratio.value;
-               diff = fabs(a_value - b_value);
-
-               if (diff > DBL_EPSILON) {
-                       goto end;
-               }
-       } else if (a->threshold_bytes.set && b->threshold_bytes.set) {
-               uint64_t a_value, b_value;
-
-               a_value = a->threshold_bytes.value;
-               b_value = b->threshold_bytes.value;
-               if (a_value != b_value) {
-                       goto end;
-               }
-       }
-
-       /* Condition is not valid if this is not true. */
-       assert(a->session_name);
-       assert(b->session_name);
-       if (strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       assert(a->channel_name);
-       assert(b->channel_name);
-       if (strcmp(a->channel_name, b->channel_name)) {
-               goto end;
-       }
-
-       assert(a->domain.set);
-       assert(b->domain.set);
-       if (a->domain.type != b->domain.type) {
-               goto end;
-       }
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-static
-struct lttng_condition *lttng_condition_buffer_usage_create(
-               enum lttng_condition_type type)
-{
-       struct lttng_condition_buffer_usage *condition;
-
-       condition = zmalloc(sizeof(struct lttng_condition_buffer_usage));
-       if (!condition) {
-               return NULL;
-       }
-
-       lttng_condition_init(&condition->parent, type);
-       condition->parent.validate = lttng_condition_buffer_usage_validate;
-       condition->parent.serialize = lttng_condition_buffer_usage_serialize;
-       condition->parent.equal = lttng_condition_buffer_usage_is_equal;
-       condition->parent.destroy = lttng_condition_buffer_usage_destroy;
-       return &condition->parent;
-}
-
-struct lttng_condition *lttng_condition_buffer_usage_low_create(void)
-{
-       return lttng_condition_buffer_usage_create(
-                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW);
-}
-
-struct lttng_condition *lttng_condition_buffer_usage_high_create(void)
-{
-       return lttng_condition_buffer_usage_create(
-                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH);
-}
-
-static
-ssize_t init_condition_from_buffer(struct lttng_condition *condition,
-               const struct lttng_buffer_view *src_view)
-{
-       ssize_t ret, condition_size;
-       enum lttng_condition_status status;
-       enum lttng_domain_type domain_type;
-       const struct lttng_condition_buffer_usage_comm *condition_comm;
-       const char *session_name, *channel_name;
-       struct lttng_buffer_view names_view;
-
-       if (src_view->size < sizeof(*condition_comm)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm = (const struct lttng_condition_buffer_usage_comm *) src_view->data;
-       names_view = lttng_buffer_view_from_view(src_view,
-                       sizeof(*condition_comm), -1);
-
-       if (condition_comm->session_name_len > LTTNG_NAME_MAX ||
-                       condition_comm->channel_name_len > LTTNG_NAME_MAX) {
-               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
-               ret = -1;
-               goto end;
-       }
-
-       if (names_view.size <
-                       (condition_comm->session_name_len +
-                       condition_comm->channel_name_len)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
-               ret = -1;
-               goto end;
-       }
-
-       if (condition_comm->threshold_set_in_bytes) {
-               status = lttng_condition_buffer_usage_set_threshold(condition,
-                               condition_comm->threshold);
-       } else {
-               status = lttng_condition_buffer_usage_set_threshold_ratio(
-                               condition,
-                               fixed_to_double(condition_comm->threshold));
-       }
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to initialize buffer usage condition threshold");
-               ret = -1;
-               goto end;
-       }
-
-       if (condition_comm->domain_type <= LTTNG_DOMAIN_NONE ||
-                       condition_comm->domain_type > LTTNG_DOMAIN_PYTHON) {
-               /* Invalid domain value. */
-               ERR("Invalid domain type value (%i) found in condition buffer",
-                               (int) condition_comm->domain_type);
-               ret = -1;
-               goto end;
-       }
-
-       domain_type = (enum lttng_domain_type) condition_comm->domain_type;
-       status = lttng_condition_buffer_usage_set_domain_type(condition,
-                       domain_type);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set buffer usage condition domain");
-               ret = -1;
-               goto end;
-       }
-
-       session_name = names_view.data;
-       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
-               ERR("Malformed session name encountered in condition buffer");
-               ret = -1;
-               goto end;
-       }
-
-       channel_name = session_name + condition_comm->session_name_len;
-       if (*(channel_name + condition_comm->channel_name_len - 1) != '\0') {
-               ERR("Malformed channel name encountered in condition buffer");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_buffer_usage_set_session_name(condition,
-                       session_name);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set buffer usage session name");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_buffer_usage_set_channel_name(condition,
-                       channel_name);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set buffer usage channel name");
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_condition_validate(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       condition_size = sizeof(*condition_comm) +
-                       (ssize_t) condition_comm->session_name_len +
-                       (ssize_t) condition_comm->channel_name_len;
-       ret = condition_size;
-end:
-       return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_buffer_usage_low_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_condition **_condition)
-{
-       ssize_t ret;
-       struct lttng_condition *condition =
-                       lttng_condition_buffer_usage_low_create();
-
-       if (!_condition || !condition) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = init_condition_from_buffer(condition, view);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_condition = condition;
-       return ret;
-error:
-       lttng_condition_destroy(condition);
-       return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_buffer_usage_high_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_condition **_condition)
-{
-       ssize_t ret;
-       struct lttng_condition *condition =
-                       lttng_condition_buffer_usage_high_create();
-
-       if (!_condition || !condition) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = init_condition_from_buffer(condition, view);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_condition = condition;
-       return ret;
-error:
-       lttng_condition_destroy(condition);
-       return ret;
-}
-
-static
-struct lttng_evaluation *create_evaluation_from_buffer(
-               enum lttng_condition_type type,
-               const struct lttng_buffer_view *view)
-{
-       const struct lttng_evaluation_buffer_usage_comm *comm =
-                       (const struct lttng_evaluation_buffer_usage_comm *) view->data;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (view->size < sizeof(*comm)) {
-               goto end;
-       }
-
-       evaluation = lttng_evaluation_buffer_usage_create(type,
-                       comm->buffer_use, comm->buffer_capacity);
-end:
-       return evaluation;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_buffer_usage_low_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       evaluation = create_evaluation_from_buffer(
-                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, view);
-       if (!evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       *_evaluation = evaluation;
-       ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
-       return ret;
-error:
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_buffer_usage_high_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       evaluation = create_evaluation_from_buffer(
-                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, view);
-       if (!evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       *_evaluation = evaluation;
-       ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
-       return ret;
-error:
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_threshold_ratio(
-               const struct lttng_condition *condition,
-               double *threshold_ratio)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) ||
-                       !threshold_ratio) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->threshold_ratio.set) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *threshold_ratio = usage->threshold_ratio.value;
-end:
-       return status;
-}
-
-/* threshold_ratio expressed as [0.0, 1.0]. */
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_threshold_ratio(
-               struct lttng_condition *condition, double threshold_ratio)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) ||
-                       threshold_ratio < 0.0 ||
-                       threshold_ratio > 1.0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       usage->threshold_ratio.set = true;
-       usage->threshold_bytes.set = false;
-       usage->threshold_ratio.value = threshold_ratio;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_threshold(
-               const struct lttng_condition *condition,
-               uint64_t *threshold_bytes)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !threshold_bytes) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->threshold_bytes.set) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *threshold_bytes = usage->threshold_bytes.value;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_threshold(
-               struct lttng_condition *condition, uint64_t threshold_bytes)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition)) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       usage->threshold_ratio.set = false;
-       usage->threshold_bytes.set = true;
-       usage->threshold_bytes.value = threshold_bytes;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_session_name(
-               const struct lttng_condition *condition,
-               const char **session_name)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !session_name) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->session_name) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *session_name = usage->session_name;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_session_name(
-               struct lttng_condition *condition, const char *session_name)
-{
-       char *session_name_copy;
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !session_name ||
-                       strlen(session_name) == 0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       session_name_copy = strdup(session_name);
-       if (!session_name_copy) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       if (usage->session_name) {
-               free(usage->session_name);
-       }
-       usage->session_name = session_name_copy;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_channel_name(
-               const struct lttng_condition *condition,
-               const char **channel_name)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->channel_name) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *channel_name = usage->channel_name;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_channel_name(
-               struct lttng_condition *condition, const char *channel_name)
-{
-       char *channel_name_copy;
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name ||
-                       strlen(channel_name) == 0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       channel_name_copy = strdup(channel_name);
-       if (!channel_name_copy) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       if (usage->channel_name) {
-               free(usage->channel_name);
-       }
-       usage->channel_name = channel_name_copy;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_domain_type(
-               const struct lttng_condition *condition,
-               enum lttng_domain_type *type)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !type) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->domain.set) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *type = usage->domain.type;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_domain_type(
-               struct lttng_condition *condition, enum lttng_domain_type type)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) ||
-                       type == LTTNG_DOMAIN_NONE) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       usage->domain.set = true;
-       usage->domain.type = type;
-end:
-       return status;
-}
-
-static
-int lttng_evaluation_buffer_usage_serialize(
-               const struct lttng_evaluation *evaluation,
-               struct lttng_dynamic_buffer *buf)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-       struct lttng_evaluation_buffer_usage_comm comm;
-
-       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
-                       parent);
-       comm.buffer_use = usage->buffer_use;
-       comm.buffer_capacity = usage->buffer_capacity;
-
-       return lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
-}
-
-static
-void lttng_evaluation_buffer_usage_destroy(
-               struct lttng_evaluation *evaluation)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-
-       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
-                       parent);
-       free(usage);
-}
-
-LTTNG_HIDDEN
-struct lttng_evaluation *lttng_evaluation_buffer_usage_create(
-               enum lttng_condition_type type, uint64_t use, uint64_t capacity)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-
-       usage = zmalloc(sizeof(struct lttng_evaluation_buffer_usage));
-       if (!usage) {
-               goto end;
-       }
-
-       usage->parent.type = type;
-       usage->buffer_use = use;
-       usage->buffer_capacity = capacity;
-       usage->parent.serialize = lttng_evaluation_buffer_usage_serialize;
-       usage->parent.destroy = lttng_evaluation_buffer_usage_destroy;
-end:
-       return &usage->parent;
-}
-
-/*
- * Get the sampled buffer usage which caused the associated condition to
- * evaluate to "true".
- */
-enum lttng_evaluation_status
-lttng_evaluation_buffer_usage_get_usage_ratio(
-               const struct lttng_evaluation *evaluation, double *usage_ratio)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !is_usage_evaluation(evaluation) || !usage_ratio) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
-                       parent);
-       *usage_ratio = (double) usage->buffer_use /
-                       (double) usage->buffer_capacity;
-end:
-       return status;
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_buffer_usage_get_usage(
-               const struct lttng_evaluation *evaluation,
-               uint64_t *usage_bytes)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !is_usage_evaluation(evaluation) || !usage_bytes) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
-                       parent);
-       *usage_bytes = usage->buffer_use;
-end:
-       return status;
-}
index 4bdb1eb7d75d48be514689200954d69c44a7809b..1065423913b060980bcfd316586882e1f0b0e267 100644 (file)
@@ -67,3 +67,45 @@ struct lttng_buffer_view lttng_buffer_view_from_dynamic_buffer(
 end:
        return view;
 }
+
+LTTNG_HIDDEN
+bool lttng_buffer_view_validate_string(const struct lttng_buffer_view *buf,
+               const char *str,
+               size_t len_with_null_terminator)
+{
+       const char *past_buf_end;
+       size_t max_str_len_with_null_terminator;
+       size_t str_len;
+       bool ret;
+
+       past_buf_end = buf->data + buf->size;
+
+       /* Is the start of the string in the buffer view? */
+       if (str < buf->data || str >= past_buf_end) {
+               ret = false;
+               goto end;
+       }
+
+       /*
+        * Max length the string could have to fit in the buffer, including
+        * NULL terminator.
+        */
+       max_str_len_with_null_terminator = past_buf_end - str;
+
+       /* Could the string even fit in the buffer? */
+       if (len_with_null_terminator > max_str_len_with_null_terminator) {
+               ret = false;
+               goto end;
+       }
+
+       str_len = lttng_strnlen(str, max_str_len_with_null_terminator);
+       if (str_len != (len_with_null_terminator - 1)) {
+               ret = false;
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       return ret;
+}
index e8c351b3f994974a847bf4936812ad073b445ef4..4486558aa9fe497ffb884511d41540b065d5af40 100644 (file)
@@ -8,9 +8,10 @@
 #ifndef LTTNG_BUFFER_VIEW_H
 #define LTTNG_BUFFER_VIEW_H
 
+#include <common/macros.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
-#include <common/macros.h>
 
 struct lttng_dynamic_buffer;
 
@@ -70,4 +71,22 @@ struct lttng_buffer_view lttng_buffer_view_from_dynamic_buffer(
                const struct lttng_dynamic_buffer *src, size_t offset,
                ptrdiff_t len);
 
+/**
+ * Validate that string starting at `str` of length `len_with_null_terminator`
+ * (including the NULL terminator) fits within the buffer view, and validate
+ * that it is indeed of that length.
+ *
+ * Return true if the validation passes, false otherwise.
+ *
+ * @buf                                The buffer view
+ * @str                                The start of the string
+ * @len_with_null_terminator   Expected length of the string, including the
+ *                             NULL terminator.
+ *
+ */
+LTTNG_HIDDEN
+bool lttng_buffer_view_validate_string(const struct lttng_buffer_view *buf,
+               const char *str,
+               size_t len_with_null_terminator);
+
 #endif /* LTTNG_BUFFER_VIEW_H */
diff --git a/src/common/condition.c b/src/common/condition.c
deleted file mode 100644 (file)
index 076f062..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/buffer-usage-internal.h>
-#include <lttng/condition/session-consumed-size-internal.h>
-#include <lttng/condition/session-rotation-internal.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <common/dynamic-buffer.h>
-#include <common/buffer-view.h>
-#include <stdbool.h>
-#include <assert.h>
-
-enum lttng_condition_type lttng_condition_get_type(
-               const struct lttng_condition *condition)
-{
-       return condition ? condition->type : LTTNG_CONDITION_TYPE_UNKNOWN;
-}
-
-void lttng_condition_destroy(struct lttng_condition *condition)
-{
-       if (!condition) {
-               return;
-       }
-
-       assert(condition->destroy);
-       condition->destroy(condition);
-}
-
-LTTNG_HIDDEN
-bool lttng_condition_validate(const struct lttng_condition *condition)
-{
-       bool valid;
-
-       if (!condition) {
-               valid = false;
-               goto end;
-       }
-
-       if (!condition->validate) {
-               /* Sub-class guarantees that it can never be invalid. */
-               valid = true;
-               goto end;
-       }
-
-       valid = condition->validate(condition);
-end:
-       return valid;
-}
-
-LTTNG_HIDDEN
-int lttng_condition_serialize(const struct lttng_condition *condition,
-               struct lttng_dynamic_buffer *buf)
-{
-       int ret;
-       struct lttng_condition_comm condition_comm = { 0 };
-
-       if (!condition) {
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm.condition_type = (int8_t) condition->type;
-
-       ret = lttng_dynamic_buffer_append(buf, &condition_comm,
-                       sizeof(condition_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = condition->serialize(condition, buf);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-LTTNG_HIDDEN
-bool lttng_condition_is_equal(const struct lttng_condition *a,
-               const struct lttng_condition *b)
-{
-       bool is_equal = false;
-
-       if (!a || !b) {
-               goto end;
-       }
-
-       if (a->type != b->type) {
-               goto end;
-       }
-
-       if (a == b) {
-               is_equal = true;
-               goto end;
-       }
-
-       is_equal = a->equal ? a->equal(a, b) : true;
-end:
-       return is_equal;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_create_from_buffer(
-               const struct lttng_buffer_view *buffer,
-               struct lttng_condition **condition)
-{
-       ssize_t ret, condition_size = 0;
-       const struct lttng_condition_comm *condition_comm;
-       condition_create_from_buffer_cb create_from_buffer = NULL;
-
-       if (!buffer || !condition) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Deserializing condition from buffer");
-       condition_comm = (const struct lttng_condition_comm *) buffer->data;
-       condition_size += sizeof(*condition_comm);
-
-       switch ((enum lttng_condition_type) condition_comm->condition_type) {
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
-               create_from_buffer = lttng_condition_buffer_usage_low_create_from_buffer;
-               break;
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-               create_from_buffer = lttng_condition_buffer_usage_high_create_from_buffer;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
-               create_from_buffer = lttng_condition_session_consumed_size_create_from_buffer;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-               create_from_buffer = lttng_condition_session_rotation_ongoing_create_from_buffer;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               create_from_buffer = lttng_condition_session_rotation_completed_create_from_buffer;
-               break;
-       default:
-               ERR("Attempted to create condition of unknown type (%i)",
-                               (int) condition_comm->condition_type);
-               ret = -1;
-               goto end;
-       }
-
-       if (create_from_buffer) {
-               const struct lttng_buffer_view view =
-                               lttng_buffer_view_from_view(buffer,
-                                       sizeof(*condition_comm), -1);
-
-               ret = create_from_buffer(&view, condition);
-               if (ret < 0) {
-                       goto end;
-               }
-               condition_size += ret;
-
-       } else {
-               abort();
-       }
-
-       ret = condition_size;
-end:
-       return ret;
-}
-
-LTTNG_HIDDEN
-void lttng_condition_init(struct lttng_condition *condition,
-               enum lttng_condition_type type)
-{
-       condition->type = type;
-}
diff --git a/src/common/conditions/buffer-usage.c b/src/common/conditions/buffer-usage.c
new file mode 100644 (file)
index 0000000..465a926
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/buffer-usage-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include <time.h>
+
+#define IS_USAGE_CONDITION(condition) ( \
+       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || \
+       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH   \
+       )
+
+static
+double fixed_to_double(uint32_t val)
+{
+       return (double) val / (double) UINT32_MAX;
+}
+
+static
+uint64_t double_to_fixed(double val)
+{
+       return (val * (double) UINT32_MAX);
+}
+
+static
+bool is_usage_evaluation(const struct lttng_evaluation *evaluation)
+{
+       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
+
+       return type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW ||
+                       type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH;
+}
+
+static
+void lttng_condition_buffer_usage_destroy(struct lttng_condition *condition)
+{
+       struct lttng_condition_buffer_usage *usage;
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+
+       free(usage->session_name);
+       free(usage->channel_name);
+       free(usage);
+}
+
+static
+bool lttng_condition_buffer_usage_validate(
+               const struct lttng_condition *condition)
+{
+       bool valid = false;
+       struct lttng_condition_buffer_usage *usage;
+
+       if (!condition) {
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->session_name) {
+               ERR("Invalid buffer condition: a target session name must be set.");
+               goto end;
+       }
+       if (!usage->channel_name) {
+               ERR("Invalid buffer condition: a target channel name must be set.");
+               goto end;
+       }
+       if (!usage->threshold_ratio.set && !usage->threshold_bytes.set) {
+               ERR("Invalid buffer condition: a threshold must be set.");
+               goto end;
+       }
+       if (!usage->domain.set) {
+               ERR("Invalid buffer usage condition: a domain must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_condition_buffer_usage_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       struct lttng_condition_buffer_usage *usage;
+       size_t session_name_len, channel_name_len;
+       struct lttng_condition_buffer_usage_comm usage_comm;
+
+       if (!condition || !IS_USAGE_CONDITION(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing buffer usage condition");
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+
+       session_name_len = strlen(usage->session_name) + 1;
+       channel_name_len = strlen(usage->channel_name) + 1;
+       if (session_name_len > LTTNG_NAME_MAX ||
+                       channel_name_len > LTTNG_NAME_MAX) {
+               ret = -1;
+               goto end;
+       }
+
+       usage_comm.threshold_set_in_bytes = !!usage->threshold_bytes.set;
+       usage_comm.session_name_len = session_name_len;
+       usage_comm.channel_name_len = channel_name_len;
+       usage_comm.domain_type = (int8_t) usage->domain.type;
+
+       if (usage->threshold_bytes.set) {
+               usage_comm.threshold = usage->threshold_bytes.value;
+       } else {
+               uint64_t val = double_to_fixed(
+                               usage->threshold_ratio.value);
+
+               if (val > UINT32_MAX) {
+                       /* overflow. */
+                       ret = -1;
+                       goto end;
+               }
+               usage_comm.threshold = val;
+       }
+
+       ret = lttng_dynamic_buffer_append(buf, &usage_comm,
+                       sizeof(usage_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, usage->session_name,
+                       session_name_len);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, usage->channel_name,
+                       channel_name_len);
+       if (ret) {
+               goto end;
+       }
+
+       if (fd_to_send) {
+               /* No fd to send */
+               *fd_to_send = -1;
+       }
+end:
+       return ret;
+}
+
+static
+bool lttng_condition_buffer_usage_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b)
+{
+       bool is_equal = false;
+       struct lttng_condition_buffer_usage *a, *b;
+
+       a = container_of(_a, struct lttng_condition_buffer_usage, parent);
+       b = container_of(_b, struct lttng_condition_buffer_usage, parent);
+
+       if ((a->threshold_ratio.set && !b->threshold_ratio.set) ||
+                       (a->threshold_bytes.set && !b->threshold_bytes.set)) {
+               goto end;
+       }
+
+       if (a->threshold_ratio.set && b->threshold_ratio.set) {
+               double a_value, b_value, diff;
+
+               a_value = a->threshold_ratio.value;
+               b_value = b->threshold_ratio.value;
+               diff = fabs(a_value - b_value);
+
+               if (diff > DBL_EPSILON) {
+                       goto end;
+               }
+       } else if (a->threshold_bytes.set && b->threshold_bytes.set) {
+               uint64_t a_value, b_value;
+
+               a_value = a->threshold_bytes.value;
+               b_value = b->threshold_bytes.value;
+               if (a_value != b_value) {
+                       goto end;
+               }
+       }
+
+       /* Condition is not valid if this is not true. */
+       assert(a->session_name);
+       assert(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       assert(a->channel_name);
+       assert(b->channel_name);
+       if (strcmp(a->channel_name, b->channel_name)) {
+               goto end;
+       }
+
+       assert(a->domain.set);
+       assert(b->domain.set);
+       if (a->domain.type != b->domain.type) {
+               goto end;
+       }
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static
+struct lttng_condition *lttng_condition_buffer_usage_create(
+               enum lttng_condition_type type)
+{
+       struct lttng_condition_buffer_usage *condition;
+
+       condition = zmalloc(sizeof(struct lttng_condition_buffer_usage));
+       if (!condition) {
+               return NULL;
+       }
+
+       lttng_condition_init(&condition->parent, type);
+       condition->parent.validate = lttng_condition_buffer_usage_validate;
+       condition->parent.serialize = lttng_condition_buffer_usage_serialize;
+       condition->parent.equal = lttng_condition_buffer_usage_is_equal;
+       condition->parent.destroy = lttng_condition_buffer_usage_destroy;
+       return &condition->parent;
+}
+
+struct lttng_condition *lttng_condition_buffer_usage_low_create(void)
+{
+       return lttng_condition_buffer_usage_create(
+                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW);
+}
+
+struct lttng_condition *lttng_condition_buffer_usage_high_create(void)
+{
+       return lttng_condition_buffer_usage_create(
+                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH);
+}
+
+static
+ssize_t init_condition_from_buffer(struct lttng_condition *condition,
+               const struct lttng_buffer_view *src_view)
+{
+       ssize_t ret, condition_size;
+       enum lttng_condition_status status;
+       enum lttng_domain_type domain_type;
+       const struct lttng_condition_buffer_usage_comm *condition_comm;
+       const char *session_name, *channel_name;
+       struct lttng_buffer_view names_view;
+
+       if (src_view->size < sizeof(*condition_comm)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm = (const struct lttng_condition_buffer_usage_comm *) src_view->data;
+       names_view = lttng_buffer_view_from_view(src_view,
+                       sizeof(*condition_comm), -1);
+
+       if (condition_comm->session_name_len > LTTNG_NAME_MAX ||
+                       condition_comm->channel_name_len > LTTNG_NAME_MAX) {
+               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
+               ret = -1;
+               goto end;
+       }
+
+       if (names_view.size <
+                       (condition_comm->session_name_len +
+                       condition_comm->channel_name_len)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
+               ret = -1;
+               goto end;
+       }
+
+       if (condition_comm->threshold_set_in_bytes) {
+               status = lttng_condition_buffer_usage_set_threshold(condition,
+                               condition_comm->threshold);
+       } else {
+               status = lttng_condition_buffer_usage_set_threshold_ratio(
+                               condition,
+                               fixed_to_double(condition_comm->threshold));
+       }
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to initialize buffer usage condition threshold");
+               ret = -1;
+               goto end;
+       }
+
+       if (condition_comm->domain_type <= LTTNG_DOMAIN_NONE ||
+                       condition_comm->domain_type > LTTNG_DOMAIN_PYTHON) {
+               /* Invalid domain value. */
+               ERR("Invalid domain type value (%i) found in condition buffer",
+                               (int) condition_comm->domain_type);
+               ret = -1;
+               goto end;
+       }
+
+       domain_type = (enum lttng_domain_type) condition_comm->domain_type;
+       status = lttng_condition_buffer_usage_set_domain_type(condition,
+                       domain_type);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set buffer usage condition domain");
+               ret = -1;
+               goto end;
+       }
+
+       session_name = names_view.data;
+       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
+               ERR("Malformed session name encountered in condition buffer");
+               ret = -1;
+               goto end;
+       }
+
+       channel_name = session_name + condition_comm->session_name_len;
+       if (*(channel_name + condition_comm->channel_name_len - 1) != '\0') {
+               ERR("Malformed channel name encountered in condition buffer");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_buffer_usage_set_session_name(condition,
+                       session_name);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set buffer usage session name");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_buffer_usage_set_channel_name(condition,
+                       channel_name);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set buffer usage channel name");
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_condition_validate(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       condition_size = sizeof(*condition_comm) +
+                       (ssize_t) condition_comm->session_name_len +
+                       (ssize_t) condition_comm->channel_name_len;
+       ret = condition_size;
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_buffer_usage_low_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_condition **_condition)
+{
+       ssize_t ret;
+       struct lttng_condition *condition =
+                       lttng_condition_buffer_usage_low_create();
+
+       if (!_condition || !condition) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = init_condition_from_buffer(condition, view);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_condition = condition;
+       return ret;
+error:
+       lttng_condition_destroy(condition);
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_buffer_usage_high_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_condition **_condition)
+{
+       ssize_t ret;
+       struct lttng_condition *condition =
+                       lttng_condition_buffer_usage_high_create();
+
+       if (!_condition || !condition) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = init_condition_from_buffer(condition, view);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_condition = condition;
+       return ret;
+error:
+       lttng_condition_destroy(condition);
+       return ret;
+}
+
+static
+struct lttng_evaluation *create_evaluation_from_buffer(
+               enum lttng_condition_type type,
+               const struct lttng_buffer_view *view)
+{
+       const struct lttng_evaluation_buffer_usage_comm *comm =
+                       (const struct lttng_evaluation_buffer_usage_comm *) view->data;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (view->size < sizeof(*comm)) {
+               goto end;
+       }
+
+       evaluation = lttng_evaluation_buffer_usage_create(type,
+                       comm->buffer_use, comm->buffer_capacity);
+end:
+       return evaluation;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_buffer_usage_low_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       evaluation = create_evaluation_from_buffer(
+                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, view);
+       if (!evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       *_evaluation = evaluation;
+       ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
+       return ret;
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_buffer_usage_high_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       evaluation = create_evaluation_from_buffer(
+                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, view);
+       if (!evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       *_evaluation = evaluation;
+       ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
+       return ret;
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_threshold_ratio(
+               const struct lttng_condition *condition,
+               double *threshold_ratio)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) ||
+                       !threshold_ratio) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->threshold_ratio.set) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *threshold_ratio = usage->threshold_ratio.value;
+end:
+       return status;
+}
+
+/* threshold_ratio expressed as [0.0, 1.0]. */
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_threshold_ratio(
+               struct lttng_condition *condition, double threshold_ratio)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) ||
+                       threshold_ratio < 0.0 ||
+                       threshold_ratio > 1.0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       usage->threshold_ratio.set = true;
+       usage->threshold_bytes.set = false;
+       usage->threshold_ratio.value = threshold_ratio;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_threshold(
+               const struct lttng_condition *condition,
+               uint64_t *threshold_bytes)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !threshold_bytes) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->threshold_bytes.set) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *threshold_bytes = usage->threshold_bytes.value;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_threshold(
+               struct lttng_condition *condition, uint64_t threshold_bytes)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition)) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       usage->threshold_ratio.set = false;
+       usage->threshold_bytes.set = true;
+       usage->threshold_bytes.value = threshold_bytes;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_session_name(
+               const struct lttng_condition *condition,
+               const char **session_name)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !session_name) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->session_name) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *session_name = usage->session_name;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_session_name(
+               struct lttng_condition *condition, const char *session_name)
+{
+       char *session_name_copy;
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !session_name ||
+                       strlen(session_name) == 0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       session_name_copy = strdup(session_name);
+       if (!session_name_copy) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       if (usage->session_name) {
+               free(usage->session_name);
+       }
+       usage->session_name = session_name_copy;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_channel_name(
+               const struct lttng_condition *condition,
+               const char **channel_name)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->channel_name) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *channel_name = usage->channel_name;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_channel_name(
+               struct lttng_condition *condition, const char *channel_name)
+{
+       char *channel_name_copy;
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name ||
+                       strlen(channel_name) == 0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       channel_name_copy = strdup(channel_name);
+       if (!channel_name_copy) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       if (usage->channel_name) {
+               free(usage->channel_name);
+       }
+       usage->channel_name = channel_name_copy;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_domain_type(
+               const struct lttng_condition *condition,
+               enum lttng_domain_type *type)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !type) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->domain.set) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *type = usage->domain.type;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_domain_type(
+               struct lttng_condition *condition, enum lttng_domain_type type)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) ||
+                       type == LTTNG_DOMAIN_NONE) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       usage->domain.set = true;
+       usage->domain.type = type;
+end:
+       return status;
+}
+
+static
+int lttng_evaluation_buffer_usage_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_dynamic_buffer *buf)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+       struct lttng_evaluation_buffer_usage_comm comm;
+
+       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+                       parent);
+       comm.buffer_use = usage->buffer_use;
+       comm.buffer_capacity = usage->buffer_capacity;
+
+       return lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+}
+
+static
+void lttng_evaluation_buffer_usage_destroy(
+               struct lttng_evaluation *evaluation)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+
+       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+                       parent);
+       free(usage);
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_buffer_usage_create(
+               enum lttng_condition_type type, uint64_t use, uint64_t capacity)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+
+       usage = zmalloc(sizeof(struct lttng_evaluation_buffer_usage));
+       if (!usage) {
+               goto end;
+       }
+
+       usage->parent.type = type;
+       usage->buffer_use = use;
+       usage->buffer_capacity = capacity;
+       usage->parent.serialize = lttng_evaluation_buffer_usage_serialize;
+       usage->parent.destroy = lttng_evaluation_buffer_usage_destroy;
+end:
+       return &usage->parent;
+}
+
+/*
+ * Get the sampled buffer usage which caused the associated condition to
+ * evaluate to "true".
+ */
+enum lttng_evaluation_status
+lttng_evaluation_buffer_usage_get_usage_ratio(
+               const struct lttng_evaluation *evaluation, double *usage_ratio)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !is_usage_evaluation(evaluation) || !usage_ratio) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+                       parent);
+       *usage_ratio = (double) usage->buffer_use /
+                       (double) usage->buffer_capacity;
+end:
+       return status;
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_buffer_usage_get_usage(
+               const struct lttng_evaluation *evaluation,
+               uint64_t *usage_bytes)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !is_usage_evaluation(evaluation) || !usage_bytes) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+                       parent);
+       *usage_bytes = usage->buffer_use;
+end:
+       return status;
+}
diff --git a/src/common/conditions/condition.c b/src/common/conditions/condition.c
new file mode 100644 (file)
index 0000000..b5e9aeb
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/buffer-usage-internal.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/condition/session-consumed-size-internal.h>
+#include <lttng/condition/session-rotation-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+#include <stdbool.h>
+#include <assert.h>
+
+enum lttng_condition_type lttng_condition_get_type(
+               const struct lttng_condition *condition)
+{
+       return condition ? condition->type : LTTNG_CONDITION_TYPE_UNKNOWN;
+}
+
+void lttng_condition_destroy(struct lttng_condition *condition)
+{
+       if (!condition) {
+               return;
+       }
+
+       assert(condition->destroy);
+       condition->destroy(condition);
+}
+
+LTTNG_HIDDEN
+bool lttng_condition_validate(const struct lttng_condition *condition)
+{
+       bool valid;
+
+       if (!condition) {
+               valid = false;
+               goto end;
+       }
+
+       if (!condition->validate) {
+               /* Sub-class guarantees that it can never be invalid. */
+               valid = true;
+               goto end;
+       }
+
+       valid = condition->validate(condition);
+end:
+       return valid;
+}
+
+LTTNG_HIDDEN
+int lttng_condition_serialize(const struct lttng_condition *condition,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       struct lttng_condition_comm condition_comm = { 0 };
+
+       if (!condition) {
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm.condition_type = (int8_t) condition->type;
+
+       ret = lttng_dynamic_buffer_append(buf, &condition_comm,
+                       sizeof(condition_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = condition->serialize(condition, buf, fd_to_send);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+bool lttng_condition_is_equal(const struct lttng_condition *a,
+               const struct lttng_condition *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       is_equal = a->equal ? a->equal(a, b) : true;
+end:
+       return is_equal;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_create_from_buffer(
+               const struct lttng_buffer_view *buffer,
+               struct lttng_condition **condition)
+{
+       ssize_t ret, condition_size = 0;
+       const struct lttng_condition_comm *condition_comm;
+       condition_create_from_buffer_cb create_from_buffer = NULL;
+
+       if (!buffer || !condition) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Deserializing condition from buffer");
+       condition_comm = (const struct lttng_condition_comm *) buffer->data;
+       condition_size += sizeof(*condition_comm);
+
+       switch ((enum lttng_condition_type) condition_comm->condition_type) {
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+               create_from_buffer = lttng_condition_buffer_usage_low_create_from_buffer;
+               break;
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+               create_from_buffer = lttng_condition_buffer_usage_high_create_from_buffer;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+               create_from_buffer = lttng_condition_session_consumed_size_create_from_buffer;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               create_from_buffer = lttng_condition_session_rotation_ongoing_create_from_buffer;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               create_from_buffer = lttng_condition_session_rotation_completed_create_from_buffer;
+               break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               create_from_buffer = lttng_condition_event_rule_create_from_buffer;
+               break;
+       default:
+               ERR("Attempted to create condition of unknown type (%i)",
+                               (int) condition_comm->condition_type);
+               ret = -1;
+               goto end;
+       }
+
+       if (create_from_buffer) {
+               const struct lttng_buffer_view view =
+                               lttng_buffer_view_from_view(buffer,
+                                       sizeof(*condition_comm), -1);
+
+               ret = create_from_buffer(&view, condition);
+               if (ret < 0) {
+                       goto end;
+               }
+               condition_size += ret;
+
+       } else {
+               abort();
+       }
+
+       ret = condition_size;
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+void lttng_condition_init(struct lttng_condition *condition,
+               enum lttng_condition_type type)
+{
+       condition->type = type;
+}
+
+LTTNG_HIDDEN
+const char *lttng_condition_type_str(enum lttng_condition_type type)
+{
+       switch (type) {
+       case LTTNG_CONDITION_TYPE_UNKNOWN:
+               return "unknown";
+
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+               return "session consumed size";
+
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+               return "buffer usage high";
+
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+               return "buffer usage low";
+
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               return "session rotation ongoing";
+
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               return "session rotation completed";
+
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               return "event rule hit";
+
+       default:
+               return "???";
+       }
+}
diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/event-rule.c
new file mode 100644 (file)
index 0000000..aa1f136
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2017 - Jérémie Galarneau <jeremie.galarneau@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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#define IS_EVENT_RULE_CONDITION(condition) ( \
+       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT \
+       )
+
+static
+bool is_event_rule_evaluation(const struct lttng_evaluation *evaluation)
+{
+       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
+
+       return type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT;
+}
+
+
+static
+bool lttng_condition_event_rule_validate(
+               const struct lttng_condition *condition);
+static
+int lttng_condition_event_rule_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send);
+static
+bool lttng_condition_event_rule_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b);
+static
+void lttng_condition_event_rule_destroy(
+               struct lttng_condition *condition);
+
+
+static
+bool lttng_condition_event_rule_validate(
+               const struct lttng_condition *condition)
+{
+       bool valid = false;
+       struct lttng_condition_event_rule *event_rule;
+
+       if (!condition) {
+               goto end;
+       }
+
+       event_rule = container_of(condition,
+                       struct lttng_condition_event_rule, parent);
+       if (!event_rule->rule) {
+               ERR("Invalid session event_rule condition: a rule must be set.");
+               goto end;
+       }
+
+       valid = lttng_event_rule_validate(event_rule->rule);
+end:
+       return valid;
+}
+
+static
+int lttng_condition_event_rule_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       size_t header_offset, size_before_payload;
+       struct lttng_condition_event_rule *event_rule;
+       struct lttng_condition_event_rule_comm event_rule_comm = { 0 };
+       struct lttng_condition_event_rule_comm *header;
+
+       if (!condition || !IS_EVENT_RULE_CONDITION(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing event rule condition");
+       event_rule = container_of(condition, struct lttng_condition_event_rule,
+                       parent);
+
+       header_offset = buf->size;
+       ret = lttng_dynamic_buffer_append(buf, &event_rule_comm,
+                       sizeof(event_rule_comm));
+       if (ret) {
+               goto end;
+       }
+
+       size_before_payload = buf->size;
+       ret = lttng_event_rule_serialize(event_rule->rule, buf, fd_to_send);
+       if (ret) {
+               goto end;
+       }
+
+       /* Update payload size */
+       header = (struct lttng_condition_event_rule_comm *) ((char *) buf->data + header_offset);
+       header->length = buf->size - size_before_payload;
+
+end:
+       return ret;
+}
+
+static
+bool lttng_condition_event_rule_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b)
+{
+       bool is_equal = false;
+       struct lttng_condition_event_rule *a, *b;
+
+       a = container_of(_a, struct lttng_condition_event_rule, parent);
+       b = container_of(_b, struct lttng_condition_event_rule, parent);
+
+       /* Both session names must be set or both must be unset. */
+       if ((a->rule && !b->rule) ||
+                       (!a->rule && b->rule)) {
+               WARN("Comparing session event_rule conditions with uninitialized rule.");
+               goto end;
+       }
+
+       is_equal = lttng_event_rule_is_equal(a->rule, b->rule);
+end:
+       return is_equal;
+}
+
+static
+void lttng_condition_event_rule_destroy(
+               struct lttng_condition *condition)
+{
+       struct lttng_condition_event_rule *event_rule;
+
+       event_rule = container_of(condition,
+                       struct lttng_condition_event_rule, parent);
+
+
+       lttng_event_rule_destroy(event_rule->rule);
+       free(event_rule);
+}
+
+struct lttng_condition *lttng_condition_event_rule_create(
+               struct lttng_event_rule *rule)
+{
+       struct lttng_condition *parent = NULL;
+       struct lttng_condition_event_rule *condition = NULL;
+
+       if (!rule) {
+               goto end;
+       }
+
+       condition = zmalloc(sizeof(struct lttng_condition_event_rule));
+       if (!condition) {
+               return NULL;
+       }
+
+       lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+       condition->parent.validate = lttng_condition_event_rule_validate,
+       condition->parent.serialize = lttng_condition_event_rule_serialize,
+       condition->parent.equal = lttng_condition_event_rule_is_equal,
+       condition->parent.destroy = lttng_condition_event_rule_destroy,
+
+       condition->rule = rule;
+       parent = &condition->parent;
+end:
+       return parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_event_rule_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_condition **_condition)
+{
+       ssize_t offset, event_rule_size;
+       const struct lttng_condition_event_rule_comm *comm;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+       struct lttng_buffer_view event_rule_view;
+
+       if (!view || !_condition) {
+               goto error;
+       }
+
+       if (view->size < sizeof(*comm)) {
+               ERR("Failed to initialize from malformed event rule condition: buffer too short to contain header");
+               goto error;
+       }
+
+       comm = (const struct lttng_condition_event_rule_comm *) view->data;
+       offset = sizeof(*comm);
+
+       /* Struct lttng_event_rule */
+       event_rule_view = lttng_buffer_view_from_view(view, offset, -1);
+       event_rule_size = lttng_event_rule_create_from_buffer(&event_rule_view, &event_rule);
+       if (event_rule_size < 0 || !event_rule) {
+               goto error;
+       }
+
+       if ((size_t) comm->length != event_rule_size) {
+               goto error;
+       }
+       
+       /* Move to the end */
+       offset += comm->length;
+
+       condition = lttng_condition_event_rule_create(event_rule);
+       if (!condition) {
+               goto error;
+       }
+
+       /* Ownership passed on condition event rule create */
+       event_rule = NULL;
+
+       *_condition = condition;
+       condition = NULL;
+       goto end;
+
+error:
+       offset = -1;
+
+end:
+       lttng_event_rule_destroy(event_rule);
+       lttng_condition_destroy(condition);
+       return offset;
+}
+
+LTTNG_HIDDEN
+enum lttng_condition_status
+lttng_condition_event_rule_get_rule_no_const(
+               const struct lttng_condition *condition,
+               struct lttng_event_rule **rule)
+{
+       struct lttng_condition_event_rule *event_rule;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !rule) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       event_rule = container_of(condition, struct lttng_condition_event_rule,
+                       parent);
+       if (!event_rule->rule) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *rule = event_rule->rule;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_event_rule_get_rule(
+               const struct lttng_condition *condition,
+               const struct lttng_event_rule **rule)
+{
+       struct lttng_event_rule *no_const_rule = NULL;
+       enum lttng_condition_status status;
+
+       status = lttng_condition_event_rule_get_rule_no_const(condition, &no_const_rule);
+       *rule = no_const_rule;
+       return status;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_event_rule_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret, offset = 0;
+       const char *name;
+       struct lttng_evaluation *evaluation = NULL;
+       const struct lttng_evaluation_event_rule_comm *comm = 
+               (const struct lttng_evaluation_event_rule_comm *) view->data;
+       struct lttng_buffer_view current_view;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       if (view->size < sizeof(*comm)) {
+               ret = -1;
+               goto error;
+       }
+
+       /* Map the name, view of the payload */
+       offset += sizeof(*comm);
+       current_view = lttng_buffer_view_from_view(view, offset, comm->trigger_name_length);
+       name = current_view.data;
+       if (!name) {
+               ret = -1;
+               goto error;
+       }
+
+       if (comm->trigger_name_length == 1 ||
+                       name[comm->trigger_name_length - 1] != '\0' ||
+                       strlen(name) != comm->trigger_name_length - 1) {
+               /*
+                * Check that the name is not NULL, is NULL-terminated, and
+                * does not contain a NULL before the last byte.
+                */
+               ret = -1;
+               goto error;
+       }
+
+       offset += comm->trigger_name_length;
+
+       evaluation = lttng_evaluation_event_rule_create(name);
+       if (!evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       *_evaluation = evaluation;
+       evaluation = NULL;
+       ret = offset;
+
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+static
+int lttng_evaluation_event_rule_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_dynamic_buffer *buf)
+{
+       int ret = 0;
+       struct lttng_evaluation_event_rule *hit;
+       struct lttng_evaluation_event_rule_comm comm;
+
+       hit = container_of(evaluation, struct lttng_evaluation_event_rule,
+                       parent);
+       comm.trigger_name_length = strlen(hit->name) + 1;
+       ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, hit->name, comm.trigger_name_length);
+end:
+       return ret;
+}
+
+static
+void lttng_evaluation_event_rule_destroy(
+               struct lttng_evaluation *evaluation)
+{
+       struct lttng_evaluation_event_rule *hit;
+
+       hit = container_of(evaluation, struct lttng_evaluation_event_rule,
+                       parent);
+       free(hit->name);
+       free(hit);
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_event_rule_create(const char *trigger_name)
+{
+       struct lttng_evaluation_event_rule *hit;
+
+       hit = zmalloc(sizeof(struct lttng_evaluation_event_rule));
+       if (!hit) {
+               goto end;
+       }
+
+       /* TODO errir handling */
+       hit->name = strdup(trigger_name);
+
+       hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_HIT;
+       hit->parent.serialize = lttng_evaluation_event_rule_serialize;
+       hit->parent.destroy = lttng_evaluation_event_rule_destroy;
+end:
+       return &hit->parent;
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_event_rule_get_trigger_name(
+               const struct lttng_evaluation *evaluation,
+               const char **name)
+{
+       struct lttng_evaluation_event_rule *hit;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !is_event_rule_evaluation(evaluation) || !name) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       hit = container_of(evaluation, struct lttng_evaluation_event_rule,
+                       parent);
+       *name = hit->name;
+end:
+       return status;
+}
diff --git a/src/common/conditions/session-consumed-size.c b/src/common/conditions/session-consumed-size.c
new file mode 100644 (file)
index 0000000..6e0fcb4
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/session-consumed-size-internal.h>
+#include <lttng/constant.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include <time.h>
+
+#define IS_CONSUMED_SIZE_CONDITION(condition) ( \
+       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
+       )
+
+#define IS_CONSUMED_SIZE_EVALUATION(evaluation) ( \
+       lttng_evaluation_get_type(evaluation) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
+       )
+
+static
+void lttng_condition_session_consumed_size_destroy(struct lttng_condition *condition)
+{
+       struct lttng_condition_session_consumed_size *consumed_size;
+
+       consumed_size = container_of(condition,
+                       struct lttng_condition_session_consumed_size, parent);
+
+       free(consumed_size->session_name);
+       free(consumed_size);
+}
+
+static
+bool lttng_condition_session_consumed_size_validate(
+               const struct lttng_condition *condition)
+{
+       bool valid = false;
+       struct lttng_condition_session_consumed_size *consumed;
+
+       if (!condition) {
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       if (!consumed->session_name) {
+               ERR("Invalid session consumed size condition: a target session name must be set.");
+               goto end;
+       }
+       if (!consumed->consumed_threshold_bytes.set) {
+               ERR("Invalid session consumed size condition: a threshold must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_condition_session_consumed_size_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       size_t session_name_len;
+       struct lttng_condition_session_consumed_size *consumed;
+       struct lttng_condition_session_consumed_size_comm consumed_comm;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing session consumed size condition");
+       consumed = container_of(condition,
+                       struct lttng_condition_session_consumed_size,
+                       parent);
+
+       session_name_len = strlen(consumed->session_name) + 1;
+       if (session_name_len > LTTNG_NAME_MAX) {
+               ret = -1;
+               goto end;
+       }
+
+       consumed_comm.consumed_threshold_bytes =
+                       consumed->consumed_threshold_bytes.value;
+       consumed_comm.session_name_len = (uint32_t) session_name_len;
+
+       ret = lttng_dynamic_buffer_append(buf, &consumed_comm,
+                       sizeof(consumed_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, consumed->session_name,
+                       session_name_len);
+       if (ret) {
+               goto end;
+       }
+
+       if (fd_to_send) {
+               /* No fd to send */
+               *fd_to_send = -1;
+       }
+end:
+       return ret;
+}
+
+static
+bool lttng_condition_session_consumed_size_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b)
+{
+       bool is_equal = false;
+       struct lttng_condition_session_consumed_size *a, *b;
+
+       a = container_of(_a, struct lttng_condition_session_consumed_size, parent);
+       b = container_of(_b, struct lttng_condition_session_consumed_size, parent);
+
+       if (a->consumed_threshold_bytes.set && b->consumed_threshold_bytes.set) {
+               uint64_t a_value, b_value;
+
+               a_value = a->consumed_threshold_bytes.value;
+               b_value = b->consumed_threshold_bytes.value;
+               if (a_value != b_value) {
+                       goto end;
+               }
+       }
+
+       assert(a->session_name);
+       assert(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+struct lttng_condition *lttng_condition_session_consumed_size_create(void)
+{
+       struct lttng_condition_session_consumed_size *condition;
+
+       condition = zmalloc(sizeof(struct lttng_condition_session_consumed_size));
+       if (!condition) {
+               return NULL;
+       }
+
+       lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE);
+       condition->parent.validate = lttng_condition_session_consumed_size_validate;
+       condition->parent.serialize = lttng_condition_session_consumed_size_serialize;
+       condition->parent.equal = lttng_condition_session_consumed_size_is_equal;
+       condition->parent.destroy = lttng_condition_session_consumed_size_destroy;
+       return &condition->parent;
+}
+
+static
+ssize_t init_condition_from_buffer(struct lttng_condition *condition,
+               const struct lttng_buffer_view *src_view)
+{
+       ssize_t ret, condition_size;
+       enum lttng_condition_status status;
+       const struct lttng_condition_session_consumed_size_comm *condition_comm;
+       const char *session_name;
+       struct lttng_buffer_view names_view;
+
+       if (src_view->size < sizeof(*condition_comm)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm = (const struct lttng_condition_session_consumed_size_comm *) src_view->data;
+       names_view = lttng_buffer_view_from_view(src_view,
+                       sizeof(*condition_comm), -1);
+
+       if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
+               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
+               ret = -1;
+               goto end;
+       }
+
+       if (names_view.size < condition_comm->session_name_len) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_session_consumed_size_set_threshold(condition,
+                       condition_comm->consumed_threshold_bytes);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to initialize session consumed size condition threshold");
+               ret = -1;
+               goto end;
+       }
+
+       session_name = names_view.data;
+       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
+               ERR("Malformed session name encountered in condition buffer");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_session_consumed_size_set_session_name(condition,
+                       session_name);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set session consumed size condition's session name");
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_condition_validate(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       condition_size = sizeof(*condition_comm) +
+                       (ssize_t) condition_comm->session_name_len;
+       ret = condition_size;
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_session_consumed_size_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_condition **_condition)
+{
+       ssize_t ret;
+       struct lttng_condition *condition =
+                       lttng_condition_session_consumed_size_create();
+
+       if (!_condition || !condition) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = init_condition_from_buffer(condition, view);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_condition = condition;
+       return ret;
+error:
+       lttng_condition_destroy(condition);
+       return ret;
+}
+
+static
+struct lttng_evaluation *create_evaluation_from_buffer(
+               const struct lttng_buffer_view *view)
+{
+       const struct lttng_evaluation_session_consumed_size_comm *comm =
+                       (const struct lttng_evaluation_session_consumed_size_comm *) view->data;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (view->size < sizeof(*comm)) {
+               goto end;
+       }
+
+       evaluation = lttng_evaluation_session_consumed_size_create(
+                       comm->session_consumed);
+end:
+       return evaluation;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_session_consumed_size_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       evaluation = create_evaluation_from_buffer(view);
+       if (!evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       *_evaluation = evaluation;
+       ret = sizeof(struct lttng_evaluation_session_consumed_size_comm);
+       return ret;
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_get_threshold(
+               const struct lttng_condition *condition,
+               uint64_t *consumed_threshold_bytes)
+{
+       struct lttng_condition_session_consumed_size *consumed;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !consumed_threshold_bytes) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       if (!consumed->consumed_threshold_bytes.set) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *consumed_threshold_bytes = consumed->consumed_threshold_bytes.value;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_set_threshold(
+               struct lttng_condition *condition, uint64_t consumed_threshold_bytes)
+{
+       struct lttng_condition_session_consumed_size *consumed;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       consumed->consumed_threshold_bytes.set = true;
+       consumed->consumed_threshold_bytes.value = consumed_threshold_bytes;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_get_session_name(
+               const struct lttng_condition *condition,
+               const char **session_name)
+{
+       struct lttng_condition_session_consumed_size *consumed;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !session_name) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       if (!consumed->session_name) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *session_name = consumed->session_name;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_set_session_name(
+               struct lttng_condition *condition, const char *session_name)
+{
+       char *session_name_copy;
+       struct lttng_condition_session_consumed_size *consumed;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) ||
+                       !session_name || strlen(session_name) == 0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       session_name_copy = strdup(session_name);
+       if (!session_name_copy) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       if (consumed->session_name) {
+               free(consumed->session_name);
+       }
+       consumed->session_name = session_name_copy;
+end:
+       return status;
+}
+
+static
+int lttng_evaluation_session_consumed_size_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_dynamic_buffer *buf)
+{
+       struct lttng_evaluation_session_consumed_size *consumed;
+       struct lttng_evaluation_session_consumed_size_comm comm;
+
+       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
+                       parent);
+       comm.session_consumed = consumed->session_consumed;
+       return lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+}
+
+static
+void lttng_evaluation_session_consumed_size_destroy(
+               struct lttng_evaluation *evaluation)
+{
+       struct lttng_evaluation_session_consumed_size *consumed;
+
+       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
+                       parent);
+       free(consumed);
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_session_consumed_size_create(
+               uint64_t consumed)
+{
+       struct lttng_evaluation_session_consumed_size *consumed_eval;
+
+       consumed_eval = zmalloc(sizeof(struct lttng_evaluation_session_consumed_size));
+       if (!consumed_eval) {
+               goto end;
+       }
+
+       consumed_eval->parent.type = LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE;
+       consumed_eval->session_consumed = consumed;
+       consumed_eval->parent.serialize = lttng_evaluation_session_consumed_size_serialize;
+       consumed_eval->parent.destroy = lttng_evaluation_session_consumed_size_destroy;
+end:
+       return &consumed_eval->parent;
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_session_consumed_size_get_consumed_size(
+               const struct lttng_evaluation *evaluation,
+               uint64_t *session_consumed)
+{
+       struct lttng_evaluation_session_consumed_size *consumed;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !IS_CONSUMED_SIZE_EVALUATION(evaluation) ||
+                       !session_consumed) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
+                       parent);
+       *session_consumed = consumed->session_consumed;
+end:
+       return status;
+}
diff --git a/src/common/conditions/session-rotation.c b/src/common/conditions/session-rotation.c
new file mode 100644 (file)
index 0000000..1c4124f
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/session-rotation-internal.h>
+#include <lttng/location-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <assert.h>
+#include <stdbool.h>
+
+static
+bool lttng_condition_session_rotation_validate(
+               const struct lttng_condition *condition);
+static
+int lttng_condition_session_rotation_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send);
+static
+bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b);
+static
+void lttng_condition_session_rotation_destroy(
+               struct lttng_condition *condition);
+
+static const
+struct lttng_condition rotation_condition_template = {
+       /* .type omitted; shall be set on creation. */
+       .validate = lttng_condition_session_rotation_validate,
+       .serialize = lttng_condition_session_rotation_serialize,
+       .equal = lttng_condition_session_rotation_is_equal,
+       .destroy = lttng_condition_session_rotation_destroy,
+};
+
+static
+int lttng_evaluation_session_rotation_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_dynamic_buffer *buf);
+static
+void lttng_evaluation_session_rotation_destroy(
+               struct lttng_evaluation *evaluation);
+
+static const
+struct lttng_evaluation rotation_evaluation_template = {
+       /* .type omitted; shall be set on creation. */
+       .serialize = lttng_evaluation_session_rotation_serialize,
+       .destroy = lttng_evaluation_session_rotation_destroy,
+};
+
+static
+bool is_rotation_condition(const struct lttng_condition *condition)
+{
+       enum lttng_condition_type type = lttng_condition_get_type(condition);
+
+       return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
+                       type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
+}
+
+static
+bool is_rotation_evaluation(const struct lttng_evaluation *evaluation)
+{
+       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
+
+       return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
+                       type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
+}
+
+static
+bool lttng_condition_session_rotation_validate(
+               const struct lttng_condition *condition)
+{
+       bool valid = false;
+       struct lttng_condition_session_rotation *rotation;
+
+       if (!condition) {
+               goto end;
+       }
+
+       rotation = container_of(condition,
+                       struct lttng_condition_session_rotation, parent);
+       if (!rotation->session_name) {
+               ERR("Invalid session rotation condition: a target session name must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_condition_session_rotation_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       size_t session_name_len;
+       struct lttng_condition_session_rotation *rotation;
+       struct lttng_condition_session_rotation_comm rotation_comm;
+
+       if (!condition || !is_rotation_condition(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing session rotation condition");
+       rotation = container_of(condition, struct lttng_condition_session_rotation,
+                       parent);
+
+       session_name_len = strlen(rotation->session_name) + 1;
+       if (session_name_len > LTTNG_NAME_MAX) {
+               ret = -1;
+               goto end;
+       }
+
+       rotation_comm.session_name_len = session_name_len;
+       ret = lttng_dynamic_buffer_append(buf, &rotation_comm,
+                       sizeof(rotation_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, rotation->session_name,
+                       session_name_len);
+       if (ret) {
+               goto end;
+       }
+
+       if (fd_to_send) {
+               /* No fd to send */
+               *fd_to_send = -1;
+       }
+end:
+       return ret;
+}
+
+static
+bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b)
+{
+       bool is_equal = false;
+       struct lttng_condition_session_rotation *a, *b;
+
+       a = container_of(_a, struct lttng_condition_session_rotation, parent);
+       b = container_of(_b, struct lttng_condition_session_rotation, parent);
+
+       /* Both session names must be set or both must be unset. */
+       if ((a->session_name && !b->session_name) ||
+                       (!a->session_name && b->session_name)) {
+               WARN("Comparing session rotation conditions with uninitialized session names.");
+               goto end;
+       }
+
+       if (a->session_name && b->session_name &&
+                       strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static
+void lttng_condition_session_rotation_destroy(
+               struct lttng_condition *condition)
+{
+       struct lttng_condition_session_rotation *rotation;
+
+       rotation = container_of(condition,
+                       struct lttng_condition_session_rotation, parent);
+
+       free(rotation->session_name);
+       free(rotation);
+}
+
+static
+struct lttng_condition *lttng_condition_session_rotation_create(
+               enum lttng_condition_type type)
+{
+       struct lttng_condition_session_rotation *condition;
+
+       condition = zmalloc(sizeof(struct lttng_condition_session_rotation));
+       if (!condition) {
+               return NULL;
+       }
+
+       memcpy(&condition->parent, &rotation_condition_template,
+                       sizeof(condition->parent));
+       lttng_condition_init(&condition->parent, type);
+       return &condition->parent;
+}
+
+struct lttng_condition *lttng_condition_session_rotation_ongoing_create(void)
+{
+       return lttng_condition_session_rotation_create(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
+}
+
+struct lttng_condition *lttng_condition_session_rotation_completed_create(void)
+{
+       return lttng_condition_session_rotation_create(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
+}
+
+static
+ssize_t init_condition_from_buffer(struct lttng_condition *condition,
+               const struct lttng_buffer_view *src_view)
+{
+       ssize_t ret, condition_size;
+       enum lttng_condition_status status;
+       const struct lttng_condition_session_rotation_comm *condition_comm;
+       const char *session_name;
+       struct lttng_buffer_view name_view;
+
+       if (src_view->size < sizeof(*condition_comm)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm = (const struct lttng_condition_session_rotation_comm *) src_view->data;
+       name_view = lttng_buffer_view_from_view(src_view,
+                       sizeof(*condition_comm), -1);
+
+       if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
+               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
+               ret = -1;
+               goto end;
+       }
+
+       if (name_view.size < condition_comm->session_name_len) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain session name");
+               ret = -1;
+               goto end;
+       }
+
+       session_name = name_view.data;
+       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
+               ERR("Malformed session name encountered in condition buffer");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_session_rotation_set_session_name(condition,
+                       session_name);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set buffer consumed session name");
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_condition_validate(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       condition_size = sizeof(*condition_comm) +
+                       (ssize_t) condition_comm->session_name_len;
+       ret = condition_size;
+end:
+       return ret;
+}
+
+static
+ssize_t lttng_condition_session_rotation_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_condition **_condition,
+               enum lttng_condition_type type)
+{
+       ssize_t ret;
+       struct lttng_condition *condition = NULL;
+
+       switch (type) {
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               condition = lttng_condition_session_rotation_ongoing_create();
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               condition = lttng_condition_session_rotation_completed_create();
+               break;
+       default:
+               ret = -1;
+               goto error;
+       }
+
+       if (!_condition || !condition) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = init_condition_from_buffer(condition, view);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_condition = condition;
+       return ret;
+error:
+       lttng_condition_destroy(condition);
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_session_rotation_ongoing_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_condition **condition)
+{
+       return lttng_condition_session_rotation_create_from_buffer(view,
+                       condition,
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_session_rotation_completed_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_condition **condition)
+{
+       return lttng_condition_session_rotation_create_from_buffer(view,
+                       condition,
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
+}
+
+static
+struct lttng_evaluation *lttng_evaluation_session_rotation_create(
+               enum lttng_condition_type type, uint64_t id,
+               struct lttng_trace_archive_location *location)
+{
+       struct lttng_evaluation_session_rotation *evaluation;
+
+       evaluation = zmalloc(sizeof(struct lttng_evaluation_session_rotation));
+       if (!evaluation) {
+               return NULL;
+       }
+
+       memcpy(&evaluation->parent, &rotation_evaluation_template,
+                       sizeof(evaluation->parent));
+       lttng_evaluation_init(&evaluation->parent, type);
+       evaluation->id = id;
+       evaluation->location = location;
+       return &evaluation->parent;
+}
+
+static
+ssize_t create_evaluation_from_buffer(
+               enum lttng_condition_type type,
+               const struct lttng_buffer_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret, size;
+       struct lttng_evaluation *evaluation = NULL;
+       struct lttng_trace_archive_location *location = NULL;
+       const struct lttng_evaluation_session_rotation_comm *comm =
+                       (const struct lttng_evaluation_session_rotation_comm *) view->data;
+        struct lttng_buffer_view location_view;
+
+       if (view->size < sizeof(*comm)) {
+               goto error;
+       }
+
+       size = sizeof(*comm);
+       if (comm->has_location) {
+               location_view = lttng_buffer_view_from_view(view, sizeof(*comm),
+                               -1);
+               if (!location_view.data) {
+                       goto error;
+               }
+
+               ret = lttng_trace_archive_location_create_from_buffer(
+                               &location_view, &location);
+               if (ret < 0) {
+                       goto error;
+               }
+               size += ret;
+       }
+
+       evaluation = lttng_evaluation_session_rotation_create(type, comm->id,
+                       location);
+       if (!evaluation) {
+               goto error;
+       }
+
+       ret = size;
+       *_evaluation = evaluation;
+       return ret;
+error:
+       lttng_trace_archive_location_destroy(location);
+       evaluation = NULL;
+       return -1;
+}
+
+static
+ssize_t lttng_evaluation_session_rotation_create_from_buffer(
+               enum lttng_condition_type type,
+               const struct lttng_buffer_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = create_evaluation_from_buffer(type, view, &evaluation);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_evaluation = evaluation;
+       return ret;
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_session_rotation_ongoing_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_evaluation **evaluation)
+{
+       return lttng_evaluation_session_rotation_create_from_buffer(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING,
+                       view, evaluation);
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_session_rotation_completed_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_evaluation **evaluation)
+{
+       return lttng_evaluation_session_rotation_create_from_buffer(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED,
+                       view, evaluation);
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_session_rotation_ongoing_create(
+               uint64_t id)
+{
+       return lttng_evaluation_session_rotation_create(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, id,
+                       NULL);
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_session_rotation_completed_create(
+               uint64_t id, struct lttng_trace_archive_location *location)
+{
+       return lttng_evaluation_session_rotation_create(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, id,
+                       location);
+}
+
+enum lttng_condition_status
+lttng_condition_session_rotation_get_session_name(
+               const struct lttng_condition *condition,
+               const char **session_name)
+{
+       struct lttng_condition_session_rotation *rotation;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !is_rotation_condition(condition) || !session_name) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       rotation = container_of(condition, struct lttng_condition_session_rotation,
+                       parent);
+       if (!rotation->session_name) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *session_name = rotation->session_name;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_rotation_set_session_name(
+               struct lttng_condition *condition, const char *session_name)
+{
+       char *session_name_copy;
+       struct lttng_condition_session_rotation *rotation;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !is_rotation_condition(condition) ||
+                       !session_name || strlen(session_name) == 0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       rotation = container_of(condition,
+                       struct lttng_condition_session_rotation, parent);
+       session_name_copy = strdup(session_name);
+       if (!session_name_copy) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       free(rotation->session_name);
+       rotation->session_name = session_name_copy;
+end:
+       return status;
+}
+
+static
+int lttng_evaluation_session_rotation_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_dynamic_buffer *buf)
+{
+       int ret;
+       struct lttng_evaluation_session_rotation *rotation;
+       struct lttng_evaluation_session_rotation_comm comm = { 0 };
+
+       rotation = container_of(evaluation,
+                       struct lttng_evaluation_session_rotation, parent);
+       comm.id = rotation->id;
+       comm.has_location = !!rotation->location;
+        ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+       if (!rotation->location) {
+               goto end;
+       }
+       ret = lttng_trace_archive_location_serialize(rotation->location,
+                       buf);
+end:
+       return ret;
+}
+
+static
+void lttng_evaluation_session_rotation_destroy(
+               struct lttng_evaluation *evaluation)
+{
+       struct lttng_evaluation_session_rotation *rotation;
+
+       rotation = container_of(evaluation,
+                       struct lttng_evaluation_session_rotation, parent);
+       lttng_trace_archive_location_destroy(rotation->location);
+       free(rotation);
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_session_rotation_get_id(
+               const struct lttng_evaluation *evaluation, uint64_t *id)
+{
+       const struct lttng_evaluation_session_rotation *rotation;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !id || !is_rotation_evaluation(evaluation)) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       rotation = container_of(evaluation,
+                       struct lttng_evaluation_session_rotation, parent);
+       *id = rotation->id;
+end:
+       return status;
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_session_rotation_completed_get_location(
+               const struct lttng_evaluation *evaluation,
+               const struct lttng_trace_archive_location **location)
+{
+       const struct lttng_evaluation_session_rotation *rotation;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !location ||
+                       evaluation->type != LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       rotation = container_of(evaluation,
+                       struct lttng_evaluation_session_rotation, parent);
+       *location = rotation->location;
+end:
+       return status;
+}
diff --git a/src/common/credentials.c b/src/common/credentials.c
new file mode 100644 (file)
index 0000000..c7d621f
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 - Jonathan Rajotte-Julien <jonathan.rajotte-julien@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 <assert.h>
+#include <stdbool.h>
+#include "credentials.h"
+
+bool lttng_credentials_is_equal(const struct lttng_credentials *a,
+               const struct lttng_credentials *b)
+{
+       assert(a);
+       assert(b);
+       if ((a->uid != b->uid) || (a->gid != b->gid)) {
+               return false;
+       }
+       return true;
+};
index a0d93308c42d11ecf64a7a659d6c4ef0d6621278..16b65575b1978cd1d438893269486725547ededa 100644 (file)
@@ -9,10 +9,15 @@
 #define LTTNG_CREDENTIALS_H
 
 #include <sys/types.h>
+#include <assert.h>
+#include <stdbool.h>
 
 struct lttng_credentials {
        uid_t uid;
        gid_t gid;
 };
 
+bool lttng_credentials_is_equal(const struct lttng_credentials *a,
+               const struct lttng_credentials *b);
+
 #endif /* LTTNG_CREDENTIALS_H */
diff --git a/src/common/domain.c b/src/common/domain.c
new file mode 100644 (file)
index 0000000..5d6e7a0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 - EfficiOS, inc.
+ *
+ * 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 "lttng/domain-internal.h"
+#include "common/macros.h"
+
+LTTNG_HIDDEN
+const char *lttng_domain_type_str(enum lttng_domain_type domain_type)
+{
+       switch (domain_type) {
+       case LTTNG_DOMAIN_NONE:
+               return "none";
+
+       case LTTNG_DOMAIN_KERNEL:
+               return "kernel";
+
+       case LTTNG_DOMAIN_UST:
+               return "ust";
+
+       case LTTNG_DOMAIN_JUL:
+               return "jul";
+
+       case LTTNG_DOMAIN_LOG4J:
+               return "log4j";
+
+       case LTTNG_DOMAIN_PYTHON:
+               return "python";
+
+       default:
+               return "???";
+       }
+}
index 208dd1956b41263df7f8336605f22e082bff21ac..2b3ea62731cb3c54a5cc17d2bbe8fd9b068744a5 100644 (file)
@@ -121,6 +121,23 @@ void *lttng_dynamic_pointer_array_get_pointer(
        return *element;
 }
 
+/*
+ * Returns the pointer at index `index`, sets the array slot to NULL. Does not
+ * run the destructor.
+ */
+
+static inline
+void *lttng_dynamic_pointer_array_steal_pointer(
+               struct lttng_dynamic_pointer_array *array, size_t index)
+{
+       void **p_element = lttng_dynamic_array_get_element(&array->array, index);
+       void *element = *p_element;
+
+       *p_element = NULL;
+
+       return element;
+}
+
 /*
  * Add a pointer to the end of a dynamic pointer array. The array's element
  * count is increased by one and its underlying capacity is adjusted
index ed81b8b452e6f871e938d96d8c9d5ac7d61a4501..8983652f9a1351c693ff2f5990231f5207178bd6 100644 (file)
@@ -7,14 +7,15 @@
 
 #define _LGPL_SOURCE
 #include <assert.h>
+#include <errno.h>
 #include <inttypes.h>
+#include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
-#include <errno.h>
 
-#include <lttng/lttng-error.h>
 #include <common/common.h>
 #include <common/compat/getenv.h>
+#include <lttng/lttng-error.h>
 
 #include "error.h"
 
@@ -28,6 +29,7 @@ static int lttng_opt_abort_on_error = -1;
 
 /* TLS variable that contains the time of one single log entry. */
 DEFINE_URCU_TLS(struct log_time, error_log_time);
+DEFINE_URCU_TLS(const char *, logger_thread_name);
 
 LTTNG_HIDDEN
 const char *log_add_time(void)
@@ -66,6 +68,30 @@ error:
        return "";
 }
 
+LTTNG_HIDDEN
+void logger_set_thread_name(const char *name, bool set_pthread_name)
+{
+       int ret;
+
+       assert(name);
+       URCU_TLS(logger_thread_name) = name;
+
+       if (set_pthread_name) {
+               char pthread_name[16];
+
+               /*
+                * Truncations are expected since pthread limits thread names to
+                * a generous 16 characters.
+                */
+               strncpy(pthread_name, name, sizeof(pthread_name));
+               pthread_name[sizeof(pthread_name) - 1] = '\0';
+               ret = pthread_setname_np(pthread_self(), pthread_name);
+               if (ret) {
+                       DBG("Failed to set pthread name attribute");
+               }
+       }
+}
+
 /*
  * Human readable error message.
  */
index 5218869da8ac492ea722a74ccff23e9a3f4975a8..d3a60c5827748829b2a3e84a7ae08db248fd50ac 100644 (file)
@@ -15,6 +15,8 @@
 #include <stdbool.h>
 #include <urcu/tls-compat.h>
 #include <common/compat/time.h>
+#include <common/string-utils/format.h>
+#include <common/macros.h>
 
 #ifndef _GNU_SOURCE
 #error "lttng-tools error.h needs _GNU_SOURCE"
@@ -42,6 +44,7 @@ struct log_time {
        char str[19];
 };
 extern DECLARE_URCU_TLS(struct log_time, error_log_time);
+extern DECLARE_URCU_TLS(const char *, logger_thread_name);
 
 extern int lttng_opt_quiet;
 extern int lttng_opt_verbose;
@@ -139,13 +142,44 @@ static inline void __lttng_print_check_abort(enum lttng_error_level type)
        } while (0)
 
 /* Three level of debug. Use -v, -vv or -vvv for the levels */
-#define _ERRMSG(msg, type, fmt, args...) __lttng_print(type, msg \
-               " - %s [%ld/%ld]: " fmt " (in %s() at " __FILE__ ":" XSTR(__LINE__) ")\n", \
-                       log_add_time(), (long) getpid(), (long) lttng_gettid(), ## args, __func__)
+#define _ERRMSG(msg, type, fmt, args...)                                \
+       do {                                                            \
+               if (caa_unlikely(__lttng_print_check_opt(type))) {      \
+                       char generic_name[MAX_INT_DEC_LEN(long) +       \
+                                         MAX_INT_DEC_LEN(long)];       \
+                                                                        \
+                       snprintf(generic_name, sizeof(generic_name),    \
+                                       "%ld/%ld", (long) getpid(),     \
+                                       (long) lttng_gettid());         \
+                                                                        \
+                       __lttng_print(type,                             \
+                                       msg " - %s [%s]: " fmt          \
+                                           " (in %s() at " __FILE__    \
+                                           ":" XSTR(__LINE__) ")\n",   \
+                                       log_add_time(),                 \
+                                       URCU_TLS(logger_thread_name) ?: \
+                                                       generic_name,   \
+                                       ##args, __func__);              \
+               }                                                       \
+       } while (0)
 
-#define _ERRMSG_NO_LOC(msg, type, fmt, args...) __lttng_print(type, msg        \
-               " - %s [%ld/%ld]: " fmt "\n", \
-                       log_add_time(), (long) getpid(), (long) lttng_gettid(), ## args)
+#define _ERRMSG_NO_LOC(msg, type, fmt, args...)                          \
+       do {                                                             \
+               if (caa_unlikely(__lttng_print_check_opt(type))) {       \
+                       char generic_name[MAX_INT_DEC_LEN(long) +        \
+                                         MAX_INT_DEC_LEN(long)];        \
+                                                                         \
+                       snprintf(generic_name, sizeof(generic_name),     \
+                                       "%ld/%ld", (long) getpid(),      \
+                                       (long) lttng_gettid());          \
+                                                                         \
+                       __lttng_print(type, msg " - %s [%s]: " fmt "\n", \
+                                       log_add_time(),                  \
+                                       URCU_TLS(logger_thread_name) ?:  \
+                                                       generic_name,    \
+                                       ##args);                         \
+               }                                                        \
+       } while (0)
 
 #define MSG(fmt, args...) \
        __lttng_print(PRINT_MSG, fmt "\n", ## args)
@@ -158,10 +192,10 @@ static inline void __lttng_print_check_abort(enum lttng_error_level type)
 
 #define BUG(fmt, args...) _ERRMSG("BUG", PRINT_BUG, fmt, ## args)
 
-#define DBG(fmt, args...) _ERRMSG("DEBUG1", PRINT_DBG, fmt, ## args)
-#define DBG_NO_LOC(fmt, args...) _ERRMSG_NO_LOC("DEBUG1", PRINT_DBG, fmt, ## args)
-#define DBG2(fmt, args...) _ERRMSG("DEBUG2", PRINT_DBG2, fmt, ## args)
-#define DBG3(fmt, args...) _ERRMSG("DEBUG3", PRINT_DBG3, fmt, ## args)
+#define DBG(fmt, args...) _ERRMSG("DBG1", PRINT_DBG, fmt, ## args)
+#define DBG_NO_LOC(fmt, args...) _ERRMSG_NO_LOC("DBG1", PRINT_DBG, fmt, ## args)
+#define DBG2(fmt, args...) _ERRMSG("DBG2", PRINT_DBG2, fmt, ## args)
+#define DBG3(fmt, args...) _ERRMSG("DBG3", PRINT_DBG3, fmt, ## args)
 #define LOG(type, fmt, args...)                        \
        do {                                    \
                switch (type) {                 \
@@ -226,4 +260,8 @@ const char *error_get_str(int32_t code);
  */
 const char *log_add_time();
 
+/* Name must be a statically-allocated string. */
+LTTNG_HIDDEN
+void logger_set_thread_name(const char *name, bool set_pthread_name);
+
 #endif /* _ERROR_H */
index efd2129217e8829816808901ecf3351baa173d38..2e098900c6ebf2a8166b0c4ef3ad6f3e17417fb3 100644 (file)
@@ -9,6 +9,7 @@
 #include <lttng/condition/buffer-usage-internal.h>
 #include <lttng/condition/session-consumed-size-internal.h>
 #include <lttng/condition/session-rotation-internal.h>
+#include <lttng/condition/event-rule-internal.h>
 #include <common/macros.h>
 #include <common/error.h>
 #include <stdbool.h>
@@ -106,6 +107,13 @@ ssize_t lttng_evaluation_create_from_buffer(
                }
                evaluation_size += ret;
                break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               ret = lttng_evaluation_event_rule_create_from_buffer(&evaluation_view, evaluation);
+               if (ret < 0) {
+                       goto end;
+               }
+               evaluation_size += ret;
+               break;
        default:
                ERR("Attempted to create evaluation of unknown type (%i)",
                                (int) evaluation_comm->type);
diff --git a/src/common/event-rule-kprobe.c b/src/common/event-rule-kprobe.c
new file mode 100644 (file)
index 0000000..8d4b8c4
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <lttng/constant.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kprobe-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/runas.h>
+#include <assert.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define IS_KPROBE_EVENT_RULE(rule) ( \
+       lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KPROBE \
+       )
+
+#if (LTTNG_SYMBOL_NAME_LEN == 256)
+#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255"
+#endif
+
+static
+void lttng_event_rule_kprobe_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kprobe *kprobe;
+
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe,
+                       parent);
+
+       free(kprobe->name);
+       free(kprobe->probe.symbol_name);
+       free(kprobe);
+}
+
+static
+bool lttng_event_rule_kprobe_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_kprobe *kprobe;
+
+       if (!rule) {
+               goto end;
+       }
+
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+       /* Required field */
+       if (!kprobe->name) {
+               ERR("Invalid name event rule: a name must be set.");
+               goto end;
+       }
+       if (kprobe->probe.set == LTTNG_DOMAIN_NONE) {
+               ERR("Invalid kprobe event rule: a source must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_event_rule_kprobe_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       size_t name_len, probe_symbol_name_len;
+       struct lttng_event_rule_kprobe *kprobe;
+       struct lttng_event_rule_kprobe_comm kprobe_comm;
+
+       if (!rule || !IS_KPROBE_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing kprobe event rule");
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+       name_len = strlen(kprobe->name) + 1;
+
+       if (kprobe->probe.symbol_name != NULL) {
+               probe_symbol_name_len = strlen(kprobe->probe.symbol_name) + 1;
+       } else {
+               probe_symbol_name_len = 0;
+       }
+
+       kprobe_comm.name_len = name_len;
+       kprobe_comm.probe_symbol_name_len = probe_symbol_name_len;
+       kprobe_comm.probe_address = kprobe->probe.address;
+       kprobe_comm.probe_offset = kprobe->probe.offset;
+
+       ret = lttng_dynamic_buffer_append(
+                       buf, &kprobe_comm, sizeof(kprobe_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, kprobe->name, name_len);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(
+                       buf, kprobe->probe.symbol_name, probe_symbol_name_len);
+       if (ret) {
+               goto end;
+       }
+
+       if (fd_to_send) {
+               /* Nothing to send */
+               *fd_to_send = -1;
+       }
+end:
+       return ret;
+}
+
+static
+bool lttng_event_rule_kprobe_is_equal(const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_kprobe *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_kprobe, parent);
+       b = container_of(_b, struct lttng_event_rule_kprobe, parent);
+
+       /* Quick checks */
+       if (!!a->name != !!b->name) {
+               goto end;
+       }
+
+       if (!!a->probe.symbol_name != !!b->probe.symbol_name) {
+               goto end;
+       }
+
+       /* Long check */
+       /* kprobe is invalid if this is not true */
+       /* TODO: validate that a kprobe MUST have a name */
+       assert(a->name);
+       assert(b->name);
+       if (strcmp(a->name, b->name)) {
+               goto end;
+       }
+
+       if (a->probe.symbol_name) {
+               /* Both have symbol name due to previous checks */
+               if (strcmp(a->probe.symbol_name, b->probe.symbol_name)) {
+                       goto end;
+               }
+       }
+
+       if (a->probe.offset != b->probe.offset) {
+               goto end;
+       }
+
+       if (a->probe.address != b->probe.address) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static
+enum lttng_error_code lttng_event_rule_kprobe_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+       /* Nothing to do */
+       return LTTNG_OK;
+}
+
+static const char *lttng_event_rule_kprobe_get_filter(
+               const struct lttng_event_rule *rule)
+{
+       /* Not supported */
+       return NULL;
+}
+
+static const struct lttng_filter_bytecode *
+lttng_event_rule_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
+{
+       /* Not supported */
+       return NULL;
+}
+
+static
+struct lttng_event_exclusion *lttng_event_rule_kprobe_generate_exclusions(struct lttng_event_rule *rule)
+{
+       /* Not supported */
+       return NULL;
+}
+
+struct lttng_event_rule *lttng_event_rule_kprobe_create()
+{
+       struct lttng_event_rule_kprobe *rule;
+
+       rule = zmalloc(sizeof(struct lttng_event_rule_kprobe));
+       if (!rule) {
+               return NULL;
+       }
+
+       lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_KPROBE);
+       rule->parent.validate = lttng_event_rule_kprobe_validate;
+       rule->parent.serialize = lttng_event_rule_kprobe_serialize;
+       rule->parent.equal = lttng_event_rule_kprobe_is_equal;
+       rule->parent.destroy = lttng_event_rule_kprobe_destroy;
+       rule->parent.populate = lttng_event_rule_kprobe_populate;
+       rule->parent.get_filter = lttng_event_rule_kprobe_get_filter;
+       rule->parent.get_filter_bytecode = lttng_event_rule_kprobe_get_filter_bytecode;
+       rule->parent.generate_exclusions = lttng_event_rule_kprobe_generate_exclusions;
+       return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_kprobe_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_event_rule_kprobe_comm *kprobe_comm;
+       const char *name;
+       const char *probe_symbol_name = NULL;
+       struct lttng_buffer_view current_view;
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_event_rule_kprobe *kprobe = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       if (view->size < sizeof(*kprobe_comm)) {
+               ERR("Failed to initialize from malformed event rule kprobe: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       current_view = lttng_buffer_view_from_view(
+                       view, offset, sizeof(*kprobe_comm));
+       kprobe_comm = (typeof(kprobe_comm)) current_view.data;
+       if (!kprobe_comm) {
+               ret = -1;
+               goto end;
+       }
+
+       rule = lttng_event_rule_kprobe_create();
+       if (!rule) {
+               ERR("Failed to create event rule kprobe");
+               ret = -1;
+               goto end;
+       }
+
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+       /* Skip to payload */
+       offset += current_view.size;
+       /* Map the name */
+       current_view = lttng_buffer_view_from_view(
+                       view, offset, kprobe_comm->name_len);
+       name = current_view.data;
+       if (!name) {
+               ret = -1;
+               goto end;
+       }
+
+       if (kprobe_comm->name_len == 1 ||
+                       name[kprobe_comm->name_len - 1] != '\0' ||
+                       strlen(name) != kprobe_comm->name_len - 1) {
+               /*
+                * Check that the name is not NULL, is NULL-terminated, and
+                * does not contain a NULL before the last byte.
+                */
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the name */
+       offset += kprobe_comm->name_len;
+       if (!kprobe_comm->probe_symbol_name_len) {
+               goto skip_probe_symbol_name;
+       }
+
+       /* Map the probe_symbol_name */
+       current_view = lttng_buffer_view_from_view(
+                       view, offset, kprobe_comm->probe_symbol_name_len);
+       probe_symbol_name = current_view.data;
+       if (!probe_symbol_name) {
+               ret = -1;
+               goto end;
+       }
+
+       if (kprobe_comm->probe_symbol_name_len == 1 ||
+                       probe_symbol_name[kprobe_comm->probe_symbol_name_len -
+                                         1] != '\0' ||
+                       strlen(probe_symbol_name) !=
+                                       kprobe_comm->probe_symbol_name_len -
+                                                       1) {
+               /*
+                * Check that the filter expression is not NULL, is
+                * NULL-terminated, and does not contain a NULL before the last
+                * byte.
+                */
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern */
+       offset += kprobe_comm->probe_symbol_name_len;
+
+skip_probe_symbol_name:
+
+       status = lttng_event_rule_kprobe_set_name(rule, name);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule kprobe name");
+               ret = -1;
+               goto end;
+       }
+       kprobe->probe.offset = kprobe_comm->probe_offset;
+       kprobe->probe.address = kprobe_comm->probe_address;
+       if (probe_symbol_name) {
+               kprobe->probe.symbol_name = strdup(probe_symbol_name);
+               if (!kprobe->probe.symbol_name) {
+                       ERR("Failed to set event rule kprobe probe symbol name");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kprobe_set_source(
+               struct lttng_event_rule *rule, const char *source)
+{
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+       int match;
+       char s_hex[19];
+       char name[LTTNG_SYMBOL_NAME_LEN];
+       struct lttng_event_rule_kprobe *kprobe;
+
+       /* TODO: support multiple call for this, we must free the symbol name if
+        * that happens !!!
+        */
+
+       if (!source || !IS_KPROBE_EVENT_RULE(rule) || !rule) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+       /* Check for symbol+offset */
+       match = sscanf(source, "%18s[^'+']+%18s", name, s_hex);
+       if (match == 2) {
+               /* TODO double validate termination handling of this */
+               kprobe->probe.symbol_name =
+                               strndup(name, LTTNG_SYMBOL_NAME_LEN);
+               if (!kprobe->probe.symbol_name) {
+                       status = LTTNG_EVENT_RULE_STATUS_ERROR;
+                       goto end;
+               }
+               if (*s_hex == '\0') {
+                       status = LTTNG_EVENT_RULE_STATUS_INVALID;
+                       goto end;
+               }
+               kprobe->probe.offset = strtoul(s_hex, NULL, 0);
+               kprobe->probe.address = 0;
+               kprobe->probe.set = true;
+               goto end;
+       }
+
+       /* Check for symbol */
+       if (isalpha(name[0]) || name[0] == '_') {
+               match = sscanf(source,
+                               "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API
+                               "s",
+                               name);
+               if (match == 1) {
+                       /* TODO double validate termination handling of this */
+                       kprobe->probe.symbol_name =
+                                       strndup(name, LTTNG_SYMBOL_NAME_LEN);
+                       if (!kprobe->probe.symbol_name) {
+                               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+                               goto end;
+                       }
+                       kprobe->probe.offset = 0;
+                       kprobe->probe.address = 0;
+                       kprobe->probe.set = true;
+                       goto end;
+               }
+       }
+
+       /* Check for address */
+       match = sscanf(source, "%18s", s_hex);
+       if (match > 0) {
+               if (*s_hex == '\0') {
+                       status = LTTNG_EVENT_RULE_STATUS_INVALID;
+                       goto end;
+               }
+               kprobe->probe.address = strtoul(s_hex, NULL, 0);
+               kprobe->probe.offset = 0;
+               kprobe->probe.symbol_name = NULL;
+               kprobe->probe.set = true;
+               goto end;
+       }
+
+       /* No match */
+       status = LTTNG_EVENT_RULE_STATUS_INVALID;
+
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kprobe_set_name(
+               struct lttng_event_rule *rule, const char *name)
+{
+       char *name_copy = NULL;
+       struct lttng_event_rule_kprobe *kprobe;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name ||
+                       strlen(name) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+       name_copy = strdup(name);
+       if (!name_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (kprobe->name) {
+               free(kprobe->name);
+       }
+
+       kprobe->name = name_copy;
+       name_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kprobe_get_name(
+               const struct lttng_event_rule *rule, const char **name)
+{
+       struct lttng_event_rule_kprobe *kprobe;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+       if (!kprobe->name) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *name = kprobe->name;
+end:
+       return status;
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kprobe_get_address(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kprobe *kprobe;
+
+       assert(rule && IS_KPROBE_EVENT_RULE(rule));
+
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+       return kprobe->probe.address;
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kprobe_get_offset(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kprobe *kprobe;
+
+       assert(rule && IS_KPROBE_EVENT_RULE(rule));
+
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+       return kprobe->probe.offset;
+}
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_kprobe_get_symbol_name(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kprobe *kprobe;
+
+       assert(rule && IS_KPROBE_EVENT_RULE(rule));
+
+       kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+       return kprobe->probe.symbol_name;
+}
diff --git a/src/common/event-rule-kretprobe.c b/src/common/event-rule-kretprobe.c
new file mode 100644 (file)
index 0000000..56916d9
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kretprobe-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/runas.h>
+#include <assert.h>
+
+#define IS_KRETPROBE_EVENT_RULE(rule) ( \
+       lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KRETPROBE \
+       )
+
+static
+void lttng_event_rule_kretprobe_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kretprobe *kretprobe;
+
+       kretprobe = container_of(rule, struct lttng_event_rule_kretprobe,
+                       parent);
+
+       /*
+        * TODO
+        */
+       free(kretprobe);
+}
+
+static
+bool lttng_event_rule_kretprobe_validate(
+               const struct lttng_event_rule *rule)
+{
+       /* TODO */
+       return false;
+}
+
+static
+int lttng_event_rule_kretprobe_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_dynamic_buffer *buf, int *fd_to_send)
+{
+       /* TODO */
+       return -1;
+}
+
+static
+bool lttng_event_rule_kretprobe_is_equal(const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       /* TODO */
+       return false;
+}
+
+static
+enum lttng_error_code lttng_event_rule_kretprobe_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+       /* Nothing to do */
+       return LTTNG_OK;
+}
+
+static const char *lttng_event_rule_kretprobe_get_filter(
+               const struct lttng_event_rule *rule)
+{
+       /* Not supported */
+       return NULL;
+}
+
+static const struct lttng_filter_bytecode *
+lttng_event_rule_kretprobe_get_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       /* Not supported */
+       return NULL;
+}
+
+static struct lttng_event_exclusion *
+lttng_event_rule_kretprobe_generate_exclusions(struct lttng_event_rule *rule)
+{
+       /* Not supported */
+       return NULL;
+}
+
+struct lttng_event_rule *lttng_event_rule_kretprobe_create()
+{
+       struct lttng_event_rule_kretprobe *rule;
+
+       rule = zmalloc(sizeof(struct lttng_event_rule_kretprobe));
+       if (!rule) {
+               return NULL;
+       }
+
+       lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_KRETPROBE);
+       rule->parent.validate = lttng_event_rule_kretprobe_validate;
+       rule->parent.serialize = lttng_event_rule_kretprobe_serialize;
+       rule->parent.equal = lttng_event_rule_kretprobe_is_equal;
+       rule->parent.destroy = lttng_event_rule_kretprobe_destroy;
+       rule->parent.populate = lttng_event_rule_kretprobe_populate;
+       rule->parent.get_filter = lttng_event_rule_kretprobe_get_filter;
+       rule->parent.get_filter_bytecode = lttng_event_rule_kretprobe_get_filter_bytecode;
+       rule->parent.generate_exclusions = lttng_event_rule_kretprobe_generate_exclusions;
+       return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_kretprobe_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       /* TODO */
+       return -1;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kretprobe_set_source(
+               struct lttng_event_rule *rule, const char *source)
+{
+       return LTTNG_EVENT_RULE_STATUS_UNSUPPORTED;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kretprobe_set_name(
+               struct lttng_event_rule *rule, const char *name)
+{
+       return LTTNG_EVENT_RULE_STATUS_UNSUPPORTED;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kretprobe_get_name(
+               const struct lttng_event_rule *rule, const char **name)
+{
+       return LTTNG_EVENT_RULE_STATUS_UNSUPPORTED;
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kretprobe_get_address(
+               const struct lttng_event_rule *rule)
+{
+       assert("Not implemented" && 0);
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kretprobe_get_offset(
+               const struct lttng_event_rule *rule)
+{
+       assert("Not implemented" && 0);
+}
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_kretprobe_get_symbol_name(
+               const struct lttng_event_rule *rule)
+{
+       assert("Not implemented" && 0);
+}
diff --git a/src/common/event-rule-syscall.c b/src/common/event-rule-syscall.c
new file mode 100644 (file)
index 0000000..97f5f86
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/syscall-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/runas.h>
+#include <assert.h>
+
+#define IS_SYSCALL_EVENT_RULE(rule) ( \
+       lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_SYSCALL \
+       )
+
+static
+void lttng_event_rule_syscall_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_syscall *syscall;
+
+       if (rule == NULL) {
+               return;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_syscall,
+                       parent);
+
+       free(syscall->pattern);
+       free(syscall->filter_expression);
+       free(syscall->internal_filter.filter);
+       free(syscall->internal_filter.bytecode);
+       free(syscall);
+}
+
+static
+bool lttng_event_rule_syscall_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_syscall *syscall;
+
+       if (!rule) {
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+
+       /* Required field */
+       if (!syscall->pattern) {
+               ERR("Invalid syscall event rule: a pattern must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_event_rule_syscall_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       size_t pattern_len, filter_expression_len;
+       struct lttng_event_rule_syscall *syscall;
+       struct lttng_event_rule_syscall_comm syscall_comm;
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing syscall event rule");
+       syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+
+       pattern_len = strlen(syscall->pattern) + 1;
+
+       if (syscall->filter_expression != NULL) {
+               filter_expression_len = strlen(syscall->filter_expression) + 1;
+       } else {
+               filter_expression_len = 0;
+       }
+
+       syscall_comm.pattern_len = pattern_len;
+       syscall_comm.filter_expression_len = filter_expression_len;
+
+       ret = lttng_dynamic_buffer_append(
+                       buf, &syscall_comm, sizeof(syscall_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, syscall->pattern, pattern_len);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(
+                       buf, syscall->filter_expression, filter_expression_len);
+       if (ret) {
+               goto end;
+       }
+
+       if (fd_to_send) {
+               /* Nothing to send */
+               *fd_to_send = -1;
+       }
+end:
+       return ret;
+}
+
+static
+bool lttng_event_rule_syscall_is_equal(const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_syscall *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_syscall, parent);
+       b = container_of(_b, struct lttng_event_rule_syscall, parent);
+
+       if (!!a->filter_expression != !!b->filter_expression) {
+               goto end;
+       }
+
+       /* Long check */
+       /* syscall is invalid if this is not true */
+       assert(a->pattern);
+       assert(b->pattern);
+       if (strcmp(a->pattern, b->pattern)) {
+               goto end;
+       }
+
+       if (a->filter_expression && b->filter_expression) {
+               if (strcmp(a->filter_expression, b->filter_expression)) {
+                       goto end;
+               }
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static
+enum lttng_error_code lttng_event_rule_syscall_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+       int ret;
+       enum lttng_error_code ret_code = LTTNG_OK;
+       struct lttng_event_rule_syscall *syscall;
+       enum lttng_event_rule_status status;
+       const char *filter;
+       struct lttng_filter_bytecode *bytecode = NULL;
+
+       assert(rule);
+
+       syscall = container_of(rule, struct lttng_event_rule_syscall,
+                       parent);
+
+       /* Generate the filter bytecode */
+       status = lttng_event_rule_syscall_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       if (filter && filter[0] == '\0') {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       if (filter == NULL) {
+               /* Nothing to do */
+               ret = LTTNG_OK;
+               goto end;
+       }
+
+       syscall->internal_filter.filter = strdup(filter);
+       if (syscall->internal_filter.filter == NULL) {
+               ret_code = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       ret = run_as_generate_filter_bytecode(syscall->internal_filter.filter, uid, gid, &bytecode);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+       }
+
+       syscall->internal_filter.bytecode = bytecode;
+       bytecode = NULL;
+
+error:
+end:
+       free(bytecode);
+       return ret_code;
+}
+
+static const char *lttng_event_rule_syscall_get_internal_filter(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_syscall *syscall;
+       assert(rule);
+
+       syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+       return syscall->internal_filter.filter;
+}
+
+static const struct lttng_filter_bytecode *
+lttng_event_rule_syscall_get_internal_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_syscall *syscall;
+       assert(rule);
+
+       syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+       return syscall->internal_filter.bytecode;
+}
+
+static struct lttng_event_exclusion *
+lttng_event_rule_syscall_generate_exclusions(struct lttng_event_rule *rule)
+{
+       /* Not supported */
+       return NULL;
+}
+
+struct lttng_event_rule *lttng_event_rule_syscall_create()
+{
+       struct lttng_event_rule_syscall *rule;
+
+       rule = zmalloc(sizeof(struct lttng_event_rule_syscall));
+       if (!rule) {
+               return NULL;
+       }
+
+       lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_SYSCALL);
+       rule->parent.validate = lttng_event_rule_syscall_validate;
+       rule->parent.serialize = lttng_event_rule_syscall_serialize;
+       rule->parent.equal = lttng_event_rule_syscall_is_equal;
+       rule->parent.destroy = lttng_event_rule_syscall_destroy;
+       rule->parent.populate = lttng_event_rule_syscall_populate;
+       rule->parent.get_filter = lttng_event_rule_syscall_get_internal_filter;
+       rule->parent.get_filter_bytecode =
+                       lttng_event_rule_syscall_get_internal_filter_bytecode;
+       rule->parent.generate_exclusions =
+                       lttng_event_rule_syscall_generate_exclusions;
+       return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_syscall_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_event_rule_syscall_comm *syscall_comm;
+       const char *pattern;
+       const char *filter_expression = NULL;
+       struct lttng_buffer_view current_view;
+       struct lttng_event_rule *rule = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       if (view->size < sizeof(*syscall_comm)) {
+               ERR("Failed to initialize from malformed event rule syscall: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       current_view = lttng_buffer_view_from_view(
+                       view, offset, sizeof(*syscall_comm));
+       syscall_comm = (typeof(syscall_comm)) current_view.data;
+
+       if (!syscall_comm) {
+               ret = -1;
+               goto end;
+       }
+
+       rule = lttng_event_rule_syscall_create();
+       if (!rule) {
+               ERR("Failed to create event rule syscall");
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip to payload */
+       offset += current_view.size;
+
+       /* Map the pattern */
+       current_view = lttng_buffer_view_from_view(
+                       view, offset, syscall_comm->pattern_len);
+       pattern = current_view.data;
+       if (!pattern) {
+               ret = -1;
+               goto end;
+       }
+
+       if (syscall_comm->pattern_len == 1 ||
+                       pattern[syscall_comm->pattern_len - 1] != '\0' ||
+                       strlen(pattern) != syscall_comm->pattern_len - 1) {
+               /*
+                * Check that the pattern is not NULL, is NULL-terminated, and
+                * does not contain a NULL before the last byte.
+                */
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern */
+       offset += syscall_comm->pattern_len;
+
+       if (!syscall_comm->filter_expression_len) {
+               goto skip_filter_expression;
+       }
+
+       /* Map the filter_expression */
+       current_view = lttng_buffer_view_from_view(
+                       view, offset, syscall_comm->filter_expression_len);
+       filter_expression = current_view.data;
+       if (!filter_expression) {
+               ret = -1;
+               goto end;
+       }
+
+       if (syscall_comm->filter_expression_len == 1 ||
+                       filter_expression[syscall_comm->filter_expression_len -
+                                         1] != '\0' ||
+                       strlen(filter_expression) !=
+                                       syscall_comm->filter_expression_len -
+                                                       1) {
+               /*
+                * Check that the filter expression is not NULL, is
+                * NULL-terminated, and does not contain a NULL before the last
+                * byte.
+                */
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern */
+       offset += syscall_comm->filter_expression_len;
+
+skip_filter_expression:
+
+       status = lttng_event_rule_syscall_set_pattern(rule, pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule syscall pattern");
+               ret = -1;
+               goto end;
+       }
+
+       if (filter_expression) {
+               status = lttng_event_rule_syscall_set_filter(
+                               rule, filter_expression);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule syscall pattern");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_syscall_set_pattern(
+               struct lttng_event_rule *rule, const char *pattern)
+{
+       char *pattern_copy = NULL;
+       struct lttng_event_rule_syscall *syscall;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern ||
+                       strlen(pattern) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+       pattern_copy = strdup(pattern);
+       if (!pattern_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (syscall->pattern) {
+               free(syscall->pattern);
+       }
+
+       syscall->pattern = pattern_copy;
+       pattern_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_syscall_get_pattern(
+               const struct lttng_event_rule *rule, const char **pattern)
+{
+       struct lttng_event_rule_syscall *syscall;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+       if (!syscall->pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *pattern = syscall->pattern;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_syscall_set_filter(
+               struct lttng_event_rule *rule, const char *expression)
+{
+       char *expression_copy = NULL;
+       struct lttng_event_rule_syscall *syscall;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       /* TODO: validate that the passed expression is valid */
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression ||
+                       strlen(expression) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+       expression_copy = strdup(expression);
+       if (!expression_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (syscall->filter_expression) {
+               free(syscall->filter_expression);
+       }
+
+       syscall->filter_expression = expression_copy;
+       expression_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_syscall_get_filter(
+               const struct lttng_event_rule *rule, const char **expression)
+{
+       struct lttng_event_rule_syscall *syscall;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+       if (!syscall->filter_expression) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *expression = syscall->filter_expression;
+end:
+       return status;
+}
diff --git a/src/common/event-rule-tracepoint.c b/src/common/event-rule-tracepoint.c
new file mode 100644 (file)
index 0000000..f66e641
--- /dev/null
@@ -0,0 +1,1125 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <lttng/event.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/tracepoint-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/runas.h>
+#include <assert.h>
+
+#define IS_TRACEPOINT_EVENT_RULE(rule) ( \
+       lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT \
+       )
+
+static
+void lttng_event_rule_tracepoint_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+
+       if (rule == NULL) {
+               return;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+
+       free(tracepoint->pattern);
+       free(tracepoint->filter_expression);
+       for(int i = 0; i < tracepoint->exclusions.count; i++) {
+               free(tracepoint->exclusions.values[i]);
+       }
+       free(tracepoint->exclusions.values);
+       free(tracepoint->internal_filter.filter);
+       free(tracepoint->internal_filter.bytecode);
+       free(tracepoint);
+}
+
+static
+bool lttng_event_rule_tracepoint_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_tracepoint *tracepoint;
+
+       if (!rule) {
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+
+       /* Required field */
+       if (!tracepoint->pattern) {
+               ERR("Invalid tracepoint event rule: a pattern must be set.");
+               goto end;
+       }
+       if (tracepoint->domain == LTTNG_DOMAIN_NONE) {
+               ERR("Invalid tracepoint event rule: a domain must be set.");
+               goto end;
+       }
+
+       /* QUESTION: do we validate inside state on validate or during set of
+        * each component */
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_event_rule_tracepoint_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       size_t pattern_len, filter_expression_len, exclusions_len;
+       struct lttng_event_rule_tracepoint *tracepoint;
+       struct lttng_event_rule_tracepoint_comm tracepoint_comm;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing tracepoint event rule");
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_tracepoint, parent);
+
+       pattern_len = strlen(tracepoint->pattern) + 1;
+
+       if (tracepoint->filter_expression != NULL) {
+               filter_expression_len =
+                               strlen(tracepoint->filter_expression) + 1;
+       } else {
+               filter_expression_len = 0;
+       }
+
+       exclusions_len = 0;
+       for (int i = 0; i < tracepoint->exclusions.count; i++) {
+               /* Payload */
+               exclusions_len += strlen(tracepoint->exclusions.values[i]) + 1;
+               /* Bound check */
+               exclusions_len += sizeof(uint32_t);
+       }
+
+       tracepoint_comm.domain_type = (int8_t) tracepoint->domain;
+       tracepoint_comm.loglevel_type = (int8_t) tracepoint->loglevel.type;
+       tracepoint_comm.loglevel_value = tracepoint->loglevel.value;
+       tracepoint_comm.pattern_len = pattern_len;
+       tracepoint_comm.filter_expression_len = filter_expression_len;
+       tracepoint_comm.exclusions_count = tracepoint->exclusions.count;
+       tracepoint_comm.exclusions_len = exclusions_len;
+
+       ret = lttng_dynamic_buffer_append(
+                       buf, &tracepoint_comm, sizeof(tracepoint_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, tracepoint->pattern,
+                       pattern_len);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, tracepoint->filter_expression,
+                       filter_expression_len);
+       if (ret) {
+               goto end;
+       }
+
+       size_t exclusions_appended = 0;
+       for (int i = 0; i < tracepoint->exclusions.count; i++) {
+               size_t len;
+               len = strlen(tracepoint->exclusions.values[i]) + 1;
+               /* Append bound check, does not include the '\0' */
+               ret = lttng_dynamic_buffer_append(buf, &len, sizeof(uint32_t));
+               if (ret) {
+                       goto end;
+               }
+               exclusions_appended += sizeof(uint32_t);
+
+               /* Include the '\0' in the payload */
+               ret = lttng_dynamic_buffer_append(buf, tracepoint->exclusions.values[i],
+                               len);
+               if (ret) {
+                       goto end;
+               }
+               exclusions_appended += len;
+       }
+
+       assert(exclusions_len == exclusions_appended);
+
+       if (fd_to_send) {
+               /* No fd to send */
+               *fd_to_send = -1;
+       }
+
+end:
+       return ret;
+}
+
+static
+bool lttng_event_rule_tracepoint_is_equal(const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_tracepoint *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_tracepoint, parent);
+       b = container_of(_b, struct lttng_event_rule_tracepoint, parent);
+
+       /* Quick checks */
+       if (a->domain != b->domain) {
+               goto end;
+       }
+
+       if (a->exclusions.count != b->exclusions.count) {
+               goto end;
+       }
+
+       if (!!a->filter_expression != !!b->filter_expression) {
+               goto end;
+       }
+
+       /* Long check */
+       /* Tracepoint is invalid if this is not true */
+       assert(a->pattern);
+       assert(b->pattern);
+       if (strcmp(a->pattern, b->pattern)) {
+               goto end;
+       }
+
+       if (a->filter_expression && b->filter_expression) {
+               if (strcmp(a->filter_expression, b->filter_expression)) {
+                       goto end;
+               }
+       }
+
+       if (a->loglevel.type != b->loglevel.type) {
+               goto end;
+       }
+
+       if (a->loglevel.value != b->loglevel.value) {
+               goto end;
+       }
+
+       for (int i = 0; i < a->exclusions.count; i++) {
+               if (strcmp(a->exclusions.values[i], b->exclusions.values[i])) {
+                       goto end;
+               }
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+/*
+ * On success ret is 0;
+ *
+ * On error ret is negative.
+ *
+ * An event with NO loglevel and the name is * will return NULL.
+ */
+static int generate_agent_filter(
+               const struct lttng_event_rule *rule,
+               char **_agent_filter)
+{
+       int err;
+       int ret = 0;
+       char *agent_filter = NULL;
+       const char *pattern;
+       const char *filter;
+       enum lttng_loglevel_type loglevel_type;
+       enum lttng_event_rule_status status;
+
+       assert(rule);
+       assert(_agent_filter);
+
+       status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_event_rule_tracepoint_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_event_rule_tracepoint_get_loglevel_type(rule, &loglevel_type);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Don't add filter for the '*' event. */
+       if (strcmp(pattern, "*") != 0) {
+               if (filter) {
+                       err = asprintf(&agent_filter, "(%s) && (logger_name == \"%s\")", filter,
+                                       pattern);
+               } else {
+                       err = asprintf(&agent_filter, "logger_name == \"%s\"", pattern);
+               }
+               if (err < 0) {
+                       PERROR("asprintf");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) {
+               const char *op;
+               int loglevel_value;
+
+               status = lttng_event_rule_tracepoint_get_loglevel(rule, &loglevel_value);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               if (loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE) {
+                       op = ">=";
+               } else {
+                       op = "==";
+               }
+
+               if (filter || agent_filter) {
+                       char *new_filter;
+
+                       err = asprintf(&new_filter, "(%s) && (int_loglevel %s %d)",
+                                       agent_filter ? agent_filter : filter, op,
+                                       loglevel_value);
+                       if (agent_filter) {
+                               free(agent_filter);
+                       }
+                       agent_filter = new_filter;
+               } else {
+                       err = asprintf(&agent_filter, "int_loglevel %s %d", op,
+                                       loglevel_value);
+               }
+               if (err < 0) {
+                       PERROR("asprintf");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_agent_filter = agent_filter;
+       agent_filter = NULL;
+
+end:
+       free(agent_filter);
+       return ret;
+}
+
+static
+enum lttng_error_code lttng_event_rule_tracepoint_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_domain_type domain_type;
+       enum lttng_event_rule_status status;
+       const char *filter;
+       struct lttng_filter_bytecode *bytecode = NULL;
+
+       assert(rule);
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+
+       status = lttng_event_rule_tracepoint_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       if (filter && filter[0] == '\0') {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       status = lttng_event_rule_tracepoint_get_domain_type(rule, &domain_type);
+       if (status !=  LTTNG_EVENT_RULE_STATUS_OK) {
+               ret_code = LTTNG_ERR_UNK;
+               goto error;
+       }
+
+       switch (domain_type) {
+       case LTTNG_DOMAIN_LOG4J:
+       case LTTNG_DOMAIN_JUL:
+       case LTTNG_DOMAIN_PYTHON:
+       {
+               char *agent_filter;
+               ret = generate_agent_filter(rule, &agent_filter);
+               if (ret) {
+                       ret_code = LTTNG_ERR_FILTER_INVAL;
+                       goto error;
+               }
+               tracepoint->internal_filter.filter = agent_filter;
+               break;
+       }
+       default:
+       {
+               if (filter) {
+                       tracepoint->internal_filter.filter = strdup(filter);
+                       if (tracepoint->internal_filter.filter == NULL) {
+                               ret_code = LTTNG_ERR_NOMEM;
+                               goto error;
+                       }
+               } else {
+                       tracepoint->internal_filter.filter = NULL;
+               }
+               break;
+       }
+       }
+
+       if (tracepoint->internal_filter.filter == NULL) {
+               ret_code = LTTNG_OK;
+               goto end;
+       }
+
+       ret = run_as_generate_filter_bytecode(tracepoint->internal_filter.filter, uid, gid, &bytecode);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+       }
+
+       tracepoint->internal_filter.bytecode = bytecode;
+       bytecode = NULL;
+       ret_code = LTTNG_OK;
+
+error:
+end:
+       free(bytecode);
+       return ret_code;
+
+}
+
+static const char *lttng_event_rule_tracepoint_get_internal_filter(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       assert(rule);
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       return tracepoint->internal_filter.filter;
+}
+
+static const struct lttng_filter_bytecode *
+lttng_event_rule_tracepoint_get_internal_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       assert(rule);
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       return tracepoint->internal_filter.bytecode;
+}
+
+/* TODO: review error handling, the function should be able to
+ * return error information.
+ */
+static struct lttng_event_exclusion *
+lttng_event_rule_tracepoint_generate_exclusions(struct lttng_event_rule *rule)
+{
+       enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
+       struct lttng_event_exclusion *local_exclusions = NULL;
+       struct lttng_event_exclusion *ret_exclusions = NULL;
+       unsigned int nb_exclusions = 0;
+
+       (void) lttng_event_rule_tracepoint_get_domain_type(rule, &domain_type);
+
+       switch (domain_type) {
+       case LTTNG_DOMAIN_KERNEL:
+       case LTTNG_DOMAIN_JUL:
+       case LTTNG_DOMAIN_LOG4J:
+       case LTTNG_DOMAIN_PYTHON:
+               /* Not supported */
+               ret_exclusions = NULL;
+               goto end;
+       case LTTNG_DOMAIN_UST:
+               /* Exclusions supported */
+               break;
+       default:
+               assert(0);
+       }
+
+       (void) lttng_event_rule_tracepoint_get_exclusions_count(rule, &nb_exclusions);
+       if (nb_exclusions == 0) {
+               /* Nothing to do */
+               ret_exclusions = NULL;
+               goto end;
+       }
+
+       local_exclusions = zmalloc(sizeof(struct lttng_event_exclusion) + (LTTNG_SYMBOL_NAME_LEN * nb_exclusions));
+       if (!local_exclusions) {
+               ERR("local exclusion allocation");
+               ret_exclusions = NULL;
+               goto end;
+       }
+
+       local_exclusions->count = nb_exclusions;
+       for (unsigned int i = 0; i < nb_exclusions; i++) {
+               /* TODO: check for truncation.
+                * Part of this should be validated on set exclusion
+                */
+               const char *tmp;
+               (void) lttng_event_rule_tracepoint_get_exclusion_at_index(rule, i, &tmp);
+               strncpy(local_exclusions->names[i], tmp, LTTNG_SYMBOL_NAME_LEN);
+               local_exclusions->names[i][LTTNG_SYMBOL_NAME_LEN-1] = '\0';
+       }
+
+       /* Pass ownership */
+       ret_exclusions = local_exclusions;
+       local_exclusions = NULL;
+end:
+       free(local_exclusions);
+       /* Not supported */
+       return ret_exclusions;
+}
+
+static struct lttng_event *lttng_event_rule_tracepoint_generate_lttng_event(
+               const struct lttng_event_rule *rule)
+{
+       const struct lttng_event_rule_tracepoint *tracepoint;
+       struct lttng_event *local_event = NULL;
+       struct lttng_event *event = NULL;
+
+       tracepoint = container_of(
+                       rule, const struct lttng_event_rule_tracepoint, parent);
+
+       local_event = zmalloc(sizeof(*local_event));
+       if (!local_event) {
+               goto error;
+       }
+
+       local_event->type = LTTNG_EVENT_TRACEPOINT;
+       (void) strncpy(local_event->name, tracepoint->pattern,
+                       sizeof(local_event->name) - 1);
+       local_event->name[sizeof(local_event->name) - 1] = '\0';
+       local_event->loglevel_type = tracepoint->loglevel.type;
+       local_event->loglevel = tracepoint->loglevel.value;
+
+       event = local_event;
+       local_event = NULL;
+error:
+       free(local_event);
+       return event;
+}
+
+struct lttng_event_rule *lttng_event_rule_tracepoint_create(enum lttng_domain_type domain_type)
+{
+       struct lttng_event_rule_tracepoint *rule;
+
+       if (domain_type == LTTNG_DOMAIN_NONE) {
+               return NULL;
+       }
+
+       rule = zmalloc(sizeof(struct lttng_event_rule_tracepoint));
+       if (!rule) {
+               return NULL;
+       }
+
+       lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_TRACEPOINT);
+       rule->parent.validate = lttng_event_rule_tracepoint_validate;
+       rule->parent.serialize = lttng_event_rule_tracepoint_serialize;
+       rule->parent.equal = lttng_event_rule_tracepoint_is_equal;
+       rule->parent.destroy = lttng_event_rule_tracepoint_destroy;
+       rule->parent.populate = lttng_event_rule_tracepoint_populate;
+       rule->parent.get_filter = lttng_event_rule_tracepoint_get_internal_filter;
+       rule->parent.get_filter_bytecode = lttng_event_rule_tracepoint_get_internal_filter_bytecode;
+       rule->parent.generate_exclusions = lttng_event_rule_tracepoint_generate_exclusions;
+       rule->parent.generate_lttng_event =
+                       lttng_event_rule_tracepoint_generate_lttng_event;
+
+       rule->domain = domain_type;
+       rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL;
+
+       return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_tracepoint_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       enum lttng_event_rule_status status;
+       enum lttng_domain_type domain_type;
+       enum lttng_loglevel_type loglevel_type;
+       const struct lttng_event_rule_tracepoint_comm *tracepoint_comm;
+       const char *pattern;
+       const char *filter_expression = NULL;
+       const char **exclusions =  NULL;
+       const uint32_t *exclusion_len;
+       const char *exclusion;
+       struct lttng_buffer_view current_view;
+       struct lttng_event_rule *rule = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       if (view->size < sizeof(*tracepoint_comm)) {
+               ERR("Failed to initialize from malformed event rule tracepoint: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       current_view = lttng_buffer_view_from_view(view, offset, sizeof(*tracepoint_comm));
+       tracepoint_comm = (typeof(tracepoint_comm)) current_view.data;
+
+       if(!tracepoint_comm) {
+               ret = -1;
+               goto end;
+       }
+
+       if (tracepoint_comm->domain_type <= LTTNG_DOMAIN_NONE ||
+                       tracepoint_comm->domain_type > LTTNG_DOMAIN_PYTHON) {
+               /* Invalid domain value. */
+               ERR("Invalid domain type value (%i) found in tracepoint_comm buffer",
+                               (int) tracepoint_comm->domain_type);
+               ret = -1;
+               goto end;
+       }
+
+       domain_type = (enum lttng_domain_type) tracepoint_comm->domain_type;
+       rule = lttng_event_rule_tracepoint_create(domain_type);
+       if (!rule) {
+               ERR("Failed to create event rule tracepoint");
+               ret = -1;
+               goto end;
+       }
+
+       loglevel_type = (enum lttng_loglevel_type)
+                                       tracepoint_comm->loglevel_type;
+       switch (loglevel_type) {
+       case LTTNG_EVENT_LOGLEVEL_ALL:
+               status = lttng_event_rule_tracepoint_set_loglevel_all(rule);
+               break;
+       case LTTNG_EVENT_LOGLEVEL_RANGE:
+               status = lttng_event_rule_tracepoint_set_loglevel_range(
+                               rule, (enum lttng_loglevel_type) tracepoint_comm
+                                                     ->loglevel_value);
+               break;
+       case LTTNG_EVENT_LOGLEVEL_SINGLE:
+               status = lttng_event_rule_tracepoint_set_loglevel(
+                               rule, (enum lttng_loglevel_type) tracepoint_comm
+                                                     ->loglevel_value);
+               break;
+       default:
+               ERR("Failed to set event rule tracepoint loglevel: unknown loglevel type");
+               ret = -1;
+               goto end;
+       }
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule tracepoint loglevel");
+       }
+
+       /* Skip to payload */
+       offset += current_view.size;
+
+       /* Map the pattern */
+       current_view = lttng_buffer_view_from_view(view, offset, tracepoint_comm->pattern_len);
+       pattern = current_view.data;
+       if (!pattern) {
+               ret = -1;
+               goto end;
+       }
+
+       if (tracepoint_comm->pattern_len == 1 ||
+                       pattern[tracepoint_comm->pattern_len - 1] != '\0' ||
+                       strlen(pattern) != tracepoint_comm->pattern_len - 1) {
+               /*
+                * Check that the pattern is not NULL, is NULL-terminated, and
+                * does not contain a NULL before the last byte.
+                */
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern */
+       offset += tracepoint_comm->pattern_len;
+
+       if (!tracepoint_comm->filter_expression_len) {
+               goto skip_filter_expression;
+       }
+
+       /* Map the filter_expression */
+       current_view = lttng_buffer_view_from_view(view, offset, tracepoint_comm->filter_expression_len);
+       filter_expression = current_view.data;
+       if (!filter_expression) {
+               ret = -1;
+               goto end;
+       }
+
+       if (tracepoint_comm->filter_expression_len == 1 ||
+                       filter_expression[tracepoint_comm->filter_expression_len -
+                                         1] != '\0' ||
+                       strlen(filter_expression) !=
+                                       tracepoint_comm->filter_expression_len -
+                                                       1) {
+               /*
+                * Check that the filter expression is not NULL, is
+                * NULL-terminated, and does not contain a NULL before the last
+                * byte.
+                */
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern */
+       offset += tracepoint_comm->filter_expression_len;
+
+skip_filter_expression:
+
+       if (!tracepoint_comm->exclusions_count) {
+               goto skip_exclusions;
+       }
+
+       exclusions = zmalloc(sizeof(*exclusions) * tracepoint_comm->exclusions_count);
+       if (!exclusions) {
+               ret = -1;
+               goto end;
+       }
+
+       for (int i = 0; i < tracepoint_comm->exclusions_count; i++) {
+               current_view = lttng_buffer_view_from_view(view, offset, sizeof(*exclusion_len));
+               exclusion_len = (typeof(exclusion_len)) current_view.data;
+               if (!exclusion_len) {
+                       ret = -1;
+                       goto end;
+               }
+
+               offset += sizeof(*exclusion_len);
+               current_view = lttng_buffer_view_from_view(view, offset, *exclusion_len);
+               exclusion = current_view.data;
+               if (*exclusion_len == 1 ||
+                               exclusion[*exclusion_len - 1] != '\0' ||
+                               strlen(exclusion) != *exclusion_len - 1) {
+                       /*
+                        * Check that the exclusion is not NULL, is
+                        * NULL-terminated, and does not contain a NULL before
+                        * the last byte.
+                        */
+                       ret = -1;
+                       goto end;
+               }
+               exclusions[i] = exclusion;
+               /* Skip to next exclusion */
+               offset += *exclusion_len;
+       }
+
+skip_exclusions:
+       status = lttng_event_rule_tracepoint_set_pattern(rule, pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule tracepoint pattern");
+               ret = -1;
+               goto end;
+       }
+
+       if (filter_expression) {
+               status = lttng_event_rule_tracepoint_set_filter(
+                               rule, filter_expression);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule tracepoint pattern");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       if (exclusions) {
+               status = lttng_event_rule_tracepoint_set_exclusions(rule,
+                               tracepoint_comm->exclusions_count, exclusions);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule tracepoint exclusions");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       free(exclusions);
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_pattern(
+               struct lttng_event_rule *rule, const char *pattern)
+{
+       char *pattern_copy = NULL;
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !pattern ||
+                       strlen(pattern) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       pattern_copy = strdup(pattern);
+       if (!pattern_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (tracepoint->pattern) {
+               free(tracepoint->pattern);
+       }
+
+       tracepoint->pattern = pattern_copy;
+       pattern_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_pattern(
+               const struct lttng_event_rule *rule, const char **pattern)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       if (!tracepoint->pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *pattern = tracepoint->pattern;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_domain_type(
+               const struct lttng_event_rule *rule,
+               enum lttng_domain_type *type)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !type) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       *type = tracepoint->domain;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter(
+               struct lttng_event_rule *rule, const char *expression)
+{
+       char *expression_copy = NULL;
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       /* TODO: validate that the passed expression is valid */
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !expression ||
+                       strlen(expression) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_tracepoint, parent);
+       expression_copy = strdup(expression);
+       if (!expression_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (tracepoint->filter_expression) {
+               free(tracepoint->filter_expression);
+       }
+
+       tracepoint->filter_expression = expression_copy;
+       expression_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter(
+               const struct lttng_event_rule *rule, const char **expression)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !expression) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       if (!tracepoint->filter_expression) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *expression = tracepoint->filter_expression;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel(
+               struct lttng_event_rule *rule, int level)
+{      
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       /*
+        * TODO/QUESTION: do we validate the passed level based on the domain?
+        * What if no domain is set yet? Should we move the domain to the
+        * "create" api call to enforce the domain type?
+        */
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       tracepoint->loglevel.value = level;
+       tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_SINGLE;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel_range(
+               struct lttng_event_rule *rule, int level)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       /*
+        * TODO/QUESTION: do we validate the passed level based on the domain?
+        * What if no domain is set yet? Should we move the domain to the
+        * "create" api call to enforce the domain type?
+        */
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       tracepoint->loglevel.value = level;
+       tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_RANGE;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel_all(
+               struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_loglevel_type(
+               const struct lttng_event_rule *rule, enum lttng_loglevel_type *type)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !type) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       *type = tracepoint->loglevel.type;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_loglevel(
+               const struct lttng_event_rule *rule, int *level)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !level) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       if (tracepoint->loglevel.type == LTTNG_EVENT_LOGLEVEL_ALL) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+       *level = tracepoint->loglevel.value;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_exclusions(
+               struct lttng_event_rule *rule,
+               unsigned int count,
+               const char **exclusions)
+{
+       char **exclusions_copy = NULL;
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+       enum lttng_domain_type domain_type;
+
+       /* TODO: validate that the passed exclusions are valid? */
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || count == 0 ||
+                       !exclusions) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_tracepoint, parent);
+
+       status = lttng_event_rule_tracepoint_get_domain_type(rule, &domain_type);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               goto end;
+       }
+
+       switch (domain_type) {
+       case LTTNG_DOMAIN_KERNEL:
+       case LTTNG_DOMAIN_JUL:
+       case LTTNG_DOMAIN_LOG4J:
+       case LTTNG_DOMAIN_PYTHON:
+               status = LTTNG_EVENT_RULE_STATUS_UNSUPPORTED;
+               goto end;
+       case LTTNG_DOMAIN_UST:
+               /* Exclusions supported */
+               break;
+       default:
+               assert(0);
+       }
+
+       exclusions_copy = zmalloc(sizeof(char *) * count);
+       if (!exclusions_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Perform the copy locally */
+       for (int i = 0; i < count; i++) {
+               exclusions_copy[i] = strdup(exclusions[i]);
+               if (!exclusions_copy[i]) {
+                       status = LTTNG_EVENT_RULE_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       if (tracepoint->exclusions.count != 0) {
+               for (int i = 0; i < tracepoint->exclusions.count; i++) {
+                       free(tracepoint->exclusions.values[i]);
+               }
+               free(tracepoint->exclusions.values);
+       }
+
+       tracepoint->exclusions.values = exclusions_copy;
+       tracepoint->exclusions.count = count;
+       exclusions_copy = NULL;
+end:
+       if (exclusions_copy) {
+               for (int i = 0; i < count; i++) {
+                       free(exclusions_copy[i]);
+               }
+               free(exclusions_copy);
+       }
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_exclusions_count(
+               const struct lttng_event_rule *rule, unsigned int *count)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !count) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       *count = tracepoint->exclusions.count;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_exclusion_at_index(
+               const struct lttng_event_rule *rule,
+               unsigned int index,
+               const char **exclusion)
+{
+       struct lttng_event_rule_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !exclusion) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(rule, struct lttng_event_rule_tracepoint,
+                       parent);
+       if (index >= tracepoint->exclusions.count) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+       *exclusion = tracepoint->exclusions.values[index];
+end:
+       return status;
+}
diff --git a/src/common/event-rule-uprobe.c b/src/common/event-rule-uprobe.c
new file mode 100644 (file)
index 0000000..5f9a5e3
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
+#include <lttng/userspace-probe-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/runas.h>
+#include <assert.h>
+
+#define IS_UPROBE_EVENT_RULE(rule) ( \
+       lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE \
+       )
+
+static
+void lttng_event_rule_uprobe_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_uprobe *uprobe;
+
+       uprobe = container_of(rule, struct lttng_event_rule_uprobe,
+                       parent);
+
+       lttng_userspace_probe_location_destroy(uprobe->location);
+       free(uprobe->name);
+       free(uprobe);
+}
+
+static
+bool lttng_event_rule_uprobe_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_uprobe *uprobe;
+
+       if (!rule) {
+               goto end;
+       }
+
+       uprobe = container_of(rule, struct lttng_event_rule_uprobe,
+                       parent);
+
+       /* Required field */
+       if (!uprobe->name) {
+               ERR("Invalid uprobe event rule: a pattern must be set.");
+               goto end;
+       }
+
+       if (!uprobe->location) {
+               ERR("Invalid uprobe event rule: a location must be set.");
+               goto end;
+       }
+
+       /* TODO should we validate the probe location? */
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_event_rule_uprobe_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       size_t name_len, header_offset, size_before_probe;
+       struct lttng_event_rule_uprobe *uprobe;
+       struct lttng_event_rule_uprobe_comm uprobe_comm = { 0 };
+       struct lttng_event_rule_uprobe_comm *header;
+
+       if (!rule || !IS_UPROBE_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       header_offset = buf->size;
+
+       DBG("Serializing uprobe event rule");
+       uprobe = container_of(
+                       rule, struct lttng_event_rule_uprobe, parent);
+
+       name_len = strlen(uprobe->name) + 1;
+
+       uprobe_comm.name_len = name_len;
+
+       ret = lttng_dynamic_buffer_append(
+                       buf, &uprobe_comm, sizeof(uprobe_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buf, uprobe->name,
+                       name_len);
+       if (ret) {
+               goto end;
+       }
+
+       size_before_probe = buf->size;
+
+       /* This serialize return the size taken in the buffer */
+       /* TODO: should all serialize standardise on this? */
+       ret = lttng_userspace_probe_location_serialize(
+                       uprobe->location, buf, fd_to_send);
+       if (ret < 0) {
+               goto end;
+       }
+
+       /* Update the header regarding the probe size */
+       header = (struct lttng_event_rule_uprobe_comm *) ((char *) buf->data + header_offset);
+       header->location_len = buf->size - size_before_probe;
+
+       ret = 0;
+
+end:
+       return ret;
+}
+
+static
+bool lttng_event_rule_uprobe_is_equal(const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_uprobe *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_uprobe, parent);
+       b = container_of(_b, struct lttng_event_rule_uprobe, parent);
+
+       /* uprobe is invalid if this is not true */
+       assert(a->name);
+       assert(b->name);
+       if (strcmp(a->name, b->name)) {
+               goto end;
+       }
+
+       assert(a->location);
+       assert(b->location);
+       is_equal = lttng_userspace_probe_location_is_equal(a->location, b->location);
+end:
+       return is_equal;
+}
+
+static
+enum lttng_error_code lttng_event_rule_uprobe_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+       /* Nothing to do */
+       return LTTNG_OK;
+}
+
+static const char *lttng_event_rule_uprobe_get_filter(
+               const struct lttng_event_rule *rule)
+{
+       /* Unsupported */
+       return NULL;
+}
+
+static const struct lttng_filter_bytecode *
+lttng_event_rule_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
+{
+       /* Unsupported */
+       return NULL;
+}
+
+static
+struct lttng_event_exclusion *lttng_event_rule_uprobe_generate_exclusions(struct lttng_event_rule *rule)
+{
+       /* Unsupported */
+       return NULL;
+}
+
+struct lttng_event_rule *lttng_event_rule_uprobe_create()
+{
+       struct lttng_event_rule_uprobe *rule;
+
+       rule = zmalloc(sizeof(struct lttng_event_rule_uprobe));
+       if (!rule) {
+               return NULL;
+       }
+
+       lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_UPROBE);
+       rule->parent.validate = lttng_event_rule_uprobe_validate;
+       rule->parent.serialize = lttng_event_rule_uprobe_serialize;
+       rule->parent.equal = lttng_event_rule_uprobe_is_equal;
+       rule->parent.destroy = lttng_event_rule_uprobe_destroy;
+       rule->parent.populate = lttng_event_rule_uprobe_populate;
+       rule->parent.get_filter = lttng_event_rule_uprobe_get_filter;
+       rule->parent.get_filter_bytecode = lttng_event_rule_uprobe_get_filter_bytecode;
+       rule->parent.generate_exclusions = lttng_event_rule_uprobe_generate_exclusions;
+       return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_uprobe_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       const struct lttng_event_rule_uprobe_comm *uprobe_comm;
+       const char *name;
+       struct lttng_buffer_view current_view;
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_userspace_probe_location *location;
+       struct lttng_event_rule_uprobe *uprobe;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       if (view->size < sizeof(*uprobe_comm)) {
+               ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       current_view = lttng_buffer_view_from_view(view, offset, sizeof(*uprobe_comm));
+       uprobe_comm = (typeof(uprobe_comm)) current_view.data;
+
+       if(!uprobe_comm) {
+               ret = -1;
+               goto end;
+       }
+
+       rule = lttng_event_rule_uprobe_create();
+       if (!rule) {
+               ERR("Failed to create event rule uprobe");
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip to payload */
+       offset += current_view.size;
+
+       /* Map the name */
+       current_view = lttng_buffer_view_from_view(view, offset, uprobe_comm->name_len);
+       name = current_view.data;
+       if (!name) {
+               ret = -1;
+               goto end;
+       }
+
+       if (uprobe_comm->name_len == 1 ||
+                       name[uprobe_comm->name_len - 1] != '\0' ||
+                       strlen(name) != uprobe_comm->name_len - 1) {
+               /*
+                * Check that the name is not NULL, is NULL-terminated, and
+                * does not contain a NULL before the last byte.
+                */
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the name */
+       offset += uprobe_comm->name_len;
+
+       /* Map the location */
+       current_view = lttng_buffer_view_from_view(view, offset, uprobe_comm->location_len);
+       ret = lttng_userspace_probe_location_create_from_buffer(&current_view, &location);
+       if (ret < 0) {
+               ret = -1;
+               goto end;
+       }
+
+       assert(ret == uprobe_comm->location_len);
+
+       /* Skip after the location */
+       offset += uprobe_comm->location_len;
+
+       uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent);
+       uprobe->location = location;
+
+       (void) lttng_event_rule_uprobe_set_name(rule, name);
+
+       if (!lttng_event_rule_uprobe_validate(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_uprobe_set_location(
+               struct lttng_event_rule *rule,
+               const struct lttng_userspace_probe_location *location)
+{
+       struct lttng_userspace_probe_location *location_copy = NULL;
+       struct lttng_event_rule_uprobe *uprobe;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       uprobe = container_of(rule, struct lttng_event_rule_uprobe,
+                       parent);
+       location_copy = lttng_userspace_probe_location_copy(location);
+       if (!location_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (uprobe->location) {
+               lttng_userspace_probe_location_destroy(uprobe->location);
+       }
+
+       uprobe->location = location_copy;
+       location_copy = NULL;
+end:
+       lttng_userspace_probe_location_destroy(location_copy);
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_uprobe_get_location(
+               const struct lttng_event_rule *rule,
+               const struct lttng_userspace_probe_location **location)
+{
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       *location = lttng_event_rule_uprobe_get_location_no_const(rule);
+       if (!*location) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+end:
+       return status;
+
+}
+
+LTTNG_HIDDEN
+struct lttng_userspace_probe_location *
+lttng_event_rule_uprobe_get_location_no_const(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_uprobe *uprobe;
+       assert(rule);
+       uprobe = container_of(rule, struct lttng_event_rule_uprobe,
+                       parent);
+
+       return uprobe->location;
+}
+
+enum lttng_event_rule_status lttng_event_rule_uprobe_set_name(
+               struct lttng_event_rule *rule, const char *name)
+{
+       char *name_copy = NULL;
+       struct lttng_event_rule_uprobe *uprobe;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name ||
+                       strlen(name) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       uprobe = container_of(rule, struct lttng_event_rule_uprobe,
+                       parent);
+       name_copy = strdup(name);
+       if (!name_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (uprobe->name) {
+               free(uprobe->name);
+       }
+
+       uprobe->name = name_copy;
+       name_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_uprobe_get_name(
+               const struct lttng_event_rule *rule, const char **name)
+{
+       struct lttng_event_rule_uprobe *uprobe;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       uprobe = container_of(rule, struct lttng_event_rule_uprobe,
+                       parent);
+       if (!uprobe->name) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *name = uprobe->name;
+end:
+       return status;
+}
diff --git a/src/common/event-rule.c b/src/common/event-rule.c
new file mode 100644 (file)
index 0000000..57bd68b
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2019 - 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, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kprobe-internal.h>
+#include <lttng/event-rule/kretprobe-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
+#include <lttng/event-rule/syscall-internal.h>
+#include <lttng/event-rule/tracepoint-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+#include <stdbool.h>
+#include <assert.h>
+
+enum lttng_event_rule_type lttng_event_rule_get_type(
+               const struct lttng_event_rule *event_rule)
+{
+       return event_rule ? event_rule->type : LTTNG_EVENT_RULE_TYPE_UNKNOWN;
+}
+
+LTTNG_HIDDEN
+enum lttng_domain_type lttng_event_rule_get_domain_type(
+               const struct lttng_event_rule *event_rule)
+{
+       enum lttng_domain_type domain_type;
+
+       switch (lttng_event_rule_get_type(event_rule)) {
+       case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+               (void) lttng_event_rule_tracepoint_get_domain_type(event_rule, &domain_type);
+               break;
+       case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+       case LTTNG_EVENT_RULE_TYPE_KPROBE:
+       case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+       case LTTNG_EVENT_RULE_TYPE_UPROBE:
+               domain_type = LTTNG_DOMAIN_KERNEL;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
+               domain_type = LTTNG_DOMAIN_NONE;
+               break;
+       }
+
+       return domain_type;
+}
+
+static
+void lttng_event_rule_release(struct urcu_ref *ref)
+{
+
+       struct lttng_event_rule *event_rule = container_of(ref, typeof(*event_rule),
+                       ref);
+
+       assert(event_rule->destroy);
+       event_rule->destroy(event_rule);
+}
+
+void lttng_event_rule_destroy(struct lttng_event_rule *event_rule)
+{
+       lttng_event_rule_put(event_rule);
+}
+
+LTTNG_HIDDEN
+bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule)
+{
+       bool valid;
+
+       if (!event_rule) {
+               valid = false;
+               goto end;
+       }
+
+       if (!event_rule->validate) {
+               /* Sub-class guarantees that it can never be invalid. */
+               valid = true;
+               goto end;
+       }
+
+       valid = event_rule->validate(event_rule);
+end:
+       return valid;
+}
+
+LTTNG_HIDDEN
+int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
+{
+       int ret;
+       struct lttng_event_rule_comm event_rule_comm = { 0 };
+
+       if (!event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       event_rule_comm.event_rule_type = (int8_t) event_rule->type;
+
+       ret = lttng_dynamic_buffer_append(buf, &event_rule_comm,
+                       sizeof(event_rule_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = event_rule->serialize(event_rule, buf, fd_to_send);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+bool lttng_event_rule_is_equal(const struct lttng_event_rule *a,
+               const struct lttng_event_rule *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       is_equal = a->equal ? a->equal(a, b) : true;
+end:
+       return is_equal;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_create_from_buffer(
+               const struct lttng_buffer_view *buffer,
+               struct lttng_event_rule **event_rule)
+{
+       ssize_t ret, event_rule_size = 0;
+       const struct lttng_event_rule_comm *event_rule_comm;
+       event_rule_create_from_buffer_cb create_from_buffer = NULL;
+
+       if (!buffer || !event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Deserializing event_rule from buffer");
+       event_rule_comm = (const struct lttng_event_rule_comm *) buffer->data;
+       event_rule_size += sizeof(*event_rule_comm);
+
+       switch ((enum lttng_event_rule_type) event_rule_comm->event_rule_type) {
+       case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+               create_from_buffer = lttng_event_rule_tracepoint_create_from_buffer;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_KPROBE:
+               create_from_buffer = lttng_event_rule_kprobe_create_from_buffer;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+               create_from_buffer = lttng_event_rule_kretprobe_create_from_buffer;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_UPROBE:
+               create_from_buffer = lttng_event_rule_uprobe_create_from_buffer;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+               create_from_buffer = lttng_event_rule_syscall_create_from_buffer;
+               break;
+       default:
+               ERR("Attempted to create event rule of unknown type (%i)",
+                               (int) event_rule_comm->event_rule_type);
+               ret = -1;
+               goto end;
+       }
+
+       if (create_from_buffer) {
+               const struct lttng_buffer_view view =
+                               lttng_buffer_view_from_view(buffer,
+                                       sizeof(*event_rule_comm), -1);
+
+               ret = create_from_buffer(&view, event_rule);
+               if (ret < 0) {
+                       goto end;
+               }
+               event_rule_size += ret;
+
+       } else {
+               abort();
+       }
+
+       ret = event_rule_size;
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+void lttng_event_rule_init(struct lttng_event_rule *event_rule,
+               enum lttng_event_rule_type type)
+{
+       urcu_ref_init(&event_rule->ref);
+       event_rule->type = type;
+}
+
+LTTNG_HIDDEN
+bool lttng_event_rule_get(struct lttng_event_rule *event_rule)
+{
+       return urcu_ref_get_unless_zero(&event_rule->ref);
+}
+
+LTTNG_HIDDEN
+void lttng_event_rule_put(struct lttng_event_rule *event_rule)
+{
+       if (!event_rule) {
+               return;
+       }
+       assert(event_rule->ref.refcount);
+       urcu_ref_put(&event_rule->ref, lttng_event_rule_release);
+}
+
+LTTNG_HIDDEN
+enum lttng_error_code lttng_event_rule_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+       assert(rule->populate);
+       return rule->populate(rule, uid, gid);
+}
+
+/* If not present return NULL
+ * Caller DO NOT own the returned object
+ */
+LTTNG_HIDDEN
+const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule)
+{
+       assert(rule->get_filter);
+       return rule->get_filter(rule);
+}
+
+/* If not present return NULL
+ * Caller DO NOT own the returned object
+ */
+LTTNG_HIDDEN
+const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       assert(rule->get_filter_bytecode);
+       return rule->get_filter_bytecode(rule);
+}
+
+/*
+ * If not present return NULL
+ * Caller OWN the returned object
+ * TODO: should this be done another way?
+ */
+LTTNG_HIDDEN
+struct lttng_event_exclusion *lttng_event_rule_generate_exclusions(
+               struct lttng_event_rule *rule)
+{
+       assert(rule->generate_exclusions);
+       return rule->generate_exclusions(rule);
+}
+
+LTTNG_HIDDEN
+struct lttng_event *lttng_event_rule_generate_lttng_event(
+               const struct lttng_event_rule *rule)
+{
+       assert(rule->generate_lttng_event);
+       return rule->generate_lttng_event(rule);
+}
+
+LTTNG_HIDDEN
+bool lttng_event_rule_is_agent(const struct lttng_event_rule *rule)
+{
+       bool ret = false;
+       enum lttng_domain_type type = lttng_event_rule_get_domain_type(rule);
+
+       switch (type) {
+       case LTTNG_DOMAIN_JUL:
+       case LTTNG_DOMAIN_LOG4J:
+       case LTTNG_DOMAIN_PYTHON:
+               ret = true;
+               break;
+       case LTTNG_DOMAIN_UST:
+       case LTTNG_DOMAIN_KERNEL:
+               ret = false;
+               break;
+       default:
+               assert(0);
+       };
+
+       return ret;
+}
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_type_str(enum lttng_event_rule_type type)
+{
+       switch (type) {
+       case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
+               return "unknown";
+
+       case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+               return "tracepoint";
+
+       case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+               return "syscall";
+
+       case LTTNG_EVENT_RULE_TYPE_KPROBE:
+               return "probe";
+
+       case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+               return "function";
+
+       case LTTNG_EVENT_RULE_TYPE_UPROBE:
+               return "userspace-probe";
+
+       default:
+               abort();
+       }
+}
diff --git a/src/common/filter/Makefile.am b/src/common/filter/Makefile.am
new file mode 100644 (file)
index 0000000..ca85cda
--- /dev/null
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+AM_CPPFLAGS += -I$(srcdir) -I$(builddir)
+
+noinst_PROGRAMS = filter-grammar-test
+noinst_LTLIBRARIES = libfilter.la
+noinst_HEADERS = filter-ast.h \
+               filter-symbols.h
+
+BUILT_SOURCES = filter-parser.h
+
+libfilter_la_SOURCES = \
+       filter-parser.y filter-lexer.l \
+       filter-visitor-xml.c \
+       filter-visitor-generate-ir.c \
+       filter-visitor-ir-check-binary-op-nesting.c \
+       filter-visitor-ir-validate-string.c \
+       filter-visitor-ir-validate-globbing.c \
+       filter-visitor-ir-normalize-glob-patterns.c \
+       filter-visitor-generate-bytecode.c \
+       filter-ast.h \
+       filter-bytecode.h \
+       filter-ir.h \
+       memstream.h
+libfilter_la_CFLAGS = -include filter-symbols.h $(AM_CFLAGS)
+libfilter_la_LIBADD = $(top_builddir)/src/common/string-utils/libstring-utils.la
+
+AM_YFLAGS = -t -d -v
+
+# start with empty files to clean
+CLEANFILES =
+
+if HAVE_BISON
+# we have bison: we can clean the generated parser files
+CLEANFILES += filter-parser.c filter-parser.h filter-parser.output
+else # HAVE_BISON
+# create target used to stop the build if we want to build the parser,
+# but we don't have the necessary tool to do so
+ERR_MSG = "Error: Cannot build target because bison is missing."
+ERR_MSG += "Make sure bison is installed and run the configure script again."
+
+filter-parser.c filter-parser.h: filter-parser.y
+       @echo $(ERR_MSG)
+       @false
+
+all-local: filter-parser.c filter-parser.h
+endif # HAVE_BISON
+
+if HAVE_FLEX
+# we have flex: we can clean the generated lexer files
+CLEANFILES += filter-lexer.c
+else # HAVE_FLEX
+# create target used to stop the build if we want to build the lexer,
+# but we don't have the necessary tool to do so
+ERR_MSG = "Error: Cannot build target because flex is missing."
+ERR_MSG += "Make sure flex is installed and run the configure script again."
+
+filter-lexer.c: filter-lexer.l
+       @echo $(ERR_MSG)
+       @false
+
+all-local: filter-lexer.c
+endif # HAVE_FLEX
+
+filter_grammar_test_SOURCES = filter-grammar-test.c
+filter_grammar_test_LDADD = libfilter.la
diff --git a/src/common/filter/filter-ast.h b/src/common/filter/filter-ast.h
new file mode 100644 (file)
index 0000000..29fde10
--- /dev/null
@@ -0,0 +1,188 @@
+#ifndef _FILTER_AST_H
+#define _FILTER_AST_H
+
+/*
+ * filter-ast.h
+ *
+ * LTTng filter AST
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+/*
+ * Note: filter-ast.h should be included before filter-parser.h.
+ */
+
+#include <urcu/list.h>
+#include <stdint.h>
+
+#define printf_debug(fmt, args...)                                     \
+       do {                                                            \
+               if (filter_parser_debug)                                \
+                       fprintf(stdout, "[debug] " fmt, ## args);       \
+       } while (0)
+
+// the parameter name (of the reentrant 'yyparse' function)
+// data is a pointer to a 'SParserParam' structure
+//#define YYPARSE_PARAM        parser_ctx
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+extern int filter_parser_debug;
+
+struct filter_node;
+struct filter_parser;
+
+enum node_type {
+       NODE_UNKNOWN = 0,
+       NODE_ROOT,
+
+       NODE_EXPRESSION,
+       NODE_OP,
+       NODE_UNARY_OP,
+
+       NR_NODE_TYPES,
+};
+
+enum op_type {
+       AST_OP_UNKNOWN = 0,
+       AST_OP_MUL,
+       AST_OP_DIV,
+       AST_OP_MOD,
+       AST_OP_PLUS,
+       AST_OP_MINUS,
+       AST_OP_BIT_RSHIFT,
+       AST_OP_BIT_LSHIFT,
+       AST_OP_AND,
+       AST_OP_OR,
+       AST_OP_BIT_AND,
+       AST_OP_BIT_OR,
+       AST_OP_BIT_XOR,
+
+       AST_OP_EQ,
+       AST_OP_NE,
+       AST_OP_GT,
+       AST_OP_LT,
+       AST_OP_GE,
+       AST_OP_LE,
+};
+
+enum unary_op_type {
+       AST_UNARY_UNKNOWN = 0,
+       AST_UNARY_PLUS,
+       AST_UNARY_MINUS,
+       AST_UNARY_NOT,
+       AST_UNARY_BIT_NOT,
+};
+
+enum ast_link_type {
+       AST_LINK_UNKNOWN = 0,
+       AST_LINK_DOT,
+       AST_LINK_RARROW,
+       AST_LINK_BRACKET,
+};
+
+struct filter_node {
+       /*
+        * Parent node is only set on demand by specific visitor.
+        */
+       struct filter_node *parent;
+       struct cds_list_head gc;
+
+       enum node_type type;
+       union {
+               struct {
+               } unknown;
+               struct {
+                       struct filter_node *child;
+               } root;
+               struct {
+                       enum {
+                               AST_EXP_UNKNOWN = 0,
+                               AST_EXP_STRING,
+                               AST_EXP_CONSTANT,
+                               AST_EXP_FLOAT_CONSTANT,
+                               AST_EXP_IDENTIFIER,
+                               AST_EXP_GLOBAL_IDENTIFIER,
+                               AST_EXP_NESTED,
+                       } type;
+                       enum ast_link_type post_op;     /* reverse */
+                       enum ast_link_type pre_op;      /* forward */
+                       union {
+                               const char *string;
+                               uint64_t constant;
+                               double float_constant;
+                               const char *identifier;
+                               /*
+                                * child can be nested.
+                                */
+                               struct filter_node *child;
+                       } u;
+                       /* prev: backward dot/arrow chain (postfix expression) */
+                       struct filter_node *prev;
+                       /* next: forward dot/arrow chain, generated by a visitor. */
+                       struct filter_node *next;
+                       /* next_bracket: linked bracket chain (prefix expression) */
+                       struct filter_node *next_bracket;
+               } expression;
+               struct {
+                       enum op_type type;
+                       struct filter_node *lchild;
+                       struct filter_node *rchild;
+               } op;
+               struct {
+                       enum unary_op_type type;
+                       struct filter_node *child;
+               } unary_op;
+       } u;
+};
+
+struct filter_ast {
+       struct filter_node root;
+       struct cds_list_head allocated_nodes;
+};
+
+const char *node_type(struct filter_node *node);
+
+struct ir_op;
+
+struct filter_parser_ctx {
+       yyscan_t scanner;
+       struct filter_ast *ast;
+       struct cds_list_head allocated_strings;
+       struct ir_op *ir_root;
+       struct lttng_filter_bytecode_alloc *bytecode;
+       struct lttng_filter_bytecode_alloc *bytecode_reloc;
+};
+
+struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input);
+void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx);
+int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx);
+int filter_parser_ctx_create_from_filter_expression(
+               const char *filter_expression, struct filter_parser_ctx **ctxp);
+
+static inline
+struct filter_ast *filter_parser_get_ast(struct filter_parser_ctx *parser_ctx)
+{
+       return parser_ctx->ast;
+}
+
+int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream,
+                       int indent);
+int filter_visitor_ir_generate(struct filter_parser_ctx *ctx);
+void filter_ir_free(struct filter_parser_ctx *ctx);
+int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx);
+void filter_bytecode_free(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx);
+
+#endif /* _FILTER_AST_H */
diff --git a/src/common/filter/filter-bytecode.h b/src/common/filter/filter-bytecode.h
new file mode 100644 (file)
index 0000000..053bb08
--- /dev/null
@@ -0,0 +1,243 @@
+#ifndef _FILTER_BYTECODE_H
+#define _FILTER_BYTECODE_H
+
+/*
+ * filter-bytecode.h
+ *
+ * LTTng filter bytecode
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/macros.h>
+
+#include "filter-ast.h"
+
+/*
+ * offsets are absolute from start of bytecode.
+ */
+
+struct field_ref {
+       /* Initially, symbol offset. After link, field offset. */
+       uint16_t offset;
+} LTTNG_PACKED;
+
+struct get_symbol {
+       /* Symbol offset. */
+       uint16_t offset;
+} LTTNG_PACKED;
+
+struct get_index_u16 {
+       uint16_t index;
+} LTTNG_PACKED;
+
+struct get_index_u64 {
+       uint64_t index;
+} LTTNG_PACKED;
+
+struct literal_numeric {
+       int64_t v;
+} LTTNG_PACKED;
+
+struct literal_double {
+       double v;
+} LTTNG_PACKED;
+
+struct literal_string {
+       char string[0];
+} LTTNG_PACKED;
+
+enum filter_op {
+       FILTER_OP_UNKNOWN                       = 0,
+
+       FILTER_OP_RETURN                        = 1,
+
+       /* binary */
+       FILTER_OP_MUL                           = 2,
+       FILTER_OP_DIV                           = 3,
+       FILTER_OP_MOD                           = 4,
+       FILTER_OP_PLUS                          = 5,
+       FILTER_OP_MINUS                         = 6,
+       FILTER_OP_BIT_RSHIFT                    = 7,
+       FILTER_OP_BIT_LSHIFT                    = 8,
+       FILTER_OP_BIT_AND                       = 9,
+       FILTER_OP_BIT_OR                        = 10,
+       FILTER_OP_BIT_XOR                       = 11,
+
+       /* binary comparators */
+       FILTER_OP_EQ                            = 12,
+       FILTER_OP_NE                            = 13,
+       FILTER_OP_GT                            = 14,
+       FILTER_OP_LT                            = 15,
+       FILTER_OP_GE                            = 16,
+       FILTER_OP_LE                            = 17,
+
+       /* string binary comparator: apply to  */
+       FILTER_OP_EQ_STRING                     = 18,
+       FILTER_OP_NE_STRING                     = 19,
+       FILTER_OP_GT_STRING                     = 20,
+       FILTER_OP_LT_STRING                     = 21,
+       FILTER_OP_GE_STRING                     = 22,
+       FILTER_OP_LE_STRING                     = 23,
+
+       /* s64 binary comparator */
+       FILTER_OP_EQ_S64                        = 24,
+       FILTER_OP_NE_S64                        = 25,
+       FILTER_OP_GT_S64                        = 26,
+       FILTER_OP_LT_S64                        = 27,
+       FILTER_OP_GE_S64                        = 28,
+       FILTER_OP_LE_S64                        = 29,
+
+       /* double binary comparator */
+       FILTER_OP_EQ_DOUBLE                     = 30,
+       FILTER_OP_NE_DOUBLE                     = 31,
+       FILTER_OP_GT_DOUBLE                     = 32,
+       FILTER_OP_LT_DOUBLE                     = 33,
+       FILTER_OP_GE_DOUBLE                     = 34,
+       FILTER_OP_LE_DOUBLE                     = 35,
+
+       /* Mixed S64-double binary comparators */
+       FILTER_OP_EQ_DOUBLE_S64                 = 36,
+       FILTER_OP_NE_DOUBLE_S64                 = 37,
+       FILTER_OP_GT_DOUBLE_S64                 = 38,
+       FILTER_OP_LT_DOUBLE_S64                 = 39,
+       FILTER_OP_GE_DOUBLE_S64                 = 40,
+       FILTER_OP_LE_DOUBLE_S64                 = 41,
+
+       FILTER_OP_EQ_S64_DOUBLE                 = 42,
+       FILTER_OP_NE_S64_DOUBLE                 = 43,
+       FILTER_OP_GT_S64_DOUBLE                 = 44,
+       FILTER_OP_LT_S64_DOUBLE                 = 45,
+       FILTER_OP_GE_S64_DOUBLE                 = 46,
+       FILTER_OP_LE_S64_DOUBLE                 = 47,
+
+       /* unary */
+       FILTER_OP_UNARY_PLUS                    = 48,
+       FILTER_OP_UNARY_MINUS                   = 49,
+       FILTER_OP_UNARY_NOT                     = 50,
+       FILTER_OP_UNARY_PLUS_S64                = 51,
+       FILTER_OP_UNARY_MINUS_S64               = 52,
+       FILTER_OP_UNARY_NOT_S64                 = 53,
+       FILTER_OP_UNARY_PLUS_DOUBLE             = 54,
+       FILTER_OP_UNARY_MINUS_DOUBLE            = 55,
+       FILTER_OP_UNARY_NOT_DOUBLE              = 56,
+
+       /* logical */
+       FILTER_OP_AND                           = 57,
+       FILTER_OP_OR                            = 58,
+
+       /* load field ref */
+       FILTER_OP_LOAD_FIELD_REF                = 59,
+       FILTER_OP_LOAD_FIELD_REF_STRING         = 60,
+       FILTER_OP_LOAD_FIELD_REF_SEQUENCE       = 61,
+       FILTER_OP_LOAD_FIELD_REF_S64            = 62,
+       FILTER_OP_LOAD_FIELD_REF_DOUBLE         = 63,
+
+       /* load immediate from operand */
+       FILTER_OP_LOAD_STRING                   = 64,
+       FILTER_OP_LOAD_S64                      = 65,
+       FILTER_OP_LOAD_DOUBLE                   = 66,
+
+       /* cast */
+       FILTER_OP_CAST_TO_S64                   = 67,
+       FILTER_OP_CAST_DOUBLE_TO_S64            = 68,
+       FILTER_OP_CAST_NOP                      = 69,
+
+       /* get context ref */
+       FILTER_OP_GET_CONTEXT_REF               = 70,
+       FILTER_OP_GET_CONTEXT_REF_STRING        = 71,
+       FILTER_OP_GET_CONTEXT_REF_S64           = 72,
+       FILTER_OP_GET_CONTEXT_REF_DOUBLE        = 73,
+
+       /* load userspace field ref */
+       FILTER_OP_LOAD_FIELD_REF_USER_STRING    = 74,
+       FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE  = 75,
+
+       /*
+        * load immediate star globbing pattern (literal string)
+        * from immediate
+        */
+       FILTER_OP_LOAD_STAR_GLOB_STRING         = 76,
+
+       /* globbing pattern binary operator: apply to */
+       FILTER_OP_EQ_STAR_GLOB_STRING           = 77,
+       FILTER_OP_NE_STAR_GLOB_STRING           = 78,
+
+       /*
+        * Instructions for recursive traversal through composed types.
+        */
+       FILTER_OP_GET_CONTEXT_ROOT              = 79,
+       FILTER_OP_GET_APP_CONTEXT_ROOT          = 80,
+       FILTER_OP_GET_PAYLOAD_ROOT              = 81,
+
+       FILTER_OP_GET_SYMBOL                    = 82,
+       FILTER_OP_GET_SYMBOL_FIELD              = 83,
+       FILTER_OP_GET_INDEX_U16                 = 84,
+       FILTER_OP_GET_INDEX_U64                 = 85,
+
+       FILTER_OP_LOAD_FIELD                    = 86,
+       FILTER_OP_LOAD_FIELD_S8                 = 87,
+       FILTER_OP_LOAD_FIELD_S16                = 88,
+       FILTER_OP_LOAD_FIELD_S32                = 89,
+       FILTER_OP_LOAD_FIELD_S64                = 90,
+       FILTER_OP_LOAD_FIELD_U8                 = 91,
+       FILTER_OP_LOAD_FIELD_U16                = 92,
+       FILTER_OP_LOAD_FIELD_U32                = 93,
+       FILTER_OP_LOAD_FIELD_U64                = 94,
+       FILTER_OP_LOAD_FIELD_STRING             = 95,
+       FILTER_OP_LOAD_FIELD_SEQUENCE           = 96,
+       FILTER_OP_LOAD_FIELD_DOUBLE             = 97,
+
+       FILTER_OP_UNARY_BIT_NOT                 = 98,
+
+       FILTER_OP_RETURN_S64                    = 99,
+
+       NR_FILTER_OPS,
+};
+
+typedef uint8_t filter_opcode_t;
+
+struct load_op {
+       filter_opcode_t op;
+       char data[0];
+       /* data to load. Size known by enum filter_opcode and null-term char. */
+} LTTNG_PACKED;
+
+struct binary_op {
+       filter_opcode_t op;
+} LTTNG_PACKED;
+
+struct unary_op {
+       filter_opcode_t op;
+} LTTNG_PACKED;
+
+/* skip_offset is absolute from start of bytecode */
+struct logical_op {
+       filter_opcode_t op;
+       uint16_t skip_offset;   /* bytecode insn, if skip second test */
+} LTTNG_PACKED;
+
+struct cast_op {
+       filter_opcode_t op;
+} LTTNG_PACKED;
+
+struct return_op {
+       filter_opcode_t op;
+} LTTNG_PACKED;
+
+struct lttng_filter_bytecode_alloc {
+       uint32_t alloc_len;
+       struct lttng_filter_bytecode b;
+};
+
+static inline
+unsigned int bytecode_get_len(struct lttng_filter_bytecode *bytecode)
+{
+       return bytecode->len;
+}
+
+#endif /* _FILTER_BYTECODE_H */
diff --git a/src/common/filter/filter-grammar-test.c b/src/common/filter/filter-grammar-test.c
new file mode 100644 (file)
index 0000000..2804053
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * filter-grammar-test.c
+ *
+ * LTTng filter grammar test
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-bytecode.h"
+
+int main(int argc, char **argv)
+{
+       struct filter_parser_ctx *ctx;
+       int ret;
+       int print_xml = 0, generate_ir = 0, generate_bytecode = 0,
+               print_bytecode = 0;
+       int i;
+
+       for (i = 1; i < argc; i++) {
+               if (strcmp(argv[i], "-p") == 0)
+                       print_xml = 1;
+               else if (strcmp(argv[i], "-i") == 0)
+                       generate_ir = 1;
+               else if (strcmp(argv[i], "-b") == 0)
+                       generate_bytecode = 1;
+               else if (strcmp(argv[i], "-d") == 0)
+                       filter_parser_debug = 1;
+               else if (strcmp(argv[i], "-B") == 0)
+                       print_bytecode = 1;
+       }
+
+       ctx = filter_parser_ctx_alloc(stdin);
+       if (!ctx) {
+               fprintf(stderr, "Error allocating parser\n");
+               goto alloc_error;
+       }
+       ret = filter_parser_ctx_append_ast(ctx);
+       if (ret) {
+               fprintf(stderr, "Parse error\n");
+               goto parse_error;
+       }
+       if (print_xml) {
+               ret = filter_visitor_print_xml(ctx, stdout, 0);
+               if (ret) {
+                       fflush(stdout);
+                       fprintf(stderr, "XML print error\n");
+                       goto parse_error;
+               }
+       }
+       if (generate_ir) {
+               printf("Generating IR... ");
+               fflush(stdout);
+               ret = filter_visitor_ir_generate(ctx);
+               if (ret) {
+                       fprintf(stderr, "Generate IR error\n");
+                       goto parse_error;
+               }
+               printf("done\n");
+
+               printf("Validating IR... ");
+               fflush(stdout);
+               ret = filter_visitor_ir_check_binary_op_nesting(ctx);
+               if (ret) {
+                       goto parse_error;
+               }
+               printf("done\n");
+       }
+       if (generate_bytecode) {
+               printf("Generating bytecode... ");
+               fflush(stdout);
+               ret = filter_visitor_bytecode_generate(ctx);
+               if (ret) {
+                       fprintf(stderr, "Generate bytecode error\n");
+                       goto parse_error;
+               }
+               printf("done\n");
+               printf("Size of bytecode generated: %u bytes.\n",
+                       bytecode_get_len(&ctx->bytecode->b));
+       }
+
+       if (print_bytecode) {
+               unsigned int bytecode_len, len, i;
+
+               len = bytecode_get_len(&ctx->bytecode->b);
+               bytecode_len = ctx->bytecode->b.reloc_table_offset;
+               printf("Bytecode:\n");
+               for (i = 0; i < bytecode_len; i++) {
+                       printf("0x%X ",
+                               ((uint8_t *) ctx->bytecode->b.data)[i]);
+               }
+               printf("\n");
+               printf("Reloc table:\n");
+               for (i = bytecode_len; i < len;) {
+                       printf("{ 0x%X, ",
+                               *(uint16_t *) &ctx->bytecode->b.data[i]);
+                       i += sizeof(uint16_t);
+                       printf("%s } ", &((char *) ctx->bytecode->b.data)[i]);
+                       i += strlen(&((char *) ctx->bytecode->b.data)[i]) + 1;
+               }
+               printf("\n");
+       }
+
+       filter_bytecode_free(ctx);
+       filter_ir_free(ctx);
+       filter_parser_ctx_free(ctx);
+       return 0;
+
+parse_error:
+       filter_bytecode_free(ctx);
+       filter_ir_free(ctx);
+       filter_parser_ctx_free(ctx);
+alloc_error:
+       exit(EXIT_FAILURE);
+}
diff --git a/src/common/filter/filter-ir.h b/src/common/filter/filter-ir.h
new file mode 100644 (file)
index 0000000..d62c0ee
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef _FILTER_IR_H
+#define _FILTER_IR_H
+
+/*
+ * filter-ir.h
+ *
+ * LTTng filter ir
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "filter-ast.h"
+
+enum ir_op_signedness {
+       IR_SIGN_UNKNOWN = 0,
+       IR_SIGNED,
+       IR_UNSIGNED,
+       IR_SIGN_DYN,            /* signedness determined dynamically */
+};
+
+enum ir_data_type {
+       IR_DATA_UNKNOWN = 0,
+       IR_DATA_STRING,
+       IR_DATA_NUMERIC,        /* numeric and boolean */
+       IR_DATA_FLOAT,
+       IR_DATA_FIELD_REF,
+       IR_DATA_GET_CONTEXT_REF,
+       IR_DATA_EXPRESSION,
+};
+
+enum ir_op_type {
+       IR_OP_UNKNOWN = 0,
+       IR_OP_ROOT,
+       IR_OP_LOAD,
+       IR_OP_UNARY,
+       IR_OP_BINARY,
+       IR_OP_LOGICAL,
+};
+
+/* left or right child */
+enum ir_side {
+       IR_SIDE_UNKNOWN = 0,
+       IR_LEFT,
+       IR_RIGHT,
+};
+
+enum ir_load_string_type {
+       /* Plain, no globbing at all: `hello world`. */
+       IR_LOAD_STRING_TYPE_PLAIN = 0,
+
+       /* Star at the end only: `hello *`. */
+       IR_LOAD_STRING_TYPE_GLOB_STAR_END,
+
+       /* At least one star, anywhere, but not at the end only: `he*wor*`. */
+       IR_LOAD_STRING_TYPE_GLOB_STAR,
+};
+
+struct ir_op_root {
+       struct ir_op *child;
+};
+
+enum ir_load_expression_type {
+       IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT,
+       IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT,
+       IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT,
+       IR_LOAD_EXPRESSION_GET_SYMBOL,
+       IR_LOAD_EXPRESSION_GET_INDEX,
+       IR_LOAD_EXPRESSION_LOAD_FIELD,
+};
+
+struct ir_load_expression_op {
+       struct ir_load_expression_op *next;
+       enum ir_load_expression_type type;
+       union {
+               char *symbol;
+               uint64_t index;
+       } u;
+};
+
+struct ir_load_expression {
+       struct ir_load_expression_op *child;
+};
+
+struct ir_op_load {
+       union {
+               struct {
+                       enum ir_load_string_type type;
+                       char *value;
+               } string;
+               int64_t num;
+               double flt;
+               char *ref;
+               struct ir_load_expression *expression;
+       } u;
+};
+
+struct ir_op_unary {
+       enum unary_op_type type;
+       struct ir_op *child;
+};
+
+struct ir_op_binary {
+       enum op_type type;
+       struct ir_op *left;
+       struct ir_op *right;
+};
+
+struct ir_op_logical {
+       enum op_type type;
+       struct ir_op *left;
+       struct ir_op *right;
+};
+
+struct ir_op {
+       /* common to all ops */
+       enum ir_op_type op;
+       enum ir_data_type data_type;
+       enum ir_op_signedness signedness;
+       enum ir_side side;
+
+       union {
+               struct ir_op_root root;
+               struct ir_op_load load;
+               struct ir_op_unary unary;
+               struct ir_op_binary binary;
+               struct ir_op_logical logical;
+       } u;
+};
+
+#endif /* _FILTER_IR_H */
diff --git a/src/common/filter/filter-lexer.l b/src/common/filter/filter-lexer.l
new file mode 100644 (file)
index 0000000..4ef4519
--- /dev/null
@@ -0,0 +1,132 @@
+%{
+/*
+ * filter-lexer.l
+ *
+ * LTTng filter lexer
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+
+extern
+void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src);
+
+static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+       __attribute__((unused));
+static int input (yyscan_t yyscanner) __attribute__((unused));
+
+%}
+
+%x comment_ml comment_sl string_lit char_const
+%option reentrant yylineno noyywrap bison-bridge
+%option extra-type="struct filter_parser_ctx *"
+       /* bison-locations */
+
+D                              [0-9]
+L                              [a-zA-Z_]
+H                              [a-fA-F0-9]
+E                              ([Ee][+-]?{D}+)
+P                              ([Pp][+-]?{D}+)
+FS                             (f|F|l|L)
+IS                             ((u|U)|(u|U)?(l|L|ll|LL)|(l|L|ll|LL)(u|U))
+
+INTEGER_SUFFIX                 [ \n\t]*(U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu)
+DIGIT                          [0-9]
+NONDIGIT                       [a-zA-Z_]
+HEXDIGIT                       [0-9A-Fa-f]
+OCTALDIGIT                     [0-7]
+UCHARLOWERCASE                 \\u{HEXDIGIT}{4}
+UCHARUPPERCASE                 \\U{HEXDIGIT}{8}
+ID_EXTRA_CHAR                  (":")
+ID_NONDIGIT                    {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}|{ID_EXTRA_CHAR}
+IDENTIFIER                     {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})*
+ESCSEQ                         \\(\'|\"|\?|\\|a|b|f|n|r|t|v|{OCTALDIGIT}{1,3}|u{HEXDIGIT}{4}|U{HEXDIGIT}{8}|x{HEXDIGIT}+)
+%%
+
+                               /*
+                                * Using start conditions to deal with comments
+                                * and strings.
+                                */ 
+
+"/*"                           BEGIN(comment_ml);
+<comment_ml>[^*\n]*            /* eat anything that's not a '*' */
+<comment_ml>"*"+[^*/\n]*       /* eat up '*'s not followed by '/'s */
+<comment_ml>\n                 ++yylineno;
+<comment_ml>"*"+"/"            BEGIN(INITIAL);
+
+"//"                           BEGIN(comment_sl);
+<comment_sl>[^\n]*\n           ++yylineno; BEGIN(INITIAL);
+
+L\'                            BEGIN(char_const); return CHARACTER_CONSTANT_START;
+\'                             BEGIN(char_const); return CHARACTER_CONSTANT_START;
+<char_const>\'                 BEGIN(INITIAL); return SQUOTE;
+
+L\"                            BEGIN(string_lit); return STRING_LITERAL_START;
+\"                             BEGIN(string_lit); return STRING_LITERAL_START;
+<string_lit>\"                 BEGIN(INITIAL); return DQUOTE;
+
+<char_const,string_lit>ESCSEQ  return ESCSEQ;
+<char_const,string_lit>\n      ; /* ignore */
+<char_const,string_lit>.       setstring(yyextra, yylval, yytext); return CHAR_STRING_TOKEN;
+
+
+0[xX]{H}+{IS}?                 setstring(yyextra, yylval, yytext); return HEXADECIMAL_CONSTANT;
+0[0-7]*{IS}?                   setstring(yyextra, yylval, yytext); return OCTAL_CONSTANT;
+[1-9]{D}*{IS}?                 setstring(yyextra, yylval, yytext); return DECIMAL_CONSTANT;
+
+{D}+{E}{FS}?                   setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+{D}*"."{D}+{E}?{FS}?           setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+{D}+"."{D}*{E}?{FS}?           setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+0[xX]{H}+{P}{FS}?              setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+0[xX]{H}*"."{H}+{P}?{FS}?      setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+0[xX]{H}+"."{H}*{P}?{FS}?      setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+
+"["                            return LSBRAC;
+"]"                            return RSBRAC;
+"("                            return LPAREN;
+")"                            return RPAREN;
+"{"                            return LBRAC;
+"}"                            return RBRAC;
+"->"                           return RARROW;
+
+"*"                            return STAR;
+"+"                            return PLUS;
+"-"                            return MINUS;
+
+"%"                            return MOD_OP;
+"/"                            return DIV_OP;
+">>"                           return RIGHT_OP;
+"<<"                           return LEFT_OP;
+
+"=="                           return EQ_OP;
+"!="                           return NE_OP;
+"<="                           return LE_OP;
+">="                           return GE_OP;
+"<"                            return LT_OP;
+">"                            return GT_OP;
+"&&"                           return AND_OP;
+"||"                           return OR_OP;
+"!"                            return NOT_OP;
+
+":="                           return ASSIGN;
+":"                            return COLON;
+";"                            return SEMICOLON;
+"..."                          return DOTDOTDOT;
+"."                            return DOT;
+"="                            return EQUAL;
+","                            return COMMA;
+"^"                            return XOR_BIN;
+"&"                            return AND_BIN;
+"|"                            return OR_BIN;
+"~"                            return NOT_BIN;
+"$"{IDENTIFIER}                        printf_debug("<GLOBAL_IDENTIFIER %s>\n", yytext); setstring(yyextra, yylval, yytext); return GLOBAL_IDENTIFIER;
+{IDENTIFIER}                   printf_debug("<IDENTIFIER %s>\n", yytext); setstring(yyextra, yylval, yytext); return IDENTIFIER;
+[ \t\n]+                       ; /* ignore */
+.                              return ERROR;
+%%
diff --git a/src/common/filter/filter-parser.y b/src/common/filter/filter-parser.y
new file mode 100644 (file)
index 0000000..dfcdee3
--- /dev/null
@@ -0,0 +1,816 @@
+%{
+/*
+ * filter-parser.y
+ *
+ * LTTng filter expression parser
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Grammar inspired from http://www.quut.com/c/ANSI-C-grammar-y.html
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-bytecode.h"
+#include "memstream.h"
+
+#include <common/macros.h>
+
+#define WIDTH_u64_SCANF_IS_A_BROKEN_API        "20"
+#define WIDTH_o64_SCANF_IS_A_BROKEN_API        "22"
+#define WIDTH_x64_SCANF_IS_A_BROKEN_API        "17"
+#define WIDTH_lg_SCANF_IS_A_BROKEN_API "4096"  /* Hugely optimistic approximation */
+
+#ifdef DEBUG
+static const int print_xml = 1;
+#define dbg_printf(fmt, args...)       \
+       printf("[debug filter_parser] " fmt, ## args)
+#else
+static const int print_xml = 0;
+#define dbg_printf(fmt, args...)                               \
+do {                                                           \
+       /* do nothing but check printf format */                \
+       if (0)                                                  \
+               printf("[debug filter_parser] " fmt, ## args);  \
+} while (0)
+#endif
+
+LTTNG_HIDDEN
+int yydebug;
+LTTNG_HIDDEN
+int filter_parser_debug = 0;
+
+LTTNG_HIDDEN
+int yyparse(struct filter_parser_ctx *parser_ctx, yyscan_t scanner);
+LTTNG_HIDDEN
+int yylex(union YYSTYPE *yyval, yyscan_t scanner);
+LTTNG_HIDDEN
+int yylex_init_extra(struct filter_parser_ctx *parser_ctx, yyscan_t * ptr_yy_globals);
+LTTNG_HIDDEN
+int yylex_destroy(yyscan_t yyparser_ctx);
+LTTNG_HIDDEN
+void yyrestart(FILE * in_str, yyscan_t parser_ctx);
+
+struct gc_string {
+       struct cds_list_head gc;
+       size_t alloclen;
+       char s[];
+};
+
+static const char *node_type_to_str[] = {
+       [ NODE_UNKNOWN ] = "NODE_UNKNOWN",
+       [ NODE_ROOT ] = "NODE_ROOT",
+       [ NODE_EXPRESSION ] = "NODE_EXPRESSION",
+       [ NODE_OP ] = "NODE_OP",
+       [ NODE_UNARY_OP ] = "NODE_UNARY_OP",
+};
+
+LTTNG_HIDDEN
+const char *node_type(struct filter_node *node)
+{
+       if (node->type < NR_NODE_TYPES)
+               return node_type_to_str[node->type];
+       else
+               return NULL;
+}
+
+static struct gc_string *gc_string_alloc(struct filter_parser_ctx *parser_ctx,
+                                        size_t len)
+{
+       struct gc_string *gstr;
+       size_t alloclen;
+
+       /* TODO: could be faster with find first bit or glib Gstring */
+       /* sizeof long to account for malloc header (int or long ?) */
+       for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + len;
+            alloclen *= 2);
+
+       gstr = zmalloc(alloclen);
+       if (!gstr) {
+               goto end;
+       }
+       cds_list_add(&gstr->gc, &parser_ctx->allocated_strings);
+       gstr->alloclen = alloclen;
+end:
+       return gstr;
+}
+
+/*
+ * note: never use gc_string_append on a string that has external references.
+ * gsrc will be garbage collected immediately, and gstr might be.
+ * Should only be used to append characters to a string literal or constant.
+ */
+static
+struct gc_string *gc_string_append(struct filter_parser_ctx *parser_ctx,
+                                  struct gc_string *gstr,
+                                  struct gc_string *gsrc)
+{
+       size_t newlen = strlen(gsrc->s) + strlen(gstr->s) + 1;
+       size_t alloclen;
+
+       /* TODO: could be faster with find first bit or glib Gstring */
+       /* sizeof long to account for malloc header (int or long ?) */
+       for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + newlen;
+            alloclen *= 2);
+
+       if (alloclen > gstr->alloclen) {
+               struct gc_string *newgstr;
+
+               newgstr = gc_string_alloc(parser_ctx, newlen);
+               strcpy(newgstr->s, gstr->s);
+               strcat(newgstr->s, gsrc->s);
+               cds_list_del(&gstr->gc);
+               free(gstr);
+               gstr = newgstr;
+       } else {
+               strcat(gstr->s, gsrc->s);
+       }
+       cds_list_del(&gsrc->gc);
+       free(gsrc);
+       return gstr;
+}
+
+LTTNG_HIDDEN
+void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src);
+LTTNG_HIDDEN
+void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src)
+{
+       lvalp->gs = gc_string_alloc(parser_ctx, strlen(src) + 1);
+       strcpy(lvalp->gs->s, src);
+}
+
+static struct filter_node *make_node(struct filter_parser_ctx *scanner,
+                                 enum node_type type)
+{
+       struct filter_ast *ast = filter_parser_get_ast(scanner);
+       struct filter_node *node;
+
+       node = zmalloc(sizeof(*node));
+       if (!node)
+               return NULL;
+       memset(node, 0, sizeof(*node));
+       node->type = type;
+       cds_list_add(&node->gc, &ast->allocated_nodes);
+
+       switch (type) {
+       case NODE_ROOT:
+               fprintf(stderr, "[error] %s: trying to create root node\n", __func__);
+               break;
+
+       case NODE_EXPRESSION:
+               break;
+       case NODE_OP:
+               break;
+       case NODE_UNARY_OP:
+               break;
+
+       case NODE_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown node type %d\n", __func__,
+                       (int) type);
+               break;
+       }
+
+       return node;
+}
+
+static struct filter_node *make_op_node(struct filter_parser_ctx *scanner,
+                       enum op_type type,
+                       struct filter_node *lchild,
+                       struct filter_node *rchild)
+{
+       struct filter_ast *ast = filter_parser_get_ast(scanner);
+       struct filter_node *node;
+
+       node = zmalloc(sizeof(*node));
+       if (!node)
+               return NULL;
+       memset(node, 0, sizeof(*node));
+       node->type = NODE_OP;
+       cds_list_add(&node->gc, &ast->allocated_nodes);
+       node->u.op.type = type;
+       node->u.op.lchild = lchild;
+       node->u.op.rchild = rchild;
+       return node;
+}
+
+LTTNG_HIDDEN
+void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str);
+LTTNG_HIDDEN
+void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str)
+{
+       fprintf(stderr, "error %s\n", str);
+}
+LTTNG_HIDDEN
+int yywrap(void);
+LTTNG_HIDDEN
+int yywrap(void)
+{
+       return 1;
+} 
+
+#define parse_error(parser_ctx, str)                           \
+do {                                                           \
+       yyerror(parser_ctx, parser_ctx->scanner, YY_("parse error: " str "\n"));        \
+       YYERROR;                                                \
+} while (0)
+
+static void free_strings(struct cds_list_head *list)
+{
+       struct gc_string *gstr, *tmp;
+
+       cds_list_for_each_entry_safe(gstr, tmp, list, gc)
+               free(gstr);
+}
+
+static struct filter_ast *filter_ast_alloc(void)
+{
+       struct filter_ast *ast;
+
+       ast = zmalloc(sizeof(*ast));
+       if (!ast)
+               return NULL;
+       memset(ast, 0, sizeof(*ast));
+       CDS_INIT_LIST_HEAD(&ast->allocated_nodes);
+       ast->root.type = NODE_ROOT;
+       return ast;
+}
+
+static void filter_ast_free(struct filter_ast *ast)
+{
+       struct filter_node *node, *tmp;
+
+       cds_list_for_each_entry_safe(node, tmp, &ast->allocated_nodes, gc)
+               free(node);
+       free(ast);
+}
+
+LTTNG_HIDDEN
+int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx)
+{
+       return yyparse(parser_ctx, parser_ctx->scanner);
+}
+
+LTTNG_HIDDEN
+struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input)
+{
+       struct filter_parser_ctx *parser_ctx;
+       int ret;
+
+       yydebug = filter_parser_debug;
+
+       parser_ctx = zmalloc(sizeof(*parser_ctx));
+       if (!parser_ctx)
+               return NULL;
+       memset(parser_ctx, 0, sizeof(*parser_ctx));
+
+       ret = yylex_init_extra(parser_ctx, &parser_ctx->scanner);
+       if (ret) {
+               fprintf(stderr, "yylex_init error\n");
+               goto cleanup_parser_ctx;
+       }
+       /* Start processing new stream */
+       yyrestart(input, parser_ctx->scanner);
+
+       parser_ctx->ast = filter_ast_alloc();
+       if (!parser_ctx->ast)
+               goto cleanup_lexer;
+       CDS_INIT_LIST_HEAD(&parser_ctx->allocated_strings);
+
+       if (yydebug)
+               fprintf(stdout, "parser_ctx input is a%s.\n",
+                       isatty(fileno(input)) ? "n interactive tty" :
+                                               " noninteractive file");
+
+       return parser_ctx;
+
+cleanup_lexer:
+       ret = yylex_destroy(parser_ctx->scanner);
+       if (!ret)
+               fprintf(stderr, "yylex_destroy error\n");
+cleanup_parser_ctx:
+       free(parser_ctx);
+       return NULL;
+}
+
+LTTNG_HIDDEN
+void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx)
+{
+       int ret;
+
+       free_strings(&parser_ctx->allocated_strings);
+       filter_ast_free(parser_ctx->ast);
+       ret = yylex_destroy(parser_ctx->scanner);
+       if (ret)
+               fprintf(stderr, "yylex_destroy error\n");
+       free(parser_ctx);
+}
+
+LTTNG_HIDDEN
+int filter_parser_ctx_create_from_filter_expression(
+               const char *filter_expression, struct filter_parser_ctx **ctxp)
+{
+       int ret;
+       struct filter_parser_ctx *ctx = NULL;
+       FILE *fmem = NULL;
+
+       assert(filter_expression);
+       assert(ctxp);
+
+       /*
+        * Casting const to non-const, as the underlying function will use it in
+        * read-only mode.
+        */
+       fmem = lttng_fmemopen((void *) filter_expression,
+                       strlen(filter_expression), "r");
+       if (!fmem) {
+               fprintf(stderr, "Error opening memory as stream\n");
+               ret = -LTTNG_ERR_FILTER_NOMEM;
+               goto error;
+       }
+       ctx = filter_parser_ctx_alloc(fmem);
+       if (!ctx) {
+               fprintf(stderr, "Error allocating parser\n");
+               ret = -LTTNG_ERR_FILTER_NOMEM;
+               goto filter_alloc_error;
+       }
+       ret = filter_parser_ctx_append_ast(ctx);
+       if (ret) {
+               fprintf(stderr, "Parse error\n");
+               ret = -LTTNG_ERR_FILTER_INVAL;
+               goto parse_error;
+       }
+       if (print_xml) {
+               ret = filter_visitor_print_xml(ctx, stdout, 0);
+               if (ret) {
+                       fflush(stdout);
+                       fprintf(stderr, "XML print error\n");
+                       ret = -LTTNG_ERR_FILTER_INVAL;
+                       goto parse_error;
+               }
+       }
+
+       dbg_printf("Generating IR... ");
+       fflush(stdout);
+       ret = filter_visitor_ir_generate(ctx);
+       if (ret) {
+               fprintf(stderr, "Generate IR error\n");
+               ret = -LTTNG_ERR_FILTER_INVAL;
+               goto parse_error;
+       }
+       dbg_printf("done\n");
+
+       dbg_printf("Validating IR... ");
+       fflush(stdout);
+       ret = filter_visitor_ir_check_binary_op_nesting(ctx);
+       if (ret) {
+               ret = -LTTNG_ERR_FILTER_INVAL;
+               goto parse_error;
+       }
+
+       /* Normalize globbing patterns in the expression. */
+       ret = filter_visitor_ir_normalize_glob_patterns(ctx);
+       if (ret) {
+               ret = -LTTNG_ERR_FILTER_INVAL;
+               goto parse_error;
+       }
+
+       /* Validate strings used as literals in the expression. */
+       ret = filter_visitor_ir_validate_string(ctx);
+       if (ret) {
+               ret = -LTTNG_ERR_FILTER_INVAL;
+               goto parse_error;
+       }
+
+       /* Validate globbing patterns in the expression. */
+       ret = filter_visitor_ir_validate_globbing(ctx);
+       if (ret) {
+               ret = -LTTNG_ERR_FILTER_INVAL;
+               goto parse_error;
+       }
+
+       dbg_printf("done\n");
+
+       dbg_printf("Generating bytecode... ");
+       fflush(stdout);
+       ret = filter_visitor_bytecode_generate(ctx);
+       if (ret) {
+               fprintf(stderr, "Generate bytecode error\n");
+               ret = -LTTNG_ERR_FILTER_INVAL;
+               goto parse_error;
+       }
+       dbg_printf("done\n");
+       dbg_printf("Size of bytecode generated: %u bytes.\n",
+                       bytecode_get_len(&ctx->bytecode->b));
+
+       /* No need to keep the memory stream. */
+       if (fclose(fmem) != 0) {
+               fprintf(stderr, "fclose (%d) \n", errno);
+               ret = -LTTNG_ERR_FILTER_INVAL;
+       }
+
+       *ctxp = ctx;
+       return 0;
+
+parse_error:
+       filter_ir_free(ctx);
+       filter_parser_ctx_free(ctx);
+filter_alloc_error:
+       if (fclose(fmem) != 0) {
+               fprintf(stderr, "fclose (%d) \n", errno);
+       }
+error:
+       return ret;
+}
+
+%}
+
+%define api.pure
+       /* %locations */
+%parse-param {struct filter_parser_ctx *parser_ctx}
+%parse-param {yyscan_t scanner}
+%lex-param {yyscan_t scanner}
+%start translation_unit
+%token CHARACTER_CONSTANT_START SQUOTE STRING_LITERAL_START DQUOTE
+%token ESCSEQ CHAR_STRING_TOKEN
+%token DECIMAL_CONSTANT OCTAL_CONSTANT HEXADECIMAL_CONSTANT FLOAT_CONSTANT
+%token LSBRAC RSBRAC LPAREN RPAREN LBRAC RBRAC RARROW
+%token STAR PLUS MINUS
+%token MOD_OP DIV_OP RIGHT_OP LEFT_OP
+%token EQ_OP NE_OP LE_OP GE_OP LT_OP GT_OP AND_OP OR_OP NOT_OP
+%token ASSIGN COLON SEMICOLON DOTDOTDOT DOT EQUAL COMMA
+%token XOR_BIN AND_BIN OR_BIN NOT_BIN
+
+%token <gs> IDENTIFIER GLOBAL_IDENTIFIER
+%token ERROR
+%union
+{
+       long long ll;
+       char c;
+       struct gc_string *gs;
+       struct filter_node *n;
+}
+
+%type <gs> s_char s_char_sequence c_char c_char_sequence
+
+%type <n> primary_expression
+%type <n> prefix_expression
+%type <n> prefix_expression_rec
+%type <n> postfix_expression
+%type <n> unary_expression
+%type <n> unary_operator
+%type <n> multiplicative_expression
+%type <n> additive_expression
+%type <n> shift_expression
+%type <n> relational_expression
+%type <n> equality_expression
+%type <n> and_expression
+%type <n> exclusive_or_expression
+%type <n> inclusive_or_expression
+%type <n> logical_and_expression
+%type <n> logical_or_expression
+%type <n> expression
+%type <n> identifiers
+
+%%
+
+
+/* 1.5 Constants */
+
+c_char_sequence:
+               c_char
+               {       $$ = $1;                                        }
+       |       c_char_sequence c_char
+               {       $$ = gc_string_append(parser_ctx, $1, $2);              }
+       ;
+
+c_char:
+               CHAR_STRING_TOKEN
+               {       $$ = yylval.gs;                                 }
+       |       ESCSEQ
+               {
+                       parse_error(parser_ctx, "escape sequences not supported yet");
+               }
+       ;
+
+/* 1.6 String literals */
+
+s_char_sequence:
+               s_char
+               {       $$ = $1;                                        }
+       |       s_char_sequence s_char
+               {       $$ = gc_string_append(parser_ctx, $1, $2);              }
+       ;
+
+s_char:
+               CHAR_STRING_TOKEN
+               {       $$ = yylval.gs;                                 }
+       |       ESCSEQ
+               {
+                       parse_error(parser_ctx, "escape sequences not supported yet");
+               }
+       ;
+
+primary_expression:
+               DECIMAL_CONSTANT
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_CONSTANT;
+                       if (sscanf(yylval.gs->s, "%" WIDTH_u64_SCANF_IS_A_BROKEN_API SCNu64,
+                                       &$$->u.expression.u.constant) != 1) {
+                               parse_error(parser_ctx, "cannot scanf decimal constant");
+                       }
+               }
+       |       OCTAL_CONSTANT
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_CONSTANT;
+                       if (!strcmp(yylval.gs->s, "0")) {
+                               $$->u.expression.u.constant = 0;
+                       } else if (sscanf(yylval.gs->s, "0%" WIDTH_o64_SCANF_IS_A_BROKEN_API SCNo64,
+                                       &$$->u.expression.u.constant) != 1) {
+                               parse_error(parser_ctx, "cannot scanf octal constant");
+                       }
+               }
+       |       HEXADECIMAL_CONSTANT
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_CONSTANT;
+                       if (sscanf(yylval.gs->s, "0x%" WIDTH_x64_SCANF_IS_A_BROKEN_API SCNx64,
+                                       &$$->u.expression.u.constant) != 1) {
+                               parse_error(parser_ctx, "cannot scanf hexadecimal constant");
+                       }
+               }
+       |       FLOAT_CONSTANT
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_FLOAT_CONSTANT;
+                       if (sscanf(yylval.gs->s, "%" WIDTH_lg_SCANF_IS_A_BROKEN_API "lg",
+                                       &$$->u.expression.u.float_constant) != 1) {
+                               parse_error(parser_ctx, "cannot scanf float constant");
+                       }
+               }
+       |       STRING_LITERAL_START DQUOTE
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_STRING;
+                       $$->u.expression.u.string = "";
+               }
+       |       STRING_LITERAL_START s_char_sequence DQUOTE
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_STRING;
+                       $$->u.expression.u.string = $2->s;
+               }
+       |       CHARACTER_CONSTANT_START c_char_sequence SQUOTE
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_STRING;
+                       $$->u.expression.u.string = $2->s;
+               }
+       |       LPAREN expression RPAREN
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_NESTED;
+                       $$->u.expression.u.child = $2;
+               }
+       ;
+
+identifiers
+       :       IDENTIFIER
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_IDENTIFIER;
+                       $$->u.expression.u.identifier = yylval.gs->s;
+               }
+       |       GLOBAL_IDENTIFIER
+               {
+                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
+                       $$->u.expression.type = AST_EXP_GLOBAL_IDENTIFIER;
+                       $$->u.expression.u.identifier = yylval.gs->s;
+               }
+       ;
+
+prefix_expression_rec
+       : LSBRAC unary_expression RSBRAC
+               {
+                       $$ = $2;
+               }
+       | LSBRAC unary_expression RSBRAC prefix_expression_rec
+               {
+                       $$ = $2;
+                       $$->u.expression.pre_op = AST_LINK_BRACKET;
+                       $$->u.expression.prev = $4;
+               }
+       ;
+
+prefix_expression
+       : identifiers
+               {
+                       $$ = $1;
+               }
+       | identifiers prefix_expression_rec
+               {
+                       $$ = $1;
+                       $$->u.expression.pre_op = AST_LINK_BRACKET;
+                       $$->u.expression.next_bracket = $2;
+               }
+       ;
+
+postfix_expression
+       : prefix_expression
+               {
+                       $$ = $1;
+               }
+       | postfix_expression DOT prefix_expression
+               {
+                       $$ = $3;
+                       $$->u.expression.post_op = AST_LINK_DOT;
+                       $$->u.expression.prev = $1;
+               }
+       | postfix_expression RARROW prefix_expression
+               {
+                       $$ = $3;
+                       $$->u.expression.post_op = AST_LINK_RARROW;
+                       $$->u.expression.prev = $1;
+               }
+       ;
+
+unary_expression
+       : postfix_expression
+               {       $$ = $1;                                        }
+       | primary_expression
+               {       $$ = $1;                                        }
+       | unary_operator unary_expression
+               {
+                       $$ = $1;
+                       $$->u.unary_op.child = $2;
+               }
+       ;
+
+unary_operator
+       : PLUS
+               {
+                       $$ = make_node(parser_ctx, NODE_UNARY_OP);
+                       $$->u.unary_op.type = AST_UNARY_PLUS;
+               }
+       | MINUS
+               {
+                       $$ = make_node(parser_ctx, NODE_UNARY_OP);
+                       $$->u.unary_op.type = AST_UNARY_MINUS;
+               }
+       | NOT_OP
+               {
+                       $$ = make_node(parser_ctx, NODE_UNARY_OP);
+                       $$->u.unary_op.type = AST_UNARY_NOT;
+               }
+       | NOT_BIN
+               {
+                       $$ = make_node(parser_ctx, NODE_UNARY_OP);
+                       $$->u.unary_op.type = AST_UNARY_BIT_NOT;
+               }
+       ;
+
+multiplicative_expression
+       : unary_expression
+               {       $$ = $1;                                        }
+       | multiplicative_expression STAR unary_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_MUL, $1, $3);
+               }
+       | multiplicative_expression DIV_OP unary_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_DIV, $1, $3);
+               }
+       | multiplicative_expression MOD_OP unary_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_MOD, $1, $3);
+               }
+       ;
+
+additive_expression
+       : multiplicative_expression
+               {       $$ = $1;                                        }
+       | additive_expression PLUS multiplicative_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_PLUS, $1, $3);
+               }
+       | additive_expression MINUS multiplicative_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_MINUS, $1, $3);
+               }
+       ;
+
+shift_expression
+       : additive_expression
+               {       $$ = $1;                                        }
+       | shift_expression LEFT_OP additive_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_BIT_LSHIFT, $1, $3);
+               }
+       | shift_expression RIGHT_OP additive_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_BIT_RSHIFT, $1, $3);
+               }
+       ;
+
+and_expression
+       : shift_expression
+               {       $$ = $1;                                        }
+       | and_expression AND_BIN shift_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_BIT_AND, $1, $3);
+               }
+       ;
+
+exclusive_or_expression
+       : and_expression
+               {       $$ = $1;                                        }
+       | exclusive_or_expression XOR_BIN and_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_BIT_XOR, $1, $3);
+               }
+       ;
+
+inclusive_or_expression
+       : exclusive_or_expression
+               {       $$ = $1;                                        }
+       | inclusive_or_expression OR_BIN exclusive_or_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_BIT_OR, $1, $3);
+               }
+       ;
+
+relational_expression
+       : inclusive_or_expression
+               {       $$ = $1;                                        }
+       | relational_expression LT_OP inclusive_or_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_LT, $1, $3);
+               }
+       | relational_expression GT_OP inclusive_or_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_GT, $1, $3);
+               }
+       | relational_expression LE_OP inclusive_or_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_LE, $1, $3);
+               }
+       | relational_expression GE_OP inclusive_or_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_GE, $1, $3);
+               }
+       ;
+
+equality_expression
+       : relational_expression
+               {       $$ = $1;                                        }
+       | equality_expression EQ_OP relational_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_EQ, $1, $3);
+               }
+       | equality_expression NE_OP relational_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_NE, $1, $3);
+               }
+       ;
+
+logical_and_expression
+       : equality_expression
+               {       $$ = $1;                                        }
+       | logical_and_expression AND_OP equality_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_AND, $1, $3);
+               }
+       ;
+
+logical_or_expression
+       : logical_and_expression
+               {       $$ = $1;                                        }
+       | logical_or_expression OR_OP logical_and_expression
+               {
+                       $$ = make_op_node(parser_ctx, AST_OP_OR, $1, $3);
+               }
+       ;
+
+expression
+       : logical_or_expression
+               {       $$ = $1;                                        }
+       ;
+
+translation_unit
+       : expression
+               {
+                       parser_ctx->ast->root.u.root.child = $1;
+               }
+       ;
diff --git a/src/common/filter/filter-symbols.h b/src/common/filter/filter-symbols.h
new file mode 100644 (file)
index 0000000..031776d
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef _FILTER_SYMBOLS_H
+#define _FILTER_SYMBOLS_H
+
+/*
+ * filter-symbols.h
+ *
+ * LTTng filter flex/bison symbol prefixes
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#define yy_create_buffer lttng_yy_create_buffer
+#define yy_delete_buffer lttng_yy_delete_buffer
+#define yy_flush_buffer lttng_yy_flush_buffer
+#define yy_scan_buffer lttng_yy_scan_buffer
+#define yy_scan_bytes lttng_yy_scan_bytes
+#define yy_scan_string lttng_yy_scan_string
+#define yy_switch_to_buffer lttng_yy_switch_to_buffer
+#define yyalloc lttng_yyalloc
+#define yyfree lttng_yyfree
+#define yyget_column lttng_yyget_column
+#define yyget_debug lttng_yyget_debug
+#define yyget_extra lttng_yyget_extra
+#define yyget_in lttng_yyget_in
+#define yyget_leng lttng_yyget_leng
+#define yyget_lineno lttng_yyget_lineno
+#define yyget_lval lttng_yyget_lval
+#define yyget_out lttng_yyget_out
+#define yyget_text lttng_yyget_text
+#define yylex_init lttng_yylex_init
+#define yypop_buffer_state lttng_yypop_buffer_state
+#define yypush_buffer_state lttng_yypush_buffer_state
+#define yyrealloc lttng_yyrealloc
+#define yyset_column lttng_yyset_column
+#define yyset_debug lttng_yyset_debug
+#define yyset_extra lttng_yyset_extra
+#define yyset_in lttng_yyset_in
+#define yyset_lineno lttng_yyset_lineno
+#define yyset_lval lttng_yyset_lval
+#define yyset_out lttng_yyset_out
+
+#endif /* _FILTER_SYMBOLS_H */
diff --git a/src/common/filter/filter-visitor-generate-bytecode.c b/src/common/filter/filter-visitor-generate-bytecode.c
new file mode 100644 (file)
index 0000000..699273c
--- /dev/null
@@ -0,0 +1,830 @@
+/*
+ * filter-visitor-generate-bytecode.c
+ *
+ * LTTng filter bytecode generation
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <common/align.h>
+#include <common/compat/string.h>
+
+#include "filter-bytecode.h"
+#include "filter-ir.h"
+#include "filter-ast.h"
+
+#include <common/macros.h>
+
+#ifndef max_t
+#define max_t(type, a, b)      ((type) ((a) > (b) ? (a) : (b)))
+#endif
+
+#define INIT_ALLOC_SIZE                4
+
+static
+int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx,
+               struct ir_op *node);
+
+static inline int get_count_order(unsigned int count)
+{
+       int order;
+
+       order = lttng_fls(count) - 1;
+       if (count & (count - 1))
+               order++;
+       return order;
+}
+
+static
+int bytecode_init(struct lttng_filter_bytecode_alloc **fb)
+{
+       uint32_t alloc_len;
+
+       alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + INIT_ALLOC_SIZE;
+       *fb = calloc(alloc_len, 1);
+       if (!*fb) {
+               return -ENOMEM;
+       } else {
+               (*fb)->alloc_len = alloc_len;
+               return 0;
+       }
+}
+
+static
+int32_t bytecode_reserve(struct lttng_filter_bytecode_alloc **fb, uint32_t align, uint32_t len)
+{
+       int32_t ret;
+       uint32_t padding = offset_align((*fb)->b.len, align);
+       uint32_t new_len = (*fb)->b.len + padding + len;
+       uint32_t new_alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + new_len;
+       uint32_t old_alloc_len = (*fb)->alloc_len;
+
+       if (new_len > LTTNG_FILTER_MAX_LEN)
+               return -EINVAL;
+
+       if (new_alloc_len > old_alloc_len) {
+               struct lttng_filter_bytecode_alloc *newptr;
+
+               new_alloc_len =
+                       max_t(uint32_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1);
+               newptr = realloc(*fb, new_alloc_len);
+               if (!newptr)
+                       return -ENOMEM;
+               *fb = newptr;
+               /* We zero directly the memory from start of allocation. */
+               memset(&((char *) *fb)[old_alloc_len], 0, new_alloc_len - old_alloc_len);
+               (*fb)->alloc_len = new_alloc_len;
+       }
+       (*fb)->b.len += padding;
+       ret = (*fb)->b.len;
+       (*fb)->b.len += len;
+       return ret;
+}
+
+static
+int bytecode_push(struct lttng_filter_bytecode_alloc **fb, const void *data,
+               uint32_t align, uint32_t len)
+{
+       int32_t offset;
+
+       offset = bytecode_reserve(fb, align, len);
+       if (offset < 0)
+               return offset;
+       memcpy(&(*fb)->b.data[offset], data, len);
+       return 0;
+}
+
+static
+int bytecode_push_logical(struct lttng_filter_bytecode_alloc **fb,
+               struct logical_op *data,
+               uint32_t align, uint32_t len,
+               uint16_t *skip_offset)
+{
+       int32_t offset;
+
+       offset = bytecode_reserve(fb, align, len);
+       if (offset < 0)
+               return offset;
+       memcpy(&(*fb)->b.data[offset], data, len);
+       *skip_offset =
+               (void *) &((struct logical_op *) &(*fb)->b.data[offset])->skip_offset
+                       - (void *) &(*fb)->b.data[0];
+       return 0;
+}
+
+static
+int bytecode_patch(struct lttng_filter_bytecode_alloc **fb,
+               const void *data,
+               uint16_t offset,
+               uint32_t len)
+{
+       if (offset >= (*fb)->b.len) {
+               return -EINVAL;
+       }
+       memcpy(&(*fb)->b.data[offset], data, len);
+       return 0;
+}
+
+static
+int visit_node_root(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+       int ret;
+       struct return_op insn;
+
+       /* Visit child */
+       ret = recursive_visit_gen_bytecode(ctx, node->u.root.child);
+       if (ret)
+               return ret;
+
+       /* Generate end of bytecode instruction */
+       insn.op = FILTER_OP_RETURN;
+       return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+}
+
+static
+int append_str(char **s, const char *append)
+{
+       char *old = *s;
+       char *new;
+       size_t oldlen = (old == NULL) ? 0 : strlen(old);
+       size_t appendlen = strlen(append);
+
+       new = calloc(oldlen + appendlen + 1, 1);
+       if (!new) {
+               return -ENOMEM;
+       }
+       if (oldlen) {
+               strcpy(new, old);
+       }
+       strcat(new, append);
+       *s = new;
+       free(old);
+       return 0;
+}
+
+/*
+ * 1: match
+ * 0: no match
+ * < 0: error
+ */
+static
+int load_expression_legacy_match(const struct ir_load_expression *exp,
+               enum filter_op *op_type,
+               char **symbol)
+{
+       const struct ir_load_expression_op *op;
+       bool need_dot = false;
+
+       op = exp->child;
+       switch (op->type) {
+       case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+               *op_type = FILTER_OP_GET_CONTEXT_REF;
+               if (append_str(symbol, "$ctx.")) {
+                       return -ENOMEM;
+               }
+               need_dot = false;
+               break;
+       case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+               *op_type = FILTER_OP_GET_CONTEXT_REF;
+               if (append_str(symbol, "$app.")) {
+                       return -ENOMEM;
+               }
+               need_dot = false;
+               break;
+       case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+               *op_type = FILTER_OP_LOAD_FIELD_REF;
+               need_dot = false;
+               break;
+
+       case IR_LOAD_EXPRESSION_GET_SYMBOL:
+       case IR_LOAD_EXPRESSION_GET_INDEX:
+       case IR_LOAD_EXPRESSION_LOAD_FIELD:
+       default:
+               return 0;       /* no match */
+       }
+
+       for (;;) {
+               op = op->next;
+               if (!op) {
+                       return 0;       /* no match */
+               }
+               switch (op->type) {
+               case IR_LOAD_EXPRESSION_LOAD_FIELD:
+                       goto end;
+               case IR_LOAD_EXPRESSION_GET_SYMBOL:
+                       if (need_dot && append_str(symbol, ".")) {
+                               return -ENOMEM;
+                       }
+                       if (append_str(symbol, op->u.symbol)) {
+                               return -ENOMEM;
+                       }
+                       break;
+               default:
+                       return 0;        /* no match */
+               }
+               need_dot = true;
+       }
+end:
+       return 1;       /* Legacy match */
+}
+
+/*
+ * 1: legacy match
+ * 0: no legacy match
+ * < 0: error
+ */
+static
+int visit_node_load_expression_legacy(struct filter_parser_ctx *ctx,
+               const struct ir_load_expression *exp,
+               const struct ir_load_expression_op *op)
+{
+       struct load_op *insn = NULL;
+       uint32_t insn_len = sizeof(struct load_op)
+               + sizeof(struct field_ref);
+       struct field_ref ref_offset;
+       uint32_t reloc_offset_u32;
+       uint16_t reloc_offset;
+       enum filter_op op_type;
+       char *symbol = NULL;
+       int ret;
+
+       ret = load_expression_legacy_match(exp, &op_type, &symbol);
+       if (ret <= 0) {
+               goto end;
+       }
+       insn = calloc(insn_len, 1);
+       if (!insn) {
+               ret = -ENOMEM;
+               goto end;
+       }
+       insn->op = op_type;
+       ref_offset.offset = (uint16_t) -1U;
+       memcpy(insn->data, &ref_offset, sizeof(ref_offset));
+       /* reloc_offset points to struct load_op */
+       reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
+       if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
+               ret = -EINVAL;
+               goto end;
+       }
+       reloc_offset = (uint16_t) reloc_offset_u32;
+       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+       if (ret) {
+               goto end;
+       }
+       /* append reloc */
+       ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
+                               1, sizeof(reloc_offset));
+       if (ret) {
+               goto end;
+       }
+       ret = bytecode_push(&ctx->bytecode_reloc, symbol,
+                               1, strlen(symbol) + 1);
+       if (ret) {
+               goto end;
+       }
+       ret = 1;        /* legacy */
+end:
+       free(insn);
+       free(symbol);
+       return ret;
+}
+
+static
+int visit_node_load_expression(struct filter_parser_ctx *ctx,
+               const struct ir_op *node)
+{
+       struct ir_load_expression *exp;
+       struct ir_load_expression_op *op;
+       int ret;
+
+       exp = node->u.load.u.expression;
+       if (!exp) {
+               return -EINVAL;
+       }
+       op = exp->child;
+       if (!op) {
+               return -EINVAL;
+       }
+
+       /*
+        * TODO: if we remove legacy load for application contexts, we
+        * need to update session bytecode parser as well.
+        */
+       ret = visit_node_load_expression_legacy(ctx, exp, op);
+       if (ret < 0) {
+               return ret;
+       }
+       if (ret > 0) {
+               return 0;       /* legacy */
+       }
+
+       for (; op != NULL; op = op->next) {
+               switch (op->type) {
+               case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+               {
+                       struct load_op *insn;
+                       uint32_t insn_len = sizeof(struct load_op);
+                       int ret;
+
+                       insn = calloc(insn_len, 1);
+                       if (!insn)
+                               return -ENOMEM;
+                       insn->op = FILTER_OP_GET_CONTEXT_ROOT;
+                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+                       free(insn);
+                       if (ret) {
+                               return ret;
+                       }
+                       break;
+               }
+               case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+               {
+                       struct load_op *insn;
+                       uint32_t insn_len = sizeof(struct load_op);
+                       int ret;
+
+                       insn = calloc(insn_len, 1);
+                       if (!insn)
+                               return -ENOMEM;
+                       insn->op = FILTER_OP_GET_APP_CONTEXT_ROOT;
+                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+                       free(insn);
+                       if (ret) {
+                               return ret;
+                       }
+                       break;
+               }
+               case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+               {
+                       struct load_op *insn;
+                       uint32_t insn_len = sizeof(struct load_op);
+                       int ret;
+
+                       insn = calloc(insn_len, 1);
+                       if (!insn)
+                               return -ENOMEM;
+                       insn->op = FILTER_OP_GET_PAYLOAD_ROOT;
+                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+                       free(insn);
+                       if (ret) {
+                               return ret;
+                       }
+                       break;
+               }
+               case IR_LOAD_EXPRESSION_GET_SYMBOL:
+               {
+                       struct load_op *insn;
+                       uint32_t insn_len = sizeof(struct load_op)
+                               + sizeof(struct get_symbol);
+                       struct get_symbol symbol_offset;
+                       uint32_t reloc_offset_u32;
+                       uint16_t reloc_offset;
+                       uint32_t bytecode_reloc_offset_u32;
+                       int ret;
+
+                       insn = calloc(insn_len, 1);
+                       if (!insn)
+                               return -ENOMEM;
+                       insn->op = FILTER_OP_GET_SYMBOL;
+                       bytecode_reloc_offset_u32 =
+                                       bytecode_get_len(&ctx->bytecode_reloc->b)
+                                       + sizeof(reloc_offset);
+                       symbol_offset.offset =
+                                       (uint16_t) bytecode_reloc_offset_u32;
+                       memcpy(insn->data, &symbol_offset,
+                                       sizeof(symbol_offset));
+                       /* reloc_offset points to struct load_op */
+                       reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
+                       if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
+                               free(insn);
+                               return -EINVAL;
+                       }
+                       reloc_offset = (uint16_t) reloc_offset_u32;
+                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+                       if (ret) {
+                               free(insn);
+                               return ret;
+                       }
+                       /* append reloc */
+                       ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
+                                       1, sizeof(reloc_offset));
+                       if (ret) {
+                               free(insn);
+                               return ret;
+                       }
+                       ret = bytecode_push(&ctx->bytecode_reloc,
+                                       op->u.symbol,
+                                       1, strlen(op->u.symbol) + 1);
+                       free(insn);
+                       if (ret) {
+                               return ret;
+                       }
+                       break;
+               }
+               case IR_LOAD_EXPRESSION_GET_INDEX:
+               {
+                       struct load_op *insn;
+                       uint32_t insn_len = sizeof(struct load_op)
+                               + sizeof(struct get_index_u64);
+                       struct get_index_u64 index;
+                       int ret;
+
+                       insn = calloc(insn_len, 1);
+                       if (!insn)
+                               return -ENOMEM;
+                       insn->op = FILTER_OP_GET_INDEX_U64;
+                       index.index = op->u.index;
+                       memcpy(insn->data, &index, sizeof(index));
+                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+                       free(insn);
+                       if (ret) {
+                               return ret;
+                       }
+                       break;
+               }
+               case IR_LOAD_EXPRESSION_LOAD_FIELD:
+               {
+                       struct load_op *insn;
+                       uint32_t insn_len = sizeof(struct load_op);
+                       int ret;
+
+                       insn = calloc(insn_len, 1);
+                       if (!insn)
+                               return -ENOMEM;
+                       insn->op = FILTER_OP_LOAD_FIELD;
+                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+                       free(insn);
+                       if (ret) {
+                               return ret;
+                       }
+                       break;
+               }
+               }
+       }
+       return 0;
+}
+
+static
+int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+       int ret;
+
+       switch (node->data_type) {
+       case IR_DATA_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] Unknown data type in %s\n",
+                       __func__);
+               return -EINVAL;
+
+       case IR_DATA_STRING:
+       {
+               struct load_op *insn;
+               uint32_t insn_len = sizeof(struct load_op)
+                       + strlen(node->u.load.u.string.value) + 1;
+
+               insn = calloc(insn_len, 1);
+               if (!insn)
+                       return -ENOMEM;
+
+               switch (node->u.load.u.string.type) {
+               case IR_LOAD_STRING_TYPE_GLOB_STAR:
+                       /*
+                        * We explicitly tell the interpreter here that
+                        * this load is a full star globbing pattern so
+                        * that the appropriate matching function can be
+                        * called. Also, see comment below.
+                        */
+                       insn->op = FILTER_OP_LOAD_STAR_GLOB_STRING;
+                       break;
+               default:
+                       /*
+                        * This is the "legacy" string, which includes
+                        * star globbing patterns with a star only at
+                        * the end. Both "plain" and "star at the end"
+                        * literal strings are handled at the same place
+                        * by the tracer's filter bytecode interpreter,
+                        * whereas full star globbing patterns (stars
+                        * can be anywhere in the string) is a special
+                        * case.
+                        */
+                       insn->op = FILTER_OP_LOAD_STRING;
+                       break;
+               }
+
+               strcpy(insn->data, node->u.load.u.string.value);
+               ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+               free(insn);
+               return ret;
+       }
+       case IR_DATA_NUMERIC:
+       {
+               struct load_op *insn;
+               uint32_t insn_len = sizeof(struct load_op)
+                       + sizeof(struct literal_numeric);
+
+               insn = calloc(insn_len, 1);
+               if (!insn)
+                       return -ENOMEM;
+               insn->op = FILTER_OP_LOAD_S64;
+               memcpy(insn->data, &node->u.load.u.num, sizeof(int64_t));
+               ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+               free(insn);
+               return ret;
+       }
+       case IR_DATA_FLOAT:
+       {
+               struct load_op *insn;
+               uint32_t insn_len = sizeof(struct load_op)
+                       + sizeof(struct literal_double);
+
+               insn = calloc(insn_len, 1);
+               if (!insn)
+                       return -ENOMEM;
+               insn->op = FILTER_OP_LOAD_DOUBLE;
+               memcpy(insn->data, &node->u.load.u.flt, sizeof(double));
+               ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+               free(insn);
+               return ret;
+       }
+       case IR_DATA_EXPRESSION:
+               return visit_node_load_expression(ctx, node);
+       }
+}
+
+static
+int visit_node_unary(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+       int ret;
+       struct unary_op insn;
+
+       /* Visit child */
+       ret = recursive_visit_gen_bytecode(ctx, node->u.unary.child);
+       if (ret)
+               return ret;
+
+       /* Generate end of bytecode instruction */
+       switch (node->u.unary.type) {
+       case AST_UNARY_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] Unknown unary node type in %s\n",
+                       __func__);
+               return -EINVAL;
+       case AST_UNARY_PLUS:
+               /* Nothing to do. */
+               return 0;
+       case AST_UNARY_MINUS:
+               insn.op = FILTER_OP_UNARY_MINUS;
+               return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+       case AST_UNARY_NOT:
+               insn.op = FILTER_OP_UNARY_NOT;
+               return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+       case AST_UNARY_BIT_NOT:
+               insn.op = FILTER_OP_UNARY_BIT_NOT;
+               return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+       }
+}
+
+/*
+ * Binary comparator nesting is disallowed. This allows fitting into
+ * only 2 registers.
+ */
+static
+int visit_node_binary(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+       int ret;
+       struct binary_op insn;
+
+       /* Visit child */
+       ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left);
+       if (ret)
+               return ret;
+       ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right);
+       if (ret)
+               return ret;
+
+       switch (node->u.binary.type) {
+       case AST_OP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] Unknown unary node type in %s\n",
+                       __func__);
+               return -EINVAL;
+
+       case AST_OP_AND:
+       case AST_OP_OR:
+               fprintf(stderr, "[error] Unexpected logical node type in %s\n",
+                       __func__);
+               return -EINVAL;
+
+       case AST_OP_MUL:
+               insn.op = FILTER_OP_MUL;
+               break;
+       case AST_OP_DIV:
+               insn.op = FILTER_OP_DIV;
+               break;
+       case AST_OP_MOD:
+               insn.op = FILTER_OP_MOD;
+               break;
+       case AST_OP_PLUS:
+               insn.op = FILTER_OP_PLUS;
+               break;
+       case AST_OP_MINUS:
+               insn.op = FILTER_OP_MINUS;
+               break;
+       case AST_OP_BIT_RSHIFT:
+               insn.op = FILTER_OP_BIT_RSHIFT;
+               break;
+       case AST_OP_BIT_LSHIFT:
+               insn.op = FILTER_OP_BIT_LSHIFT;
+               break;
+       case AST_OP_BIT_AND:
+               insn.op = FILTER_OP_BIT_AND;
+               break;
+       case AST_OP_BIT_OR:
+               insn.op = FILTER_OP_BIT_OR;
+               break;
+       case AST_OP_BIT_XOR:
+               insn.op = FILTER_OP_BIT_XOR;
+               break;
+
+       case AST_OP_EQ:
+               insn.op = FILTER_OP_EQ;
+               break;
+       case AST_OP_NE:
+               insn.op = FILTER_OP_NE;
+               break;
+       case AST_OP_GT:
+               insn.op = FILTER_OP_GT;
+               break;
+       case AST_OP_LT:
+               insn.op = FILTER_OP_LT;
+               break;
+       case AST_OP_GE:
+               insn.op = FILTER_OP_GE;
+               break;
+       case AST_OP_LE:
+               insn.op = FILTER_OP_LE;
+               break;
+       }
+       return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+}
+
+/*
+ * A logical op always return a s64 (1 or 0).
+ */
+static
+int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+       int ret;
+       struct logical_op insn;
+       uint16_t skip_offset_loc;
+       uint16_t target_loc;
+
+       /* Visit left child */
+       ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left);
+       if (ret)
+               return ret;
+       /* Cast to s64 if float or field ref */
+       if ((node->u.binary.left->data_type == IR_DATA_FIELD_REF
+                               || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
+                               || node->u.binary.left->data_type == IR_DATA_EXPRESSION)
+                       || node->u.binary.left->data_type == IR_DATA_FLOAT) {
+               struct cast_op cast_insn;
+
+               if (node->u.binary.left->data_type == IR_DATA_FIELD_REF
+                               || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
+                               || node->u.binary.left->data_type == IR_DATA_EXPRESSION) {
+                       cast_insn.op = FILTER_OP_CAST_TO_S64;
+               } else {
+                       cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;
+               }
+               ret = bytecode_push(&ctx->bytecode, &cast_insn,
+                                       1, sizeof(cast_insn));
+               if (ret)
+                       return ret;
+       }
+       switch (node->u.logical.type) {
+       default:
+               fprintf(stderr, "[error] Unknown node type in %s\n",
+                       __func__);
+               return -EINVAL;
+
+       case AST_OP_AND:
+               insn.op = FILTER_OP_AND;
+               break;
+       case AST_OP_OR:
+               insn.op = FILTER_OP_OR;
+               break;
+       }
+       insn.skip_offset = (uint16_t) -1UL;     /* Temporary */
+       ret = bytecode_push_logical(&ctx->bytecode, &insn, 1, sizeof(insn),
+                       &skip_offset_loc);
+       if (ret)
+               return ret;
+       /* Visit right child */
+       ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right);
+       if (ret)
+               return ret;
+       /* Cast to s64 if float or field ref */
+       if ((node->u.binary.right->data_type == IR_DATA_FIELD_REF
+                               || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
+                               || node->u.binary.right->data_type == IR_DATA_EXPRESSION)
+                       || node->u.binary.right->data_type == IR_DATA_FLOAT) {
+               struct cast_op cast_insn;
+
+               if (node->u.binary.right->data_type == IR_DATA_FIELD_REF
+                               || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
+                               || node->u.binary.right->data_type == IR_DATA_EXPRESSION) {
+                       cast_insn.op = FILTER_OP_CAST_TO_S64;
+               } else {
+                       cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;
+               }
+               ret = bytecode_push(&ctx->bytecode, &cast_insn,
+                                       1, sizeof(cast_insn));
+               if (ret)
+                       return ret;
+       }
+       /* We now know where the logical op can skip. */
+       target_loc = (uint16_t) bytecode_get_len(&ctx->bytecode->b);
+       ret = bytecode_patch(&ctx->bytecode,
+                       &target_loc,                    /* Offset to jump to */
+                       skip_offset_loc,                /* Where to patch */
+                       sizeof(uint16_t));
+       return ret;
+}
+
+/*
+ * Postorder traversal of the tree. We need the children result before
+ * we can evaluate the parent.
+ */
+static
+int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx,
+               struct ir_op *node)
+{
+       switch (node->op) {
+       case IR_OP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] Unknown node type in %s\n",
+                       __func__);
+               return -EINVAL;
+
+       case IR_OP_ROOT:
+               return visit_node_root(ctx, node);
+       case IR_OP_LOAD:
+               return visit_node_load(ctx, node);
+       case IR_OP_UNARY:
+               return visit_node_unary(ctx, node);
+       case IR_OP_BINARY:
+               return visit_node_binary(ctx, node);
+       case IR_OP_LOGICAL:
+               return visit_node_logical(ctx, node);
+       }
+}
+
+LTTNG_HIDDEN
+void filter_bytecode_free(struct filter_parser_ctx *ctx)
+{
+       if (!ctx) {
+               return;
+       }
+
+       if (ctx->bytecode) {
+               free(ctx->bytecode);
+               ctx->bytecode = NULL;
+       }
+
+       if (ctx->bytecode_reloc) {
+               free(ctx->bytecode_reloc);
+               ctx->bytecode_reloc = NULL;
+       }
+}
+
+LTTNG_HIDDEN
+int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx)
+{
+       int ret;
+
+       ret = bytecode_init(&ctx->bytecode);
+       if (ret)
+               return ret;
+       ret = bytecode_init(&ctx->bytecode_reloc);
+       if (ret)
+               goto error;
+       ret = recursive_visit_gen_bytecode(ctx, ctx->ir_root);
+       if (ret)
+               goto error;
+
+       /* Finally, append symbol table to bytecode */
+       ctx->bytecode->b.reloc_table_offset = bytecode_get_len(&ctx->bytecode->b);
+       return bytecode_push(&ctx->bytecode, ctx->bytecode_reloc->b.data,
+                       1, bytecode_get_len(&ctx->bytecode_reloc->b));
+
+error:
+       filter_bytecode_free(ctx);
+       return ret;
+}
diff --git a/src/common/filter/filter-visitor-generate-ir.c b/src/common/filter/filter-visitor-generate-ir.c
new file mode 100644 (file)
index 0000000..11c6b61
--- /dev/null
@@ -0,0 +1,909 @@
+/*
+ * filter-visitor-generate-ir.c
+ *
+ * LTTng filter generate intermediate representation
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+#include <common/macros.h>
+#include <common/string-utils/string-utils.h>
+
+static
+struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx,
+               struct filter_node *node, enum ir_side side);
+
+static
+struct ir_op *make_op_root(struct ir_op *child, enum ir_side side)
+{
+       struct ir_op *op;
+
+       op = calloc(sizeof(struct ir_op), 1);
+       if (!op)
+               return NULL;
+       switch (child->data_type) {
+       case IR_DATA_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] Unknown root child data type\n");
+               free(op);
+               return NULL;
+       case IR_DATA_STRING:
+               fprintf(stderr, "[error] String cannot be root data type\n");
+               free(op);
+               return NULL;
+       case IR_DATA_NUMERIC:
+       case IR_DATA_FIELD_REF:
+       case IR_DATA_GET_CONTEXT_REF:
+       case IR_DATA_EXPRESSION:
+               /* ok */
+               break;
+       }
+       op->op = IR_OP_ROOT;
+       op->side = side;
+       op->data_type = child->data_type;
+       op->signedness = child->signedness;
+       op->u.root.child = child;
+       return op;
+}
+
+static
+enum ir_load_string_type get_literal_string_type(const char *string)
+{
+       assert(string);
+
+       if (strutils_is_star_glob_pattern(string)) {
+               if (strutils_is_star_at_the_end_only_glob_pattern(string)) {
+                       return IR_LOAD_STRING_TYPE_GLOB_STAR_END;
+               }
+
+               return IR_LOAD_STRING_TYPE_GLOB_STAR;
+       }
+
+       return IR_LOAD_STRING_TYPE_PLAIN;
+}
+
+static
+struct ir_op *make_op_load_string(const char *string, enum ir_side side)
+{
+       struct ir_op *op;
+
+       op = calloc(sizeof(struct ir_op), 1);
+       if (!op)
+               return NULL;
+       op->op = IR_OP_LOAD;
+       op->data_type = IR_DATA_STRING;
+       op->signedness = IR_SIGN_UNKNOWN;
+       op->side = side;
+       op->u.load.u.string.type = get_literal_string_type(string);
+       op->u.load.u.string.value = strdup(string);
+       if (!op->u.load.u.string.value) {
+               free(op);
+               return NULL;
+       }
+       return op;
+}
+
+static
+struct ir_op *make_op_load_numeric(int64_t v, enum ir_side side)
+{
+       struct ir_op *op;
+
+       op = calloc(sizeof(struct ir_op), 1);
+       if (!op)
+               return NULL;
+       op->op = IR_OP_LOAD;
+       op->data_type = IR_DATA_NUMERIC;
+       /* TODO: for now, all numeric values are signed */
+       op->signedness = IR_SIGNED;
+       op->side = side;
+       op->u.load.u.num = v;
+       return op;
+}
+
+static
+struct ir_op *make_op_load_float(double v, enum ir_side side)
+{
+       struct ir_op *op;
+
+       op = calloc(sizeof(struct ir_op), 1);
+       if (!op)
+               return NULL;
+       op->op = IR_OP_LOAD;
+       op->data_type = IR_DATA_FLOAT;
+       op->signedness = IR_SIGN_UNKNOWN;
+       op->side = side;
+       op->u.load.u.flt = v;
+       return op;
+}
+
+static
+void free_load_expression(struct ir_load_expression *load_expression)
+{
+       struct ir_load_expression_op *exp_op;
+
+       if (!load_expression)
+               return;
+       exp_op = load_expression->child;
+       for (;;) {
+               struct ir_load_expression_op *prev_exp_op;
+
+               if (!exp_op)
+                       break;
+               switch (exp_op->type) {
+               case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+               case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+               case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+               case IR_LOAD_EXPRESSION_GET_INDEX:
+               case IR_LOAD_EXPRESSION_LOAD_FIELD:
+                       break;
+               case IR_LOAD_EXPRESSION_GET_SYMBOL:
+                       free(exp_op->u.symbol);
+                       break;
+               }
+               prev_exp_op = exp_op;
+               exp_op = exp_op->next;
+               free(prev_exp_op);
+       }
+       free(load_expression);
+}
+
+/*
+ * Returns the first node of the chain, after initializing the next
+ * pointers.
+ */
+static
+struct filter_node *load_expression_get_forward_chain(struct filter_node *node)
+{
+       struct filter_node *prev_node;
+
+       for (;;) {
+               assert(node->type == NODE_EXPRESSION);
+               prev_node = node;
+               node = node->u.expression.prev;
+               if (!node) {
+                       break;
+               }
+               node->u.expression.next = prev_node;
+       }
+       return prev_node;
+}
+
+static
+struct ir_load_expression *create_load_expression(struct filter_node *node)
+{
+       struct ir_load_expression *load_exp;
+       struct ir_load_expression_op *load_exp_op, *prev_op;
+       const char *str;
+
+       /* Get forward chain. */
+       node = load_expression_get_forward_chain(node);
+       if (!node)
+               return NULL;
+       load_exp = calloc(sizeof(struct ir_load_expression), 1);
+       if (!load_exp)
+               return NULL;
+
+       /* Root */
+       load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
+       if (!load_exp_op)
+               goto error;
+       load_exp->child = load_exp_op;
+       str = node->u.expression.u.string;
+       if (!strcmp(str, "$ctx")) {
+               load_exp_op->type = IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT;
+               node = node->u.expression.next;
+               if (!node) {
+                       fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str);
+                       goto error;
+               }
+               str = node->u.expression.u.string;
+       } else if (!strcmp(str, "$app")) {
+               load_exp_op->type = IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT;
+               node = node->u.expression.next;
+               if (!node) {
+                       fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str);
+                       goto error;
+               }
+               str = node->u.expression.u.string;
+       } else if (str[0] == '$') {
+               fprintf(stderr, "[error] Unexpected identifier \'%s\'\n", str);
+               goto error;
+       } else {
+               load_exp_op->type = IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT;
+       }
+
+       for (;;) {
+               struct filter_node *bracket_node;
+
+               prev_op = load_exp_op;
+               load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
+               if (!load_exp_op)
+                       goto error;
+               prev_op->next = load_exp_op;
+               load_exp_op->type = IR_LOAD_EXPRESSION_GET_SYMBOL;
+               load_exp_op->u.symbol = strdup(str);
+               if (!load_exp_op->u.symbol)
+                       goto error;
+
+               /* Explore brackets from current node. */
+               for (bracket_node = node->u.expression.next_bracket;
+                               bracket_node != NULL;
+                               bracket_node = bracket_node->u.expression.next_bracket) {
+                       prev_op = load_exp_op;
+                       load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
+                       if (!load_exp_op)
+                               goto error;
+                       prev_op->next = load_exp_op;
+                       load_exp_op->type = IR_LOAD_EXPRESSION_GET_INDEX;
+                       load_exp_op->u.index = bracket_node->u.expression.u.constant;
+               }
+               /* Go to next chain element. */
+               node = node->u.expression.next;
+               if (!node)
+                       break;
+               str = node->u.expression.u.string;
+       }
+       /* Add final load field */
+       prev_op = load_exp_op;
+       load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
+       if (!load_exp_op)
+               goto error;
+       prev_op->next = load_exp_op;
+       load_exp_op->type = IR_LOAD_EXPRESSION_LOAD_FIELD;
+       return load_exp;
+
+error:
+       free_load_expression(load_exp);
+       return NULL;
+}
+
+static
+struct ir_op *make_op_load_expression(struct filter_node *node,
+               enum ir_side side)
+{
+       struct ir_op *op;
+
+       op = calloc(sizeof(struct ir_op), 1);
+       if (!op)
+               return NULL;
+       op->op = IR_OP_LOAD;
+       op->data_type = IR_DATA_EXPRESSION;
+       op->signedness = IR_SIGN_DYN;
+       op->side = side;
+       op->u.load.u.expression = create_load_expression(node);
+       if (!op->u.load.u.expression) {
+               goto error;
+       }
+       return op;
+
+error:
+       free_load_expression(op->u.load.u.expression);
+       free(op);
+       return NULL;
+}
+
+static
+struct ir_op *make_op_unary(enum unary_op_type unary_op_type,
+                       const char *op_str, enum ir_op_signedness signedness,
+                       struct ir_op *child, enum ir_side side)
+{
+       struct ir_op *op = NULL;
+
+       if (child->data_type == IR_DATA_STRING) {
+               fprintf(stderr, "[error] unary operation '%s' not allowed on string literal\n", op_str);
+               goto error;
+       }
+
+       op = calloc(sizeof(struct ir_op), 1);
+       if (!op)
+               return NULL;
+       op->op = IR_OP_UNARY;
+       op->data_type = child->data_type;
+       op->signedness = signedness;
+       op->side = side;
+       op->u.unary.type = unary_op_type;
+       op->u.unary.child = child;
+       return op;
+
+error:
+       free(op);
+       return NULL;
+}
+
+/*
+ * unary + is pretty much useless.
+ */
+static
+struct ir_op *make_op_unary_plus(struct ir_op *child, enum ir_side side)
+{
+       return make_op_unary(AST_UNARY_PLUS, "+", child->signedness,
+                       child, side);
+}
+
+static
+struct ir_op *make_op_unary_minus(struct ir_op *child, enum ir_side side)
+{
+       return make_op_unary(AST_UNARY_MINUS, "-", child->signedness,
+                       child, side);
+}
+
+static
+struct ir_op *make_op_unary_not(struct ir_op *child, enum ir_side side)
+{
+       return make_op_unary(AST_UNARY_NOT, "!", child->signedness,
+                       child, side);
+}
+
+static
+struct ir_op *make_op_unary_bit_not(struct ir_op *child, enum ir_side side)
+{
+       return make_op_unary(AST_UNARY_BIT_NOT, "~", child->signedness,
+                       child, side);
+}
+
+static
+struct ir_op *make_op_binary_compare(enum op_type bin_op_type,
+               const char *op_str, struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       struct ir_op *op = NULL;
+
+       if (left->data_type == IR_DATA_UNKNOWN
+               || right->data_type == IR_DATA_UNKNOWN) {
+               fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str);
+               goto error;
+
+       }
+       if ((left->data_type == IR_DATA_STRING
+               && (right->data_type == IR_DATA_NUMERIC || right->data_type == IR_DATA_FLOAT))
+               || ((left->data_type == IR_DATA_NUMERIC || left->data_type == IR_DATA_FLOAT) &&
+                       right->data_type == IR_DATA_STRING)) {
+               fprintf(stderr, "[error] binary operation '%s' operand type mismatch\n", op_str);
+               goto error;
+       }
+
+       op = calloc(sizeof(struct ir_op), 1);
+       if (!op)
+               return NULL;
+       op->op = IR_OP_BINARY;
+       op->u.binary.type = bin_op_type;
+       op->u.binary.left = left;
+       op->u.binary.right = right;
+
+       /* we return a boolean, represented as signed numeric */
+       op->data_type = IR_DATA_NUMERIC;
+       op->signedness = IR_SIGNED;
+       op->side = side;
+
+       return op;
+
+error:
+       free(op);
+       return NULL;
+}
+
+static
+struct ir_op *make_op_binary_eq(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_compare(AST_OP_EQ, "==", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_ne(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_compare(AST_OP_NE, "!=", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_gt(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_compare(AST_OP_GT, ">", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_lt(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_compare(AST_OP_LT, "<", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_ge(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_compare(AST_OP_GE, ">=", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_le(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_compare(AST_OP_LE, "<=", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_logical(enum op_type bin_op_type,
+               const char *op_str, struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       struct ir_op *op = NULL;
+
+       if (left->data_type == IR_DATA_UNKNOWN
+               || right->data_type == IR_DATA_UNKNOWN) {
+               fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str);
+               goto error;
+
+       }
+       if (left->data_type == IR_DATA_STRING
+               || right->data_type == IR_DATA_STRING) {
+               fprintf(stderr, "[error] logical binary operation '%s' cannot have string operand\n", op_str);
+               goto error;
+       }
+
+       op = calloc(sizeof(struct ir_op), 1);
+       if (!op)
+               return NULL;
+       op->op = IR_OP_LOGICAL;
+       op->u.binary.type = bin_op_type;
+       op->u.binary.left = left;
+       op->u.binary.right = right;
+
+       /* we return a boolean, represented as signed numeric */
+       op->data_type = IR_DATA_NUMERIC;
+       op->signedness = IR_SIGNED;
+       op->side = side;
+
+       return op;
+
+error:
+       free(op);
+       return NULL;
+}
+
+static
+struct ir_op *make_op_binary_bitwise(enum op_type bin_op_type,
+               const char *op_str, struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       struct ir_op *op = NULL;
+
+       if (left->data_type == IR_DATA_UNKNOWN
+               || right->data_type == IR_DATA_UNKNOWN) {
+               fprintf(stderr, "[error] bitwise binary operation '%s' has unknown operand type\n", op_str);
+               goto error;
+
+       }
+       if (left->data_type == IR_DATA_STRING
+               || right->data_type == IR_DATA_STRING) {
+               fprintf(stderr, "[error] bitwise binary operation '%s' cannot have string operand\n", op_str);
+               goto error;
+       }
+       if (left->data_type == IR_DATA_FLOAT
+               || right->data_type == IR_DATA_FLOAT) {
+               fprintf(stderr, "[error] bitwise binary operation '%s' cannot have floating point operand\n", op_str);
+               goto error;
+       }
+
+       op = calloc(sizeof(struct ir_op), 1);
+       if (!op)
+               return NULL;
+       op->op = IR_OP_BINARY;
+       op->u.binary.type = bin_op_type;
+       op->u.binary.left = left;
+       op->u.binary.right = right;
+
+       /* we return a signed numeric */
+       op->data_type = IR_DATA_NUMERIC;
+       op->signedness = IR_SIGNED;
+       op->side = side;
+
+       return op;
+
+error:
+       free(op);
+       return NULL;
+}
+
+static
+struct ir_op *make_op_binary_logical_and(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_logical(AST_OP_AND, "&&", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_logical_or(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_logical(AST_OP_OR, "||", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_rshift(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_bitwise(AST_OP_BIT_RSHIFT, ">>", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_lshift(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_bitwise(AST_OP_BIT_LSHIFT, "<<", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_and(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_bitwise(AST_OP_BIT_AND, "&", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_or(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_bitwise(AST_OP_BIT_OR, "|", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_xor(struct ir_op *left, struct ir_op *right,
+               enum ir_side side)
+{
+       return make_op_binary_bitwise(AST_OP_BIT_XOR, "^", left, right, side);
+}
+
+static
+void filter_free_ir_recursive(struct ir_op *op)
+{
+       if (!op)
+               return;
+       switch (op->op) {
+       case IR_OP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] Unknown op type in %s\n",
+                       __func__);
+               break;
+       case IR_OP_ROOT:
+               filter_free_ir_recursive(op->u.root.child);
+               break;
+       case IR_OP_LOAD:
+               switch (op->data_type) {
+               case IR_DATA_STRING:
+                       free(op->u.load.u.string.value);
+                       break;
+               case IR_DATA_FIELD_REF:         /* fall-through */
+               case IR_DATA_GET_CONTEXT_REF:
+                       free(op->u.load.u.ref);
+                       break;
+               case IR_DATA_EXPRESSION:
+                       free_load_expression(op->u.load.u.expression);
+               default:
+                       break;
+               }
+               break;
+       case IR_OP_UNARY:
+               filter_free_ir_recursive(op->u.unary.child);
+               break;
+       case IR_OP_BINARY:
+               filter_free_ir_recursive(op->u.binary.left);
+               filter_free_ir_recursive(op->u.binary.right);
+               break;
+       case IR_OP_LOGICAL:
+               filter_free_ir_recursive(op->u.logical.left);
+               filter_free_ir_recursive(op->u.logical.right);
+               break;
+       }
+       free(op);
+}
+
+static
+struct ir_op *make_expression(struct filter_parser_ctx *ctx,
+               struct filter_node *node, enum ir_side side)
+{
+       switch (node->u.expression.type) {
+       case AST_EXP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown expression type\n", __func__);
+               return NULL;
+
+       case AST_EXP_STRING:
+               return make_op_load_string(node->u.expression.u.string, side);
+       case AST_EXP_CONSTANT:
+               return make_op_load_numeric(node->u.expression.u.constant,
+                                       side);
+       case AST_EXP_FLOAT_CONSTANT:
+               return make_op_load_float(node->u.expression.u.float_constant,
+                                       side);
+       case AST_EXP_IDENTIFIER:
+       case AST_EXP_GLOBAL_IDENTIFIER:
+               return make_op_load_expression(node, side);
+       case AST_EXP_NESTED:
+               return generate_ir_recursive(ctx, node->u.expression.u.child,
+                                       side);
+       }
+}
+
+static
+struct ir_op *make_op(struct filter_parser_ctx *ctx,
+               struct filter_node *node, enum ir_side side)
+{
+       struct ir_op *op = NULL, *lchild, *rchild;
+       const char *op_str = "?";
+
+       switch (node->u.op.type) {
+       case AST_OP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown binary op type\n", __func__);
+               return NULL;
+
+       /*
+        * The following binary operators other than comparators and
+        * logical and/or are not supported yet.
+        */
+       case AST_OP_MUL:
+               op_str = "*";
+               goto error_not_supported;
+       case AST_OP_DIV:
+               op_str = "/";
+               goto error_not_supported;
+       case AST_OP_MOD:
+               op_str = "%";
+               goto error_not_supported;
+       case AST_OP_PLUS:
+               op_str = "+";
+               goto error_not_supported;
+       case AST_OP_MINUS:
+               op_str = "-";
+               goto error_not_supported;
+
+       case AST_OP_BIT_RSHIFT:
+       case AST_OP_BIT_LSHIFT:
+       case AST_OP_BIT_AND:
+       case AST_OP_BIT_OR:
+       case AST_OP_BIT_XOR:
+               lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
+               if (!lchild)
+                       return NULL;
+               rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT);
+               if (!rchild) {
+                       filter_free_ir_recursive(lchild);
+                       return NULL;
+               }
+               break;
+
+       case AST_OP_EQ:
+       case AST_OP_NE:
+       case AST_OP_GT:
+       case AST_OP_LT:
+       case AST_OP_GE:
+       case AST_OP_LE:
+               lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
+               if (!lchild)
+                       return NULL;
+               rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT);
+               if (!rchild) {
+                       filter_free_ir_recursive(lchild);
+                       return NULL;
+               }
+               break;
+
+       case AST_OP_AND:
+       case AST_OP_OR:
+               /*
+                * Both children considered as left, since we need to
+                * populate R0.
+                */
+               lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
+               if (!lchild)
+                       return NULL;
+               rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_LEFT);
+               if (!rchild) {
+                       filter_free_ir_recursive(lchild);
+                       return NULL;
+               }
+               break;
+       }
+
+       switch (node->u.op.type) {
+       case AST_OP_AND:
+               op = make_op_binary_logical_and(lchild, rchild, side);
+               break;
+       case AST_OP_OR:
+               op = make_op_binary_logical_or(lchild, rchild, side);
+               break;
+       case AST_OP_EQ:
+               op = make_op_binary_eq(lchild, rchild, side);
+               break;
+       case AST_OP_NE:
+               op = make_op_binary_ne(lchild, rchild, side);
+               break;
+       case AST_OP_GT:
+               op = make_op_binary_gt(lchild, rchild, side);
+               break;
+       case AST_OP_LT:
+               op = make_op_binary_lt(lchild, rchild, side);
+               break;
+       case AST_OP_GE:
+               op = make_op_binary_ge(lchild, rchild, side);
+               break;
+       case AST_OP_LE:
+               op = make_op_binary_le(lchild, rchild, side);
+               break;
+       case AST_OP_BIT_RSHIFT:
+               op = make_op_binary_bitwise_rshift(lchild, rchild, side);
+               break;
+       case AST_OP_BIT_LSHIFT:
+               op = make_op_binary_bitwise_lshift(lchild, rchild, side);
+               break;
+       case AST_OP_BIT_AND:
+               op = make_op_binary_bitwise_and(lchild, rchild, side);
+               break;
+       case AST_OP_BIT_OR:
+               op = make_op_binary_bitwise_or(lchild, rchild, side);
+               break;
+       case AST_OP_BIT_XOR:
+               op = make_op_binary_bitwise_xor(lchild, rchild, side);
+               break;
+       default:
+               break;
+       }
+
+       if (!op) {
+               filter_free_ir_recursive(rchild);
+               filter_free_ir_recursive(lchild);
+       }
+       return op;
+
+error_not_supported:
+       fprintf(stderr, "[error] %s: binary operation '%s' not supported\n",
+               __func__, op_str);
+       return NULL;
+}
+
+static
+struct ir_op *make_unary_op(struct filter_parser_ctx *ctx,
+               struct filter_node *node, enum ir_side side)
+{
+       switch (node->u.unary_op.type) {
+       case AST_UNARY_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown unary op type\n", __func__);
+               return NULL;
+
+       case AST_UNARY_PLUS:
+       {
+               struct ir_op *op, *child;
+
+               child = generate_ir_recursive(ctx, node->u.unary_op.child,
+                                       side);
+               if (!child)
+                       return NULL;
+               op = make_op_unary_plus(child, side);
+               if (!op) {
+                       filter_free_ir_recursive(child);
+                       return NULL;
+               }
+               return op;
+       }
+       case AST_UNARY_MINUS:
+       {
+               struct ir_op *op, *child;
+
+               child = generate_ir_recursive(ctx, node->u.unary_op.child,
+                                       side);
+               if (!child)
+                       return NULL;
+               op = make_op_unary_minus(child, side);
+               if (!op) {
+                       filter_free_ir_recursive(child);
+                       return NULL;
+               }
+               return op;
+       }
+       case AST_UNARY_NOT:
+       {
+               struct ir_op *op, *child;
+
+               child = generate_ir_recursive(ctx, node->u.unary_op.child,
+                                       side);
+               if (!child)
+                       return NULL;
+               op = make_op_unary_not(child, side);
+               if (!op) {
+                       filter_free_ir_recursive(child);
+                       return NULL;
+               }
+               return op;
+       }
+       case AST_UNARY_BIT_NOT:
+       {
+               struct ir_op *op, *child;
+
+               child = generate_ir_recursive(ctx, node->u.unary_op.child,
+                                       side);
+               if (!child)
+                       return NULL;
+               op = make_op_unary_bit_not(child, side);
+               if (!op) {
+                       filter_free_ir_recursive(child);
+                       return NULL;
+               }
+               return op;
+       }
+       }
+
+       return NULL;
+}
+
+static
+struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx,
+               struct filter_node *node, enum ir_side side)
+{
+       switch (node->type) {
+       case NODE_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown node type\n", __func__);
+               return NULL;
+
+       case NODE_ROOT:
+       {
+               struct ir_op *op, *child;
+
+               child = generate_ir_recursive(ctx, node->u.root.child,
+                                       side);
+               if (!child)
+                       return NULL;
+               op = make_op_root(child, side);
+               if (!op) {
+                       filter_free_ir_recursive(child);
+                       return NULL;
+               }
+               return op;
+       }
+       case NODE_EXPRESSION:
+               return make_expression(ctx, node, side);
+       case NODE_OP:
+               return make_op(ctx, node, side);
+       case NODE_UNARY_OP:
+               return make_unary_op(ctx, node, side);
+       }
+       return 0;
+}
+
+LTTNG_HIDDEN
+void filter_ir_free(struct filter_parser_ctx *ctx)
+{
+       filter_free_ir_recursive(ctx->ir_root);
+       ctx->ir_root = NULL;
+}
+
+LTTNG_HIDDEN
+int filter_visitor_ir_generate(struct filter_parser_ctx *ctx)
+{
+       struct ir_op *op;
+
+       op = generate_ir_recursive(ctx, &ctx->ast->root, IR_LEFT);
+       if (!op) {
+               return -EINVAL;
+       }
+       ctx->ir_root = op;
+       return 0;
+}
diff --git a/src/common/filter/filter-visitor-ir-check-binary-comparator.c b/src/common/filter/filter-visitor-ir-check-binary-comparator.c
new file mode 100644 (file)
index 0000000..bcbb69e
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * filter-visitor-ir-check-binary-comparator.c
+ *
+ * LTTng filter IR check binary comparator
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+static
+int check_bin_comparator(struct ir_op *node)
+{
+       switch (node->op) {
+       case IR_OP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+               return -EINVAL;
+
+       case IR_OP_ROOT:
+               return check_bin_comparator(node->u.root.child);
+       case IR_OP_LOAD:
+               return 0;
+       case IR_OP_UNARY:
+               return check_bin_comparator(node->u.unary.child);
+       case IR_OP_BINARY:
+       {
+               int ret;
+
+               if (node->u.binary.left->data_type == IR_DATA_STRING
+                               || node->u.binary.right->data_type
+                                       == IR_DATA_STRING) {
+                       if (node->u.binary.type != AST_OP_EQ
+                                       && node->u.binary.type != AST_OP_NE) {
+                               fprintf(stderr, "[error] Only '==' and '!=' comparators are allowed for strings\n");
+                               return -EINVAL;
+                       }
+               }
+
+               ret = check_bin_comparator(node->u.binary.left);
+               if (ret)
+                       return ret;
+               return check_bin_comparator(node->u.binary.right);
+       }
+       case IR_OP_LOGICAL:
+       {
+               int ret;
+
+               ret = check_bin_comparator(node->u.logical.left);
+               if (ret)
+                       return ret;
+               return check_bin_comparator(node->u.logical.right);
+       }
+       }
+}
+
+int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx)
+{
+       return check_bin_comparator(ctx->ir_root);
+}
diff --git a/src/common/filter/filter-visitor-ir-check-binary-op-nesting.c b/src/common/filter/filter-visitor-ir-check-binary-op-nesting.c
new file mode 100644 (file)
index 0000000..04a5fbd
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * filter-visitor-ir-check-binary-op-nesting.c
+ *
+ * LTTng filter IR check binary op nesting
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+#include <common/macros.h>
+
+static
+int check_bin_op_nesting_recursive(struct ir_op *node, int nesting)
+{
+       switch (node->op) {
+       case IR_OP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+               return -EINVAL;
+
+       case IR_OP_ROOT:
+               return check_bin_op_nesting_recursive(node->u.root.child,
+                               nesting);
+       case IR_OP_LOAD:
+               return 0;
+       case IR_OP_UNARY:
+               return check_bin_op_nesting_recursive(node->u.unary.child,
+                               nesting);
+       case IR_OP_BINARY:
+       {
+               int ret;
+
+               ret = check_bin_op_nesting_recursive(node->u.binary.left,
+                               nesting + 1);
+               if (ret)
+                       return ret;
+               return check_bin_op_nesting_recursive(node->u.binary.right,
+                               nesting + 1);
+       }
+       case IR_OP_LOGICAL:
+       {
+               int ret;
+
+               ret = check_bin_op_nesting_recursive(node->u.logical.left,
+                               nesting);
+               if (ret)
+                       return ret;
+               return check_bin_op_nesting_recursive(node->u.logical.right,
+                               nesting);
+       }
+       }
+}
+
+LTTNG_HIDDEN
+int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx)
+{
+       return check_bin_op_nesting_recursive(ctx->ir_root, 0);
+}
diff --git a/src/common/filter/filter-visitor-ir-normalize-glob-patterns.c b/src/common/filter/filter-visitor-ir-normalize-glob-patterns.c
new file mode 100644 (file)
index 0000000..5e2778e
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * filter-visitor-ir-normalize-glob-patterns.c
+ *
+ * LTTng filter IR normalize string
+ *
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <common/macros.h>
+#include <common/string-utils/string-utils.h>
+
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+static
+int normalize_glob_patterns(struct ir_op *node)
+{
+       switch (node->op) {
+       case IR_OP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+               return -EINVAL;
+
+       case IR_OP_ROOT:
+               return normalize_glob_patterns(node->u.root.child);
+       case IR_OP_LOAD:
+       {
+               if (node->data_type == IR_DATA_STRING) {
+                       enum ir_load_string_type type =
+                               node->u.load.u.string.type;
+                       if (type == IR_LOAD_STRING_TYPE_GLOB_STAR_END ||
+                                       type == IR_LOAD_STRING_TYPE_GLOB_STAR) {
+                               assert(node->u.load.u.string.value);
+                               strutils_normalize_star_glob_pattern(
+                                       node->u.load.u.string.value);
+                       }
+               }
+
+               return 0;
+       }
+       case IR_OP_UNARY:
+               return normalize_glob_patterns(node->u.unary.child);
+       case IR_OP_BINARY:
+       {
+               int ret = normalize_glob_patterns(node->u.binary.left);
+
+               if (ret)
+                       return ret;
+               return normalize_glob_patterns(node->u.binary.right);
+       }
+       case IR_OP_LOGICAL:
+       {
+               int ret;
+
+               ret = normalize_glob_patterns(node->u.logical.left);
+               if (ret)
+                       return ret;
+               return normalize_glob_patterns(node->u.logical.right);
+       }
+       }
+}
+
+/*
+ * This function normalizes all the globbing literal strings with
+ * utils_normalize_glob_pattern(). See the documentation of
+ * utils_normalize_glob_pattern() for more details.
+ */
+LTTNG_HIDDEN
+int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx)
+{
+       return normalize_glob_patterns(ctx->ir_root);
+}
diff --git a/src/common/filter/filter-visitor-ir-validate-globbing.c b/src/common/filter/filter-visitor-ir-validate-globbing.c
new file mode 100644 (file)
index 0000000..3e749a4
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * filter-visitor-ir-validate-globbing.c
+ *
+ * LTTng filter IR validate globbing
+ *
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <common/macros.h>
+
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+static
+int validate_globbing(struct ir_op *node)
+{
+       int ret;
+
+       switch (node->op) {
+       case IR_OP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+               return -EINVAL;
+
+       case IR_OP_ROOT:
+               return validate_globbing(node->u.root.child);
+       case IR_OP_LOAD:
+               return 0;
+       case IR_OP_UNARY:
+               return validate_globbing(node->u.unary.child);
+       case IR_OP_BINARY:
+       {
+               struct ir_op *left = node->u.binary.left;
+               struct ir_op *right = node->u.binary.right;
+
+               if (left->op == IR_OP_LOAD && right->op == IR_OP_LOAD &&
+                               left->data_type == IR_DATA_STRING &&
+                               right->data_type == IR_DATA_STRING) {
+                       /* Test 1. */
+                       if (left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
+                                       right->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
+                               fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
+                               return -1;
+                       }
+
+                       if (right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
+                                       left->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
+                               fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
+                               return -1;
+                       }
+               }
+
+               if ((left->op == IR_OP_LOAD && left->data_type == IR_DATA_STRING) ||
+                               (right->op == IR_OP_LOAD && right->data_type == IR_DATA_STRING)) {
+                       if ((left->op == IR_OP_LOAD && left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR) ||
+                                       (right->op == IR_OP_LOAD && right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR)) {
+                               /* Test 2. */
+                               if (node->u.binary.type != AST_OP_EQ &&
+                                               node->u.binary.type != AST_OP_NE) {
+                                       fprintf(stderr, "[error] Only the `==` and `!=` operators are allowed with a globbing pattern\n");
+                                       return -1;
+                               }
+                       }
+               }
+
+               ret = validate_globbing(left);
+               if (ret) {
+                       return ret;
+               }
+
+               return validate_globbing(right);
+       }
+       case IR_OP_LOGICAL:
+               ret = validate_globbing(node->u.logical.left);
+               if (ret)
+                       return ret;
+               return validate_globbing(node->u.logical.right);
+       }
+}
+
+/*
+ * This function recursively validates that:
+ *
+ * 1. When there's a binary operation between two literal strings,
+ *    if one of them has the IR_LOAD_STRING_TYPE_GLOB_STAR type,
+ *    the other one has the IR_LOAD_STRING_TYPE_PLAIN type.
+ *
+ *    In other words, you cannot compare two globbing patterns, except
+ *    for two globbing patterns with only a star at the end for backward
+ *    compatibility reasons.
+ *
+ * 2. When there's a binary operation between two literal strings, if
+ *    one of them is a (full) star globbing pattern, the binary
+ *    operation is either == or !=.
+ */
+LTTNG_HIDDEN
+int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx)
+{
+       return validate_globbing(ctx->ir_root);
+}
diff --git a/src/common/filter/filter-visitor-ir-validate-string.c b/src/common/filter/filter-visitor-ir-validate-string.c
new file mode 100644 (file)
index 0000000..1df83b0
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * filter-visitor-ir-validate-string.c
+ *
+ * LTTng filter IR validate string
+ *
+ * Copyright 2014 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <common/macros.h>
+
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+enum parse_char_result {
+       PARSE_CHAR_UNKNOWN = -2,
+       PARSE_CHAR_WILDCARD = -1,
+       PARSE_CHAR_NORMAL = 0,
+};
+
+static
+enum parse_char_result parse_char(const char **p)
+{
+       switch (**p) {
+       case '\\':
+               (*p)++;
+               switch (**p) {
+               case '\\':
+               case '*':
+                       return PARSE_CHAR_NORMAL;
+               default:
+                       return PARSE_CHAR_UNKNOWN;
+               }
+       case '*':
+               return PARSE_CHAR_WILDCARD;
+       default:
+               return PARSE_CHAR_NORMAL;
+       }
+}
+
+static
+int validate_string(struct ir_op *node)
+{
+       switch (node->op) {
+       case IR_OP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+               return -EINVAL;
+
+       case IR_OP_ROOT:
+               return validate_string(node->u.root.child);
+       case IR_OP_LOAD:
+       {
+               int ret = 0;
+
+               if (node->data_type == IR_DATA_STRING) {
+                       const char *str;
+
+                       assert(node->u.load.u.string.value);
+                       str = node->u.load.u.string.value;
+
+                       for (;;) {
+                               enum parse_char_result res;
+
+                               if (!(*str)) {
+                                       break;
+                               }
+
+                               res = parse_char(&str);
+                               str++;
+
+                               switch (res) {
+                               case PARSE_CHAR_UNKNOWN:
+                                       ret = -EINVAL;
+                                       fprintf(stderr,
+                                               "Unsupported escape character detected.\n");
+                                       goto end_load;
+                               case PARSE_CHAR_NORMAL:
+                               default:
+                                       break;
+                               }
+                       }
+               }
+end_load:
+               return ret;
+       }
+       case IR_OP_UNARY:
+               return validate_string(node->u.unary.child);
+       case IR_OP_BINARY:
+       {
+               int ret = validate_string(node->u.binary.left);
+
+               if (ret)
+                       return ret;
+               return validate_string(node->u.binary.right);
+       }
+       case IR_OP_LOGICAL:
+       {
+               int ret;
+
+               ret = validate_string(node->u.logical.left);
+               if (ret)
+                       return ret;
+               return validate_string(node->u.logical.right);
+       }
+       }
+}
+
+LTTNG_HIDDEN
+int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx)
+{
+       return validate_string(ctx->ir_root);
+}
diff --git a/src/common/filter/filter-visitor-xml.c b/src/common/filter/filter-visitor-xml.c
new file mode 100644 (file)
index 0000000..d5ff0c1
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * filter-visitor-xml.c
+ *
+ * LTTng filter XML pretty printer visitor
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+
+#include <common/macros.h>
+
+#define fprintf_dbg(fd, fmt, args...)  fprintf(fd, "%s: " fmt, __func__, ## args)
+
+static
+int recursive_visit_print(struct filter_node *node, FILE *stream, int indent);
+
+static
+void print_tabs(FILE *fd, int depth)
+{
+       int i;
+
+       for (i = 0; i < depth; i++)
+               fprintf(fd, "\t");
+}
+
+static
+int recursive_visit_print_expression(struct filter_node *node,
+               FILE *stream, int indent)
+{
+       struct filter_node *iter_node;
+
+       if (!node) {
+               fprintf(stderr, "[error] %s: NULL child\n", __func__);
+               return -EINVAL;
+       }
+       switch (node->u.expression.type) {
+       case AST_EXP_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown expression\n", __func__);
+               return -EINVAL;
+       case AST_EXP_STRING:
+               print_tabs(stream, indent);
+               fprintf(stream, "<string value=\"%s\"/>\n",
+                       node->u.expression.u.string);
+               break;
+       case AST_EXP_CONSTANT:
+               print_tabs(stream, indent);
+               fprintf(stream, "<constant value=\"%" PRIu64 "\"/>\n",
+                       node->u.expression.u.constant);
+               break;
+       case AST_EXP_FLOAT_CONSTANT:
+               print_tabs(stream, indent);
+               fprintf(stream, "<float_constant value=\"%lg\"/>\n",
+                       node->u.expression.u.float_constant);
+               break;
+       case AST_EXP_IDENTIFIER:                /* fall-through */
+       case AST_EXP_GLOBAL_IDENTIFIER:
+               print_tabs(stream, indent);
+               fprintf(stream, "<%s value=\"%s\"/>\n",
+                       node->u.expression.type == AST_EXP_IDENTIFIER ?
+                               "identifier" : "global_identifier",
+                       node->u.expression.u.identifier);
+               iter_node = node->u.expression.next;
+               while (iter_node) {
+                       print_tabs(stream, indent);
+                       fprintf(stream, "<bracket>\n");
+                       if (recursive_visit_print_expression(iter_node,
+                                       stream, indent + 1)) {
+                               return -EINVAL;
+                       }
+                       print_tabs(stream, indent);
+                       fprintf(stream, "</bracket>\n");
+                       iter_node = iter_node->u.expression.next;
+
+               }
+               break;
+       case AST_EXP_NESTED:
+               return recursive_visit_print(node->u.expression.u.child,
+                               stream, indent + 1);
+       }
+       return 0;
+}
+
+
+static
+int recursive_visit_print(struct filter_node *node, FILE *stream, int indent)
+{
+       int ret;
+
+       if (!node) {
+               fprintf(stderr, "[error] %s: NULL child\n", __func__);
+               return -EINVAL;
+       }
+       switch (node->type) {
+       case NODE_UNKNOWN:
+       default:
+               fprintf(stderr, "[error] %s: unknown node type\n", __func__);
+               return -EINVAL;
+       case NODE_ROOT:
+               print_tabs(stream, indent);
+               fprintf(stream, "<root>\n");
+               ret = recursive_visit_print(node->u.root.child, stream,
+                                       indent + 1);
+               print_tabs(stream, indent);
+               fprintf(stream, "</root>\n");
+               return ret;
+       case NODE_EXPRESSION:
+               print_tabs(stream, indent);
+               fprintf(stream, "<expression>\n");
+               ret = recursive_visit_print_expression(node, stream,
+                                       indent + 1);
+               print_tabs(stream, indent);
+               fprintf(stream, "</expression>\n");
+               return ret;
+       case NODE_OP:
+               print_tabs(stream, indent);
+               fprintf(stream, "<op type=");
+               switch (node->u.op.type) {
+               case AST_OP_UNKNOWN:
+               default:
+                       fprintf(stderr, "[error] %s: unknown op\n", __func__);
+                       return -EINVAL;
+               case AST_OP_MUL:
+                       fprintf(stream, "\"*\"");
+                       break;
+               case AST_OP_DIV:
+                       fprintf(stream, "\"/\"");
+                       break;
+               case AST_OP_MOD:
+                       fprintf(stream, "\"%%\"");
+                       break;
+               case AST_OP_PLUS:
+                       fprintf(stream, "\"+\"");
+                       break;
+               case AST_OP_MINUS:
+                       fprintf(stream, "\"-\"");
+                       break;
+               case AST_OP_BIT_RSHIFT:
+                       fprintf(stream, "\">>\"");
+                       break;
+               case AST_OP_BIT_LSHIFT:
+                       fprintf(stream, "\"<<\"");
+                       break;
+               case AST_OP_AND:
+                       fprintf(stream, "\"&&\"");
+                       break;
+               case AST_OP_OR:
+                       fprintf(stream, "\"||\"");
+                       break;
+               case AST_OP_BIT_AND:
+                       fprintf(stream, "\"&\"");
+                       break;
+               case AST_OP_BIT_OR:
+                       fprintf(stream, "\"|\"");
+                       break;
+               case AST_OP_BIT_XOR:
+                       fprintf(stream, "\"^\"");
+                       break;
+
+               case AST_OP_EQ:
+                       fprintf(stream, "\"==\"");
+                       break;
+               case AST_OP_NE:
+                       fprintf(stream, "\"!=\"");
+                       break;
+               case AST_OP_GT:
+                       fprintf(stream, "\">\"");
+                       break;
+               case AST_OP_LT:
+                       fprintf(stream, "\"<\"");
+                       break;
+               case AST_OP_GE:
+                       fprintf(stream, "\">=\"");
+                       break;
+               case AST_OP_LE:
+                       fprintf(stream, "\"<=\"");
+                       break;
+               }
+               fprintf(stream, ">\n");
+               ret = recursive_visit_print(node->u.op.lchild,
+                                       stream, indent + 1);
+               if (ret)
+                       return ret;
+               ret = recursive_visit_print(node->u.op.rchild,
+                                       stream, indent + 1);
+               if (ret)
+                       return ret;
+               print_tabs(stream, indent);
+               fprintf(stream, "</op>\n");
+               return ret;
+       case NODE_UNARY_OP:
+               print_tabs(stream, indent);
+               fprintf(stream, "<unary_op type=");
+               switch (node->u.unary_op.type) {
+               case AST_UNARY_UNKNOWN:
+               default:
+                       fprintf(stderr, "[error] %s: unknown unary_op\n", __func__);
+                       return -EINVAL;
+               case AST_UNARY_PLUS:
+                       fprintf(stream, "\"+\"");
+                       break;
+               case AST_UNARY_MINUS:
+                       fprintf(stream, "\"-\"");
+                       break;
+               case AST_UNARY_NOT:
+                       fprintf(stream, "\"!\"");
+                       break;
+               case AST_UNARY_BIT_NOT:
+                       fprintf(stream, "\"~\"");
+                       break;
+               }
+               fprintf(stream, ">\n");
+               ret = recursive_visit_print(node->u.unary_op.child,
+                                       stream, indent + 1);
+               print_tabs(stream, indent);
+               fprintf(stream, "</unary_op>\n");
+               return ret;
+       }
+       return 0;
+}
+
+LTTNG_HIDDEN
+int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream,
+                       int indent)
+{
+       return recursive_visit_print(&ctx->ast->root, stream, indent);
+}
diff --git a/src/common/filter/memstream.h b/src/common/filter/memstream.h
new file mode 100644 (file)
index 0000000..cd246ee
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 (C) Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#ifndef _LTTNG_CTL_MEMSTREAM_H
+#define _LTTNG_CTL_MEMSTREAM_H
+
+#ifdef LTTNG_HAVE_FMEMOPEN
+#include <stdio.h>
+
+static inline
+FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
+{
+       return fmemopen(buf, size, mode);
+}
+
+#else /* LTTNG_HAVE_FMEMOPEN */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * Fallback for systems which don't have fmemopen. Copy buffer to a
+ * temporary file, and use that file as FILE * input.
+ */
+static inline
+FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
+{
+       char tmpname[PATH_MAX];
+       size_t len;
+       FILE *fp;
+       int ret;
+
+       /*
+        * Support reading only.
+        */
+       if (strcmp(mode, "rb") != 0) {
+               return NULL;
+       }
+       strncpy(tmpname, "/tmp/lttng-tmp-XXXXXX", PATH_MAX);
+       ret = mkstemp(tmpname);
+       if (ret < 0) {
+               return NULL;
+       }
+       /*
+        * We need to write to the file.
+        */
+       fp = fdopen(ret, "w+");
+       if (!fp) {
+               goto error_unlink;
+       }
+       /* Copy the entire buffer to the file */
+       len = fwrite(buf, sizeof(char), size, fp);
+       if (len != size) {
+               goto error_close;
+       }
+       ret = fseek(fp, 0L, SEEK_SET);
+       if (ret < 0) {
+               PERROR("fseek");
+               goto error_close;
+       }
+       /* We keep the handle open, but can unlink the file on the VFS. */
+       ret = unlink(tmpname);
+       if (ret < 0) {
+               PERROR("unlink");
+       }
+       return fp;
+
+error_close:
+       ret = fclose(fp);
+       if (ret < 0) {
+               PERROR("close");
+       }
+error_unlink:
+       ret = unlink(tmpname);
+       if (ret < 0) {
+               PERROR("unlink");
+       }
+       return NULL;
+}
+
+#endif /* LTTNG_HAVE_FMEMOPEN */
+
+#endif /* _LTTNG_CTL_MEMSTREAM_H */
index 01af4a297652f7dff1e26202ca7a715741f66315..878929ff0ab0bd94c0df18a4435e21d9caed72cf 100644 (file)
@@ -417,7 +417,22 @@ int kernctl_stop_session(int fd)
                        LTTNG_KERNEL_SESSION_STOP);
 }
 
-int kernctl_filter(int fd, struct lttng_filter_bytecode *filter)
+int kernctl_create_trigger_group(int fd)
+{
+               return LTTNG_IOCTL_NO_CHECK(fd, LTTNG_KERNEL_TRIGGER_GROUP_CREATE);
+}
+
+int kernctl_create_trigger_group_notification_fd(int group_fd)
+{
+               return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_TRIGGER_GROUP_NOTIFICATION_FD);
+}
+
+int kernctl_create_trigger(int group_fd, struct lttng_kernel_trigger *trigger)
+{
+               return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_TRIGGER_CREATE, trigger);
+}
+
+int kernctl_filter(int fd, const struct lttng_filter_bytecode *filter)
 {
        struct lttng_kernel_filter_bytecode *kb;
        uint32_t len;
index f8eac8d5698270a696fae54065980451ecc19882..b3d798ffd4c40cea1196cd942096e1ebfea576e1 100644 (file)
@@ -26,8 +26,14 @@ int kernctl_disable(int fd);
 int kernctl_start_session(int fd);
 int kernctl_stop_session(int fd);
 
+int kernctl_create_trigger_group(int fd);
+
+/* Apply on trigger_group FD*/
+int kernctl_create_trigger_group_notification_fd(int fd);
+int kernctl_create_trigger(int fd, struct lttng_kernel_trigger *trigger);
+
 /* Apply on event FD */
-int kernctl_filter(int fd, struct lttng_filter_bytecode *filter);
+int kernctl_filter(int fd, const struct lttng_filter_bytecode *filter);
 int kernctl_add_callsite(int fd, struct lttng_kernel_event_callsite *callsite);
 
 int kernctl_tracepoint_list(int fd);
index 59d71191f30142a8c207469764344afc526e72d5..65ed69ecd201a00830909b166d7bc15f9c51803a 100644 (file)
 #define LTTNG_KERNEL_TRACER_ABI_VERSION                \
        _IOR(0xF6, 0x4B, struct lttng_kernel_tracer_abi_version)
 
+#define LTTNG_KERNEL_TRIGGER_GROUP_CREATE _IO(0xF6, 0x4C)
+
+/* Trigger group file descriptor ioctl */
+#define LTTNG_KERNEL_TRIGGER_GROUP_NOTIFICATION_FD _IO(0xF6, 0x30)
+#define LTTNG_KERNEL_TRIGGER_CREATE \
+       _IOW(0xF6, 0x31, struct lttng_kernel_trigger)
+
 /* Session FD ioctl */
 #define LTTNG_KERNEL_METADATA                  \
        _IOW(0xF6, 0x54, struct lttng_kernel_channel)
index 816dd0d7621a15a3dc6b2f073c2acc9a583fa02a..bef20ffd4bd52eceb7fec353f885e22e6e9db6d8 100644 (file)
@@ -750,7 +750,7 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
        char *curr_sym_str = NULL;
        char *symbol_table_data = NULL;
        char *string_table_data = NULL;
-       char *string_table_name = NULL;
+       const char *string_table_name = NULL;
        struct lttng_elf_shdr symtab_hdr;
        struct lttng_elf_shdr strtab_hdr;
        struct lttng_elf *elf = NULL;
index d5904e79f5f5c3b8582496c2297ce5bc4b49f900..62e784eb61c3418a9f9aefcb2101c9ee4ceeec25 100644 (file)
@@ -150,6 +150,24 @@ struct lttng_kernel_event {
        } u;
 } LTTNG_PACKED;
 
+#define LTTNG_KERNEL_TRIGGER_PADDING1  16
+#define LTTNG_KERNEL_TRIGGER_PADDING2  LTTNG_KERNEL_SYM_NAME_LEN + 32
+struct lttng_kernel_trigger {
+       uint64_t id;
+       char name[LTTNG_KERNEL_SYM_NAME_LEN];   /* event name */
+       enum lttng_kernel_instrumentation instrumentation;
+       char padding[LTTNG_KERNEL_TRIGGER_PADDING1];
+
+       /* Per instrumentation type configuration */
+       union {
+               struct lttng_kernel_kretprobe kretprobe;
+               struct lttng_kernel_kprobe kprobe;
+               struct lttng_kernel_uprobe uprobe;
+               struct lttng_kernel_function ftrace;
+               char padding[LTTNG_KERNEL_TRIGGER_PADDING2];
+       } u;
+} LTTNG_PACKED;
+
 struct lttng_kernel_tracer_version {
        uint32_t major;
        uint32_t minor;
index 6b32bd9ac70de71e1203c538933d45a53aaa081e..87f1a8a41345014bb404cd3dade439dde3b98936 100644 (file)
@@ -51,8 +51,7 @@ int lttng_notification_serialize(const struct lttng_notification *notification,
        }
 
        size_before_payload = buf->size;
-       ret = lttng_condition_serialize(notification->condition,
-                       buf);
+       ret = lttng_condition_serialize(notification->condition, buf, NULL);
        if (ret) {
                goto end;
        }
diff --git a/src/common/notify.c b/src/common/notify.c
deleted file mode 100644 (file)
index 00d1a0e..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/action/action-internal.h>
-#include <lttng/action/notify-internal.h>
-#include <common/macros.h>
-#include <assert.h>
-
-static
-void lttng_action_notify_destroy(struct lttng_action *action)
-{
-       free(action);
-}
-
-static
-int lttng_action_notify_serialize(struct lttng_action *action,
-               struct lttng_dynamic_buffer *buf)
-{
-       return 0;
-}
-
-struct lttng_action *lttng_action_notify_create(void)
-{
-       struct lttng_action_notify *notify;
-
-       notify = zmalloc(sizeof(struct lttng_action_notify));
-       if (!notify) {
-               goto end;
-       }
-
-       notify->parent.type = LTTNG_ACTION_TYPE_NOTIFY;
-       notify->parent.serialize = lttng_action_notify_serialize;
-       notify->parent.destroy = lttng_action_notify_destroy;
-end:
-       return &notify->parent;
-}
index 7b11c81f63ed8ebfead0d36a919f4e0ec2e06dce..dfcb337a0bcd7859524faf171ed5594ade38b53c 100644 (file)
@@ -499,7 +499,7 @@ int relayd_add_stream(struct lttcomm_relayd_sock *rsock, const char *channel_nam
        int ret;
        struct lttcomm_relayd_status_stream reply;
        char pathname[RELAYD_COMM_LTTNG_PATH_MAX];
-       char *separator;
+       const char *separator;
 
        /* Code flow error. Safety net. */
        assert(rsock);
index 5d63f640e3ca20d12b29ed870e4b96688b9e9043..da2ae71a27a23a6122b1fd10d37f49402a08d3fa 100644 (file)
 #include <common/utils.h>
 #include <common/compat/getenv.h>
 #include <common/compat/prctl.h>
+#include <common/compat/string.h>
 #include <common/unix.h>
 #include <common/defaults.h>
 #include <common/lttng-elf.h>
 
 #include <lttng/constant.h>
 
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/filter/filter-ast.h>
+#include <common/filter/filter-bytecode.h>
+
 #include "runas.h"
 
 struct run_as_data;
@@ -57,6 +62,7 @@ enum run_as_cmd {
        RUN_AS_RENAMEAT,
        RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET,
        RUN_AS_EXTRACT_SDT_PROBE_OFFSETS,
+       RUN_AS_GENERATE_FILTER_BYTECODE,
 };
 
 struct run_as_mkdir_data {
@@ -94,6 +100,10 @@ struct run_as_extract_sdt_probe_offsets_data {
        char provider_name[LTTNG_SYMBOL_NAME_LEN];
 } LTTNG_PACKED;
 
+struct run_as_generate_filter_bytecode_data {
+       char filter_expression[LTTNG_FILTER_MAX_LEN];
+} LTTNG_PATCKED;
+
 struct run_as_rename_data {
        /*
         * [0] = old_dirfd
@@ -117,6 +127,11 @@ struct run_as_extract_sdt_probe_offsets_ret {
        uint64_t offsets[LTTNG_KERNEL_MAX_UPROBE_NUM];
 } LTTNG_PACKED;
 
+struct run_as_generate_filter_bytecode_ret {
+       /* A lttng_bytecode_filter strcut with "dynamic" payload */
+       char bytecode[LTTNG_FILTER_MAX_LEN];
+} LTTNG_PACKED;
+
 struct run_as_data {
        enum run_as_cmd cmd;
        union {
@@ -127,6 +142,7 @@ struct run_as_data {
                struct run_as_rename_data rename;
                struct run_as_extract_elf_symbol_offset_data extract_elf_symbol_offset;
                struct run_as_extract_sdt_probe_offsets_data extract_sdt_probe_offsets;
+               struct run_as_generate_filter_bytecode_data generate_filter_bytecode;
        } u;
        uid_t uid;
        gid_t gid;
@@ -153,6 +169,7 @@ struct run_as_ret {
                struct run_as_open_ret open;
                struct run_as_extract_elf_symbol_offset_ret extract_elf_symbol_offset;
                struct run_as_extract_sdt_probe_offsets_ret extract_sdt_probe_offsets;
+               struct run_as_generate_filter_bytecode_ret generate_filter_bytecode;
        } u;
        int _errno;
        bool _error;
@@ -306,6 +323,13 @@ static const struct run_as_command_properties command_properties[] = {
                .out_fd_count = 0,
                .use_cwd_fd = false,
        },
+       [RUN_AS_GENERATE_FILTER_BYTECODE] = {
+               .in_fds_offset = -1,
+               .in_fd_count = 0,
+               .out_fds_offset = -1,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
 };
 
 struct run_as_worker {
@@ -619,6 +643,47 @@ int _extract_sdt_probe_offsets(struct run_as_data *data,
 }
 #endif
 
+static
+int _generate_filter_bytecode(struct run_as_data *data,
+               struct run_as_ret *ret_value) {
+       int ret = 0;
+       const char *filter_expression = NULL;
+       struct filter_parser_ctx *ctx = NULL;
+
+       ret_value->_error = false;
+
+       filter_expression = data->u.generate_filter_bytecode.filter_expression;
+
+       if (lttng_strnlen(filter_expression, LTTNG_FILTER_MAX_LEN - 1) == LTTNG_FILTER_MAX_LEN - 1) {
+               ret_value->_error = true;
+               ret = -1;
+               goto end;
+       }
+
+       ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx);
+       if (ret < 0) {
+               ret_value->_error = true;
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Size of bytecode generated: %u bytes.",
+                       bytecode_get_len(&ctx->bytecode->b));
+
+       /* Copy the lttng_bytecode_filter object to the return structure */
+       memcpy(ret_value->u.generate_filter_bytecode.bytecode,
+                       &ctx->bytecode->b,
+                       sizeof(ctx->bytecode->b) +
+                                       bytecode_get_len(&ctx->bytecode->b));
+
+end:
+       if (ctx) {
+               filter_bytecode_free(ctx);
+               filter_ir_free(ctx);
+               filter_parser_ctx_free(ctx);
+       }
+       return ret;
+}
 static
 run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
 {
@@ -648,6 +713,8 @@ run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
                return _extract_elf_symbol_offset;
        case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
                return _extract_sdt_probe_offsets;
+       case RUN_AS_GENERATE_FILTER_BYTECODE:
+               return _generate_filter_bytecode;
        default:
                ERR("Unknown command %d", (int) cmd);
                return NULL;
@@ -660,6 +727,11 @@ int do_send_fds(int sock, const int *fds, unsigned int fd_count)
        ssize_t len;
        unsigned int i;
 
+       if (fd_count == 0) {
+               /* Nothing to send */
+               return 0;
+       }
+
        for (i = 0; i < fd_count; i++) {
                if (fds[i] < 0) {
                        ERR("Attempt to send invalid file descriptor to master (fd = %i)",
@@ -680,6 +752,11 @@ int do_recv_fds(int sock, int *fds, unsigned int fd_count)
        unsigned int i;
        ssize_t len;
 
+       if (fd_count == 0) {
+               /* Nothing to receive */
+               goto end;
+       }
+
        len = lttcomm_recv_fds_unix_sock(sock, fds, fd_count);
        if (len == 0) {
                ret = -1;
@@ -1726,6 +1803,50 @@ error:
        return ret;
 }
 
+LTTNG_HIDDEN
+int run_as_generate_filter_bytecode(const char *filter_expression,
+               uid_t uid,
+               gid_t gid,
+               struct lttng_filter_bytecode **bytecode)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+       const struct lttng_filter_bytecode *view_bytecode = NULL;
+       struct lttng_filter_bytecode *local_bytecode = NULL;
+
+       DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d",
+                       filter_expression, (int) uid, (int) gid);
+
+       ret = lttng_strncpy(data.u.generate_filter_bytecode.filter_expression, filter_expression,
+                       sizeof(data.u.generate_filter_bytecode.filter_expression));
+       if (ret) {
+               goto error;
+       }
+
+       run_as(RUN_AS_GENERATE_FILTER_BYTECODE, &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       if (run_as_ret._error) {
+               ret = -1;
+               goto error;
+       }
+
+       view_bytecode = (const struct lttng_filter_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode;
+
+       local_bytecode = zmalloc(sizeof(*local_bytecode) + view_bytecode->len);
+       if (!local_bytecode) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       memcpy(local_bytecode, run_as_ret.u.generate_filter_bytecode.bytecode,
+                       sizeof(*local_bytecode) + view_bytecode->len);
+       *bytecode = local_bytecode;
+error:
+       return ret;
+
+}
+
 LTTNG_HIDDEN
 int run_as_create_worker(const char *procname,
                post_fork_cleanup_cb clean_up_func,
index 619d3d0b3620fde2d6b3fafdc687dda4cbc3e77a..398221708e16948dfc62221d7df228cf3fb6f850 100644 (file)
@@ -14,6 +14,7 @@
 #include <unistd.h>
 
 #include <common/macros.h>
+#include <common/sessiond-comm/sessiond-comm.h>
 
 /*
  * The run-as process is launched by forking without an exec*() call. This means
@@ -70,6 +71,11 @@ int run_as_extract_sdt_probe_offsets(int fd, const char *provider_name,
                const char* probe_name, uid_t uid, gid_t gid,
                uint64_t **offsets, uint32_t *num_offset);
 LTTNG_HIDDEN
+int run_as_generate_filter_bytecode(const char *filter_expression,
+               uid_t uid,
+               gid_t gid,
+               struct lttng_filter_bytecode **bytecode);
+LTTNG_HIDDEN
 int run_as_create_worker(const char *procname,
                post_fork_cleanup_cb clean_up_func, void *clean_up_user_data);
 LTTNG_HIDDEN
diff --git a/src/common/session-consumed-size.c b/src/common/session-consumed-size.c
deleted file mode 100644 (file)
index dfb7263..0000000
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/session-consumed-size-internal.h>
-#include <lttng/constant.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <assert.h>
-#include <math.h>
-#include <float.h>
-#include <time.h>
-
-#define IS_CONSUMED_SIZE_CONDITION(condition) ( \
-       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
-       )
-
-#define IS_CONSUMED_SIZE_EVALUATION(evaluation) ( \
-       lttng_evaluation_get_type(evaluation) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
-       )
-
-static
-void lttng_condition_session_consumed_size_destroy(struct lttng_condition *condition)
-{
-       struct lttng_condition_session_consumed_size *consumed_size;
-
-       consumed_size = container_of(condition,
-                       struct lttng_condition_session_consumed_size, parent);
-
-       free(consumed_size->session_name);
-       free(consumed_size);
-}
-
-static
-bool lttng_condition_session_consumed_size_validate(
-               const struct lttng_condition *condition)
-{
-       bool valid = false;
-       struct lttng_condition_session_consumed_size *consumed;
-
-       if (!condition) {
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       if (!consumed->session_name) {
-               ERR("Invalid session consumed size condition: a target session name must be set.");
-               goto end;
-       }
-       if (!consumed->consumed_threshold_bytes.set) {
-               ERR("Invalid session consumed size condition: a threshold must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static
-int lttng_condition_session_consumed_size_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_dynamic_buffer *buf)
-{
-       int ret;
-       size_t session_name_len;
-       struct lttng_condition_session_consumed_size *consumed;
-       struct lttng_condition_session_consumed_size_comm consumed_comm;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing session consumed size condition");
-       consumed = container_of(condition,
-                       struct lttng_condition_session_consumed_size,
-                       parent);
-
-       session_name_len = strlen(consumed->session_name) + 1;
-       if (session_name_len > LTTNG_NAME_MAX) {
-               ret = -1;
-               goto end;
-       }
-
-       consumed_comm.consumed_threshold_bytes =
-                       consumed->consumed_threshold_bytes.value;
-       consumed_comm.session_name_len = (uint32_t) session_name_len;
-
-       ret = lttng_dynamic_buffer_append(buf, &consumed_comm,
-                       sizeof(consumed_comm));
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(buf, consumed->session_name,
-                       session_name_len);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static
-bool lttng_condition_session_consumed_size_is_equal(const struct lttng_condition *_a,
-               const struct lttng_condition *_b)
-{
-       bool is_equal = false;
-       struct lttng_condition_session_consumed_size *a, *b;
-
-       a = container_of(_a, struct lttng_condition_session_consumed_size, parent);
-       b = container_of(_b, struct lttng_condition_session_consumed_size, parent);
-
-       if (a->consumed_threshold_bytes.set && b->consumed_threshold_bytes.set) {
-               uint64_t a_value, b_value;
-
-               a_value = a->consumed_threshold_bytes.value;
-               b_value = b->consumed_threshold_bytes.value;
-               if (a_value != b_value) {
-                       goto end;
-               }
-       }
-
-       assert(a->session_name);
-       assert(b->session_name);
-       if (strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-struct lttng_condition *lttng_condition_session_consumed_size_create(void)
-{
-       struct lttng_condition_session_consumed_size *condition;
-
-       condition = zmalloc(sizeof(struct lttng_condition_session_consumed_size));
-       if (!condition) {
-               return NULL;
-       }
-
-       lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE);
-       condition->parent.validate = lttng_condition_session_consumed_size_validate;
-       condition->parent.serialize = lttng_condition_session_consumed_size_serialize;
-       condition->parent.equal = lttng_condition_session_consumed_size_is_equal;
-       condition->parent.destroy = lttng_condition_session_consumed_size_destroy;
-       return &condition->parent;
-}
-
-static
-ssize_t init_condition_from_buffer(struct lttng_condition *condition,
-               const struct lttng_buffer_view *src_view)
-{
-       ssize_t ret, condition_size;
-       enum lttng_condition_status status;
-       const struct lttng_condition_session_consumed_size_comm *condition_comm;
-       const char *session_name;
-       struct lttng_buffer_view names_view;
-
-       if (src_view->size < sizeof(*condition_comm)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm = (const struct lttng_condition_session_consumed_size_comm *) src_view->data;
-       names_view = lttng_buffer_view_from_view(src_view,
-                       sizeof(*condition_comm), -1);
-
-       if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
-               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
-               ret = -1;
-               goto end;
-       }
-
-       if (names_view.size < condition_comm->session_name_len) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_session_consumed_size_set_threshold(condition,
-                       condition_comm->consumed_threshold_bytes);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to initialize session consumed size condition threshold");
-               ret = -1;
-               goto end;
-       }
-
-       session_name = names_view.data;
-       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
-               ERR("Malformed session name encountered in condition buffer");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_session_consumed_size_set_session_name(condition,
-                       session_name);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set session consumed size condition's session name");
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_condition_validate(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       condition_size = sizeof(*condition_comm) +
-                       (ssize_t) condition_comm->session_name_len;
-       ret = condition_size;
-end:
-       return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_session_consumed_size_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_condition **_condition)
-{
-       ssize_t ret;
-       struct lttng_condition *condition =
-                       lttng_condition_session_consumed_size_create();
-
-       if (!_condition || !condition) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = init_condition_from_buffer(condition, view);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_condition = condition;
-       return ret;
-error:
-       lttng_condition_destroy(condition);
-       return ret;
-}
-
-static
-struct lttng_evaluation *create_evaluation_from_buffer(
-               const struct lttng_buffer_view *view)
-{
-       const struct lttng_evaluation_session_consumed_size_comm *comm =
-                       (const struct lttng_evaluation_session_consumed_size_comm *) view->data;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (view->size < sizeof(*comm)) {
-               goto end;
-       }
-
-       evaluation = lttng_evaluation_session_consumed_size_create(
-                       comm->session_consumed);
-end:
-       return evaluation;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_session_consumed_size_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       evaluation = create_evaluation_from_buffer(view);
-       if (!evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       *_evaluation = evaluation;
-       ret = sizeof(struct lttng_evaluation_session_consumed_size_comm);
-       return ret;
-error:
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_get_threshold(
-               const struct lttng_condition *condition,
-               uint64_t *consumed_threshold_bytes)
-{
-       struct lttng_condition_session_consumed_size *consumed;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !consumed_threshold_bytes) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       if (!consumed->consumed_threshold_bytes.set) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *consumed_threshold_bytes = consumed->consumed_threshold_bytes.value;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_set_threshold(
-               struct lttng_condition *condition, uint64_t consumed_threshold_bytes)
-{
-       struct lttng_condition_session_consumed_size *consumed;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       consumed->consumed_threshold_bytes.set = true;
-       consumed->consumed_threshold_bytes.value = consumed_threshold_bytes;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_get_session_name(
-               const struct lttng_condition *condition,
-               const char **session_name)
-{
-       struct lttng_condition_session_consumed_size *consumed;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !session_name) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       if (!consumed->session_name) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *session_name = consumed->session_name;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_set_session_name(
-               struct lttng_condition *condition, const char *session_name)
-{
-       char *session_name_copy;
-       struct lttng_condition_session_consumed_size *consumed;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) ||
-                       !session_name || strlen(session_name) == 0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       session_name_copy = strdup(session_name);
-       if (!session_name_copy) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       if (consumed->session_name) {
-               free(consumed->session_name);
-       }
-       consumed->session_name = session_name_copy;
-end:
-       return status;
-}
-
-static
-int lttng_evaluation_session_consumed_size_serialize(
-               const struct lttng_evaluation *evaluation,
-               struct lttng_dynamic_buffer *buf)
-{
-       struct lttng_evaluation_session_consumed_size *consumed;
-       struct lttng_evaluation_session_consumed_size_comm comm;
-
-       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
-                       parent);
-       comm.session_consumed = consumed->session_consumed;
-       return lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
-}
-
-static
-void lttng_evaluation_session_consumed_size_destroy(
-               struct lttng_evaluation *evaluation)
-{
-       struct lttng_evaluation_session_consumed_size *consumed;
-
-       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
-                       parent);
-       free(consumed);
-}
-
-LTTNG_HIDDEN
-struct lttng_evaluation *lttng_evaluation_session_consumed_size_create(
-               uint64_t consumed)
-{
-       struct lttng_evaluation_session_consumed_size *consumed_eval;
-
-       consumed_eval = zmalloc(sizeof(struct lttng_evaluation_session_consumed_size));
-       if (!consumed_eval) {
-               goto end;
-       }
-
-       consumed_eval->parent.type = LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE;
-       consumed_eval->session_consumed = consumed;
-       consumed_eval->parent.serialize = lttng_evaluation_session_consumed_size_serialize;
-       consumed_eval->parent.destroy = lttng_evaluation_session_consumed_size_destroy;
-end:
-       return &consumed_eval->parent;
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_session_consumed_size_get_consumed_size(
-               const struct lttng_evaluation *evaluation,
-               uint64_t *session_consumed)
-{
-       struct lttng_evaluation_session_consumed_size *consumed;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !IS_CONSUMED_SIZE_EVALUATION(evaluation) ||
-                       !session_consumed) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
-                       parent);
-       *session_consumed = consumed->session_consumed;
-end:
-       return status;
-}
diff --git a/src/common/session-rotation.c b/src/common/session-rotation.c
deleted file mode 100644 (file)
index f8d4439..0000000
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/session-rotation-internal.h>
-#include <lttng/location-internal.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <assert.h>
-#include <stdbool.h>
-
-static
-bool lttng_condition_session_rotation_validate(
-               const struct lttng_condition *condition);
-static
-int lttng_condition_session_rotation_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_dynamic_buffer *buf);
-static
-bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
-               const struct lttng_condition *_b);
-static
-void lttng_condition_session_rotation_destroy(
-               struct lttng_condition *condition);
-
-static const
-struct lttng_condition rotation_condition_template = {
-       /* .type omitted; shall be set on creation. */
-       .validate = lttng_condition_session_rotation_validate,
-       .serialize = lttng_condition_session_rotation_serialize,
-       .equal = lttng_condition_session_rotation_is_equal,
-       .destroy = lttng_condition_session_rotation_destroy,
-};
-
-static
-int lttng_evaluation_session_rotation_serialize(
-               const struct lttng_evaluation *evaluation,
-               struct lttng_dynamic_buffer *buf);
-static
-void lttng_evaluation_session_rotation_destroy(
-               struct lttng_evaluation *evaluation);
-
-static const
-struct lttng_evaluation rotation_evaluation_template = {
-       /* .type omitted; shall be set on creation. */
-       .serialize = lttng_evaluation_session_rotation_serialize,
-       .destroy = lttng_evaluation_session_rotation_destroy,
-};
-
-static
-bool is_rotation_condition(const struct lttng_condition *condition)
-{
-       enum lttng_condition_type type = lttng_condition_get_type(condition);
-
-       return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
-                       type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
-}
-
-static
-bool is_rotation_evaluation(const struct lttng_evaluation *evaluation)
-{
-       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
-
-       return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
-                       type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
-}
-
-static
-bool lttng_condition_session_rotation_validate(
-               const struct lttng_condition *condition)
-{
-       bool valid = false;
-       struct lttng_condition_session_rotation *rotation;
-
-       if (!condition) {
-               goto end;
-       }
-
-       rotation = container_of(condition,
-                       struct lttng_condition_session_rotation, parent);
-       if (!rotation->session_name) {
-               ERR("Invalid session rotation condition: a target session name must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static
-int lttng_condition_session_rotation_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_dynamic_buffer *buf)
-{
-       int ret;
-       size_t session_name_len;
-       struct lttng_condition_session_rotation *rotation;
-       struct lttng_condition_session_rotation_comm rotation_comm;
-
-       if (!condition || !is_rotation_condition(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing session rotation condition");
-       rotation = container_of(condition, struct lttng_condition_session_rotation,
-                       parent);
-
-       session_name_len = strlen(rotation->session_name) + 1;
-       if (session_name_len > LTTNG_NAME_MAX) {
-               ret = -1;
-               goto end;
-       }
-
-       rotation_comm.session_name_len = session_name_len;
-       ret = lttng_dynamic_buffer_append(buf, &rotation_comm,
-                       sizeof(rotation_comm));
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(buf, rotation->session_name,
-                       session_name_len);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static
-bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
-               const struct lttng_condition *_b)
-{
-       bool is_equal = false;
-       struct lttng_condition_session_rotation *a, *b;
-
-       a = container_of(_a, struct lttng_condition_session_rotation, parent);
-       b = container_of(_b, struct lttng_condition_session_rotation, parent);
-
-       /* Both session names must be set or both must be unset. */
-       if ((a->session_name && !b->session_name) ||
-                       (!a->session_name && b->session_name)) {
-               WARN("Comparing session rotation conditions with uninitialized session names.");
-               goto end;
-       }
-
-       if (a->session_name && b->session_name &&
-                       strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-static
-void lttng_condition_session_rotation_destroy(
-               struct lttng_condition *condition)
-{
-       struct lttng_condition_session_rotation *rotation;
-
-       rotation = container_of(condition,
-                       struct lttng_condition_session_rotation, parent);
-
-       free(rotation->session_name);
-       free(rotation);
-}
-
-static
-struct lttng_condition *lttng_condition_session_rotation_create(
-               enum lttng_condition_type type)
-{
-       struct lttng_condition_session_rotation *condition;
-
-       condition = zmalloc(sizeof(struct lttng_condition_session_rotation));
-       if (!condition) {
-               return NULL;
-       }
-
-       memcpy(&condition->parent, &rotation_condition_template,
-                       sizeof(condition->parent));
-       lttng_condition_init(&condition->parent, type);
-       return &condition->parent;
-}
-
-struct lttng_condition *lttng_condition_session_rotation_ongoing_create(void)
-{
-       return lttng_condition_session_rotation_create(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
-}
-
-struct lttng_condition *lttng_condition_session_rotation_completed_create(void)
-{
-       return lttng_condition_session_rotation_create(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
-}
-
-static
-ssize_t init_condition_from_buffer(struct lttng_condition *condition,
-               const struct lttng_buffer_view *src_view)
-{
-       ssize_t ret, condition_size;
-       enum lttng_condition_status status;
-       const struct lttng_condition_session_rotation_comm *condition_comm;
-       const char *session_name;
-       struct lttng_buffer_view name_view;
-
-       if (src_view->size < sizeof(*condition_comm)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm = (const struct lttng_condition_session_rotation_comm *) src_view->data;
-       name_view = lttng_buffer_view_from_view(src_view,
-                       sizeof(*condition_comm), -1);
-
-       if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
-               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
-               ret = -1;
-               goto end;
-       }
-
-       if (name_view.size < condition_comm->session_name_len) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain session name");
-               ret = -1;
-               goto end;
-       }
-
-       session_name = name_view.data;
-       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
-               ERR("Malformed session name encountered in condition buffer");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_session_rotation_set_session_name(condition,
-                       session_name);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set buffer consumed session name");
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_condition_validate(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       condition_size = sizeof(*condition_comm) +
-                       (ssize_t) condition_comm->session_name_len;
-       ret = condition_size;
-end:
-       return ret;
-}
-
-static
-ssize_t lttng_condition_session_rotation_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_condition **_condition,
-               enum lttng_condition_type type)
-{
-       ssize_t ret;
-       struct lttng_condition *condition = NULL;
-
-       switch (type) {
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-               condition = lttng_condition_session_rotation_ongoing_create();
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               condition = lttng_condition_session_rotation_completed_create();
-               break;
-       default:
-               ret = -1;
-               goto error;
-       }
-
-       if (!_condition || !condition) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = init_condition_from_buffer(condition, view);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_condition = condition;
-       return ret;
-error:
-       lttng_condition_destroy(condition);
-       return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_session_rotation_ongoing_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_condition **condition)
-{
-       return lttng_condition_session_rotation_create_from_buffer(view,
-                       condition,
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_session_rotation_completed_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_condition **condition)
-{
-       return lttng_condition_session_rotation_create_from_buffer(view,
-                       condition,
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
-}
-
-static
-struct lttng_evaluation *lttng_evaluation_session_rotation_create(
-               enum lttng_condition_type type, uint64_t id,
-               struct lttng_trace_archive_location *location)
-{
-       struct lttng_evaluation_session_rotation *evaluation;
-
-       evaluation = zmalloc(sizeof(struct lttng_evaluation_session_rotation));
-       if (!evaluation) {
-               return NULL;
-       }
-
-       memcpy(&evaluation->parent, &rotation_evaluation_template,
-                       sizeof(evaluation->parent));
-       lttng_evaluation_init(&evaluation->parent, type);
-       evaluation->id = id;
-       evaluation->location = location;
-       return &evaluation->parent;
-}
-
-static
-ssize_t create_evaluation_from_buffer(
-               enum lttng_condition_type type,
-               const struct lttng_buffer_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret, size;
-       struct lttng_evaluation *evaluation = NULL;
-       struct lttng_trace_archive_location *location = NULL;
-       const struct lttng_evaluation_session_rotation_comm *comm =
-                       (const struct lttng_evaluation_session_rotation_comm *) view->data;
-        struct lttng_buffer_view location_view;
-
-       if (view->size < sizeof(*comm)) {
-               goto error;
-       }
-
-       size = sizeof(*comm);
-       if (comm->has_location) {
-               location_view = lttng_buffer_view_from_view(view, sizeof(*comm),
-                               -1);
-               if (!location_view.data) {
-                       goto error;
-               }
-
-               ret = lttng_trace_archive_location_create_from_buffer(
-                               &location_view, &location);
-               if (ret < 0) {
-                       goto error;
-               }
-               size += ret;
-       }
-
-       evaluation = lttng_evaluation_session_rotation_create(type, comm->id,
-                       location);
-       if (!evaluation) {
-               goto error;
-       }
-
-       ret = size;
-       *_evaluation = evaluation;
-       return ret;
-error:
-       lttng_trace_archive_location_destroy(location);
-       evaluation = NULL;
-       return -1;
-}
-
-static
-ssize_t lttng_evaluation_session_rotation_create_from_buffer(
-               enum lttng_condition_type type,
-               const struct lttng_buffer_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = create_evaluation_from_buffer(type, view, &evaluation);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_evaluation = evaluation;
-       return ret;
-error:
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_session_rotation_ongoing_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_evaluation **evaluation)
-{
-       return lttng_evaluation_session_rotation_create_from_buffer(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING,
-                       view, evaluation);
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_session_rotation_completed_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_evaluation **evaluation)
-{
-       return lttng_evaluation_session_rotation_create_from_buffer(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED,
-                       view, evaluation);
-}
-
-LTTNG_HIDDEN
-struct lttng_evaluation *lttng_evaluation_session_rotation_ongoing_create(
-               uint64_t id)
-{
-       return lttng_evaluation_session_rotation_create(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, id,
-                       NULL);
-}
-
-LTTNG_HIDDEN
-struct lttng_evaluation *lttng_evaluation_session_rotation_completed_create(
-               uint64_t id, struct lttng_trace_archive_location *location)
-{
-       return lttng_evaluation_session_rotation_create(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, id,
-                       location);
-}
-
-enum lttng_condition_status
-lttng_condition_session_rotation_get_session_name(
-               const struct lttng_condition *condition,
-               const char **session_name)
-{
-       struct lttng_condition_session_rotation *rotation;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !is_rotation_condition(condition) || !session_name) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       rotation = container_of(condition, struct lttng_condition_session_rotation,
-                       parent);
-       if (!rotation->session_name) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *session_name = rotation->session_name;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_rotation_set_session_name(
-               struct lttng_condition *condition, const char *session_name)
-{
-       char *session_name_copy;
-       struct lttng_condition_session_rotation *rotation;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !is_rotation_condition(condition) ||
-                       !session_name || strlen(session_name) == 0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       rotation = container_of(condition,
-                       struct lttng_condition_session_rotation, parent);
-       session_name_copy = strdup(session_name);
-       if (!session_name_copy) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       free(rotation->session_name);
-       rotation->session_name = session_name_copy;
-end:
-       return status;
-}
-
-static
-int lttng_evaluation_session_rotation_serialize(
-               const struct lttng_evaluation *evaluation,
-               struct lttng_dynamic_buffer *buf)
-{
-       int ret;
-       struct lttng_evaluation_session_rotation *rotation;
-       struct lttng_evaluation_session_rotation_comm comm = { 0 };
-
-       rotation = container_of(evaluation,
-                       struct lttng_evaluation_session_rotation, parent);
-       comm.id = rotation->id;
-       comm.has_location = !!rotation->location;
-        ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
-       if (ret) {
-               goto end;
-       }
-       if (!rotation->location) {
-               goto end;
-       }
-       ret = lttng_trace_archive_location_serialize(rotation->location,
-                       buf);
-end:
-       return ret;
-}
-
-static
-void lttng_evaluation_session_rotation_destroy(
-               struct lttng_evaluation *evaluation)
-{
-       struct lttng_evaluation_session_rotation *rotation;
-
-       rotation = container_of(evaluation,
-                       struct lttng_evaluation_session_rotation, parent);
-       lttng_trace_archive_location_destroy(rotation->location);
-       free(rotation);
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_session_rotation_get_id(
-               const struct lttng_evaluation *evaluation, uint64_t *id)
-{
-       const struct lttng_evaluation_session_rotation *rotation;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !id || !is_rotation_evaluation(evaluation)) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       rotation = container_of(evaluation,
-                       struct lttng_evaluation_session_rotation, parent);
-       *id = rotation->id;
-end:
-       return status;
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_session_rotation_completed_get_location(
-               const struct lttng_evaluation *evaluation,
-               const struct lttng_trace_archive_location **location)
-{
-       const struct lttng_evaluation_session_rotation *rotation;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !location ||
-                       evaluation->type != LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       rotation = container_of(evaluation,
-                       struct lttng_evaluation_session_rotation, parent);
-       *location = rotation->location;
-end:
-       return status;
-}
index d89da4e32c402f1a623b80bfa4abe5ac2ce9d8f9..735fa023c5470ce7e349985e4ba9491983ef79bb 100644 (file)
@@ -99,6 +99,7 @@ enum lttcomm_sessiond_command {
        LTTNG_SESSION_LIST_ROTATION_SCHEDULES = 48,
        LTTNG_CREATE_SESSION_EXT              = 49,
        LTTNG_CLEAR_SESSION                   = 50,
+       LTTNG_LIST_TRIGGERS                   = 51,
 };
 
 enum lttcomm_relayd_command {
diff --git a/src/common/snapshot.c b/src/common/snapshot.c
new file mode 100644 (file)
index 0000000..2879f0f
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2020 - EfficiOS, Inc.
+ *
+ * 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 "buffer-view.h"
+#include "dynamic-buffer.h"
+#include "lttng/snapshot.h"
+#include "lttng/snapshot-internal.h"
+#include "snapshot.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+LTTNG_HIDDEN
+bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output)
+{
+       bool valid = false;
+       size_t len;
+
+       /*
+        * It is mandatory to have a ctrl_url.  If there is only one output
+        * URL (in the net://, net6:// or file:// form), it will be in this
+        * field.
+        */
+       len = lttng_strnlen(output->ctrl_url, sizeof(output->ctrl_url));
+       if (len == 0 || len >= sizeof(output->ctrl_url)) {
+               goto end;
+       }
+
+       len = lttng_strnlen(output->data_url, sizeof(output->data_url));
+       if (len >= sizeof(output->data_url)) {
+               goto end;
+       }
+
+       len = lttng_strnlen(output->name, sizeof(output->name));
+       if (len >= sizeof(output->name)) {
+               goto end;
+       }
+
+       valid = true;
+
+end:
+       return valid;
+}
+
+LTTNG_HIDDEN
+bool lttng_snapshot_output_is_equal(
+               const struct lttng_snapshot_output *a,
+               const struct lttng_snapshot_output *b)
+{
+       bool equal = false;
+
+       assert(a);
+       assert(b);
+
+       if (a->max_size != b->max_size) {
+               goto end;
+       }
+
+       if (strcmp(a->name, b->name) != 0) {
+               goto end;
+       }
+
+       if (strcmp(a->ctrl_url, b->ctrl_url) != 0) {
+               goto end;
+       }
+
+       if (strcmp(a->data_url, b->data_url) != 0) {
+               goto end;
+       }
+
+       equal = true;
+
+end:
+       return equal;
+}
+
+/*
+ * This is essentially the same as `struct lttng_snapshot_output`, but packed.
+ */
+struct LTTNG_PACKED lttng_snapshot_output_comm {
+       uint32_t id;
+       uint64_t max_size;
+       char name[LTTNG_NAME_MAX];
+       char ctrl_url[PATH_MAX];
+       char data_url[PATH_MAX];
+};
+
+LTTNG_HIDDEN
+int lttng_snapshot_output_serialize(
+               const struct lttng_snapshot_output *output,
+               struct lttng_dynamic_buffer *buf)
+{
+       struct lttng_snapshot_output_comm comm;
+       int ret;
+
+       comm.id = output->id;
+       comm.max_size = output->max_size;
+
+       ret = lttng_strncpy(comm.name, output->name, sizeof(comm.name));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_strncpy(comm.ctrl_url, output->ctrl_url, sizeof(comm.ctrl_url));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_strncpy(comm.data_url, output->data_url, sizeof(comm.data_url));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_snapshot_output_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_snapshot_output **output_p)
+{
+       const struct lttng_snapshot_output_comm *comm;
+       struct lttng_snapshot_output *output = NULL;
+       int ret;
+
+       if (view->size != sizeof(*comm)) {
+               ret = -1;
+               goto end;
+       }
+
+       output = lttng_snapshot_output_create();
+       if (!output) {
+               ret = -1;
+               goto end;
+       }
+
+       comm = (struct lttng_snapshot_output_comm *) view->data;
+
+       output->id = comm->id;
+       output->max_size = comm->max_size;
+
+       ret = lttng_strncpy(output->name, comm->name, sizeof(output->name));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_strncpy(output->ctrl_url, comm->ctrl_url, sizeof(output->ctrl_url));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_strncpy(output->data_url, comm->data_url, sizeof(output->data_url));
+       if (ret) {
+               goto end;
+       }
+
+       *output_p = output;
+       output = NULL;
+       ret = sizeof(*comm);
+
+end:
+       lttng_snapshot_output_destroy(output);
+       return ret;
+}
diff --git a/src/common/snapshot.h b/src/common/snapshot.h
new file mode 100644 (file)
index 0000000..2f27d5c
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef COMMON_SNAPSHOT_H
+#define COMMON_SNAPSHOT_H
+
+/*
+ * Copyright (C) 2020 - EfficiOS, Inc.
+ *
+ * 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/macros.h"
+
+#include <stdbool.h>
+
+struct lttng_buffer_view;
+struct lttng_dynamic_buffer;
+struct lttng_snapshot_output;
+
+LTTNG_HIDDEN
+bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output);
+
+LTTNG_HIDDEN
+bool lttng_snapshot_output_is_equal(
+               const struct lttng_snapshot_output *a,
+               const struct lttng_snapshot_output *b);
+
+LTTNG_HIDDEN
+int lttng_snapshot_output_serialize(
+               const struct lttng_snapshot_output *output,
+               struct lttng_dynamic_buffer *buf);
+
+LTTNG_HIDDEN
+ssize_t lttng_snapshot_output_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_snapshot_output **output_p);
+
+#endif /* COMMON_SNAPSHOT_H */
index dd161eb38420dbc40935674a63fb7bd0308e92d0..5c93c0ed4907215d544b61a91a84fed756a1d40a 100644 (file)
@@ -7,12 +7,36 @@
 
 #include <lttng/trigger/trigger-internal.h>
 #include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/condition/buffer-usage.h>
+#include <lttng/event-rule/event-rule-internal.h>
 #include <lttng/action/action-internal.h>
+#include <lttng/domain.h>
 #include <common/error.h>
+#include <common/dynamic-array.h>
 #include <assert.h>
+#include <inttypes.h>
+
+static void lttng_trigger_set_internal_object_ownership(
+               struct lttng_trigger *trigger)
+{
+       /*
+        * This is necessary to faciliate the object destroy phase. A trigger
+        * created by a client does not OWN the internal objects (condition and
+        * action) but when a trigger object is created on the sessiond side or
+        * for listing triggers (mostly via create_from_buffer) the object is
+        * the owner of the internal objects. Hence, we set the ownership bool
+        * to true, in such case, to facilitate object lifetime management and
+        * internal ownership.
+        *
+        * TODO: I'm open to any other solution
+        */
+       assert(trigger);
+       trigger->owns_internal_objects = true;
+}
 
 LTTNG_HIDDEN
-bool lttng_trigger_validate(struct lttng_trigger *trigger)
+bool lttng_trigger_validate(const struct lttng_trigger *trigger)
 {
        bool valid;
 
@@ -42,8 +66,15 @@ struct lttng_trigger *lttng_trigger_create(
                goto end;
        }
 
+       urcu_ref_init(&trigger->ref);
+       trigger->owns_internal_objects = false;
+       trigger->firing_policy.type = LTTNG_TRIGGER_FIRE_EVERY_N;
+       trigger->firing_policy.threshold = 1;
        trigger->condition = condition;
        trigger->action = action;
+
+       trigger->creds.set = false;
+
 end:
        return trigger;
 }
@@ -54,11 +85,10 @@ struct lttng_condition *lttng_trigger_get_condition(
        return trigger ? trigger->condition : NULL;
 }
 
-LTTNG_HIDDEN
 const struct lttng_condition *lttng_trigger_get_const_condition(
                const struct lttng_trigger *trigger)
 {
-       return trigger->condition;
+       return trigger ? trigger->condition : NULL;
 }
 
 struct lttng_action *lttng_trigger_get_action(
@@ -67,33 +97,53 @@ struct lttng_action *lttng_trigger_get_action(
        return trigger ? trigger->action : NULL;
 }
 
-LTTNG_HIDDEN
 const struct lttng_action *lttng_trigger_get_const_action(
                const struct lttng_trigger *trigger)
 {
-       return trigger->action;
+       return trigger ? trigger->action : NULL;
 }
 
-void lttng_trigger_destroy(struct lttng_trigger *trigger)
+static void trigger_destroy_ref(struct urcu_ref *ref)
 {
-       if (!trigger) {
-               return;
+       struct lttng_trigger *trigger =
+                       container_of(ref, struct lttng_trigger, ref);
+
+       if (trigger->owns_internal_objects) {
+               struct lttng_action *action = lttng_trigger_get_action(trigger);
+               struct lttng_condition *condition =
+                               lttng_trigger_get_condition(trigger);
+
+               assert(action);
+               assert(condition);
+               lttng_action_destroy(action);
+               lttng_condition_destroy(condition);
        }
 
+       free(trigger->name);
        free(trigger);
 }
 
+void lttng_trigger_destroy(struct lttng_trigger *trigger)
+{
+       lttng_trigger_put(trigger);
+}
+
 LTTNG_HIDDEN
 ssize_t lttng_trigger_create_from_buffer(
                const struct lttng_buffer_view *src_view,
                struct lttng_trigger **trigger)
 {
-       ssize_t ret, offset = 0, condition_size, action_size;
+       ssize_t ret, offset = 0, condition_size, action_size, name_size = 0;
+       enum lttng_trigger_status status;
        struct lttng_condition *condition = NULL;
        struct lttng_action *action = NULL;
        const struct lttng_trigger_comm *trigger_comm;
        struct lttng_buffer_view condition_view;
        struct lttng_buffer_view action_view;
+       struct lttng_buffer_view name_view;
+       const char *name = NULL;
+       unsigned long long firing_threshold;
+       enum lttng_trigger_firing_policy_type firing_policy;
 
        if (!src_view || !trigger) {
                ret = -1;
@@ -104,6 +154,22 @@ ssize_t lttng_trigger_create_from_buffer(
        trigger_comm = (const struct lttng_trigger_comm *) src_view->data;
        offset += sizeof(*trigger_comm);
 
+       firing_policy = trigger_comm->policy_type;
+       firing_threshold = trigger_comm->policy_threshold;
+       if (trigger_comm->name_length != 0) {
+               name_view = lttng_buffer_view_from_view(
+                               src_view, offset, trigger_comm->name_length);
+               name = name_view.data;
+               if (trigger_comm->name_length == 1 ||
+                               name[trigger_comm->name_length - 1] != '\0' ||
+                               strlen(name) != trigger_comm->name_length - 1) {
+                       ret = -1;
+                       goto end;
+               }
+               offset += trigger_comm->name_length;
+               name_size = trigger_comm->name_length;
+       }
+
        condition_view = lttng_buffer_view_from_view(src_view, offset, -1);
 
        /* struct lttng_condition */
@@ -125,7 +191,7 @@ ssize_t lttng_trigger_create_from_buffer(
        offset += action_size;
 
        /* Unexpected size of inner-elements; the buffer is corrupted. */
-       if ((ssize_t) trigger_comm->length != condition_size + action_size) {
+       if ((ssize_t) trigger_comm->length != condition_size + action_size + name_size) {
                ret = -1;
                goto error;
        }
@@ -135,6 +201,26 @@ ssize_t lttng_trigger_create_from_buffer(
                ret = -1;
                goto error;
        }
+
+       /* Take ownership of the internal object from there */
+       lttng_trigger_set_internal_object_ownership(*trigger);
+       condition = NULL;
+       action = NULL;
+
+       if (name) {
+               status = lttng_trigger_set_name(*trigger, name);
+               if (status != LTTNG_TRIGGER_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       status = lttng_trigger_set_firing_policy(*trigger, firing_policy, firing_threshold);
+       if (status != LTTNG_TRIGGER_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
        ret = offset;
 end:
        return ret;
@@ -149,15 +235,27 @@ error:
  * for the detailed format.
  */
 LTTNG_HIDDEN
-int lttng_trigger_serialize(struct lttng_trigger *trigger,
-               struct lttng_dynamic_buffer *buf)
+int lttng_trigger_serialize(const struct lttng_trigger *trigger,
+               struct lttng_dynamic_buffer *buf,
+               int *fd_to_send)
 {
        int ret;
-       size_t header_offset, size_before_payload;
+       size_t header_offset, size_before_payload, size_name;
        struct lttng_trigger_comm trigger_comm = { 0 };
        struct lttng_trigger_comm *header;
 
        header_offset = buf->size;
+
+       if (trigger->name != NULL) {
+               size_name = strlen(trigger->name) + 1;
+       } else {
+               size_name = 0;
+       }
+
+       trigger_comm.name_length = size_name;
+       trigger_comm.policy_type = (uint8_t) trigger->firing_policy.type;
+       trigger_comm.policy_threshold = (uint64_t) trigger->firing_policy.threshold;
+
        ret = lttng_dynamic_buffer_append(buf, &trigger_comm,
                        sizeof(trigger_comm));
        if (ret) {
@@ -165,7 +263,14 @@ int lttng_trigger_serialize(struct lttng_trigger *trigger,
        }
 
        size_before_payload = buf->size;
-       ret = lttng_condition_serialize(trigger->condition, buf);
+
+       /* Trigger name */
+       ret = lttng_dynamic_buffer_append(buf, trigger->name, size_name);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_condition_serialize(trigger->condition, buf, fd_to_send);
        if (ret) {
                goto end;
        }
@@ -181,3 +286,478 @@ int lttng_trigger_serialize(struct lttng_trigger *trigger,
 end:
        return ret;
 }
+
+enum lttng_trigger_status lttng_trigger_set_name(struct lttng_trigger *trigger, const char* name)
+{
+       char *name_copy = NULL;
+       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+       if (!trigger || !name ||
+                       strlen(name) == 0) {
+               status = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       name_copy = strdup(name);
+       if (!name_copy) {
+               status = LTTNG_TRIGGER_STATUS_ERROR;
+               goto end;
+       }
+
+       if (trigger->name) {
+               free(trigger->name);
+       }
+
+       trigger->name = name_copy;
+       name_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_trigger_status lttng_trigger_get_name(const struct lttng_trigger *trigger, const char **name)
+{
+       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+       if (!trigger || !name) {
+               status = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       if (!trigger->name) {
+               status = LTTNG_TRIGGER_STATUS_UNSET;
+       }
+
+       *name = trigger->name;
+end:
+       return status;
+}
+
+LTTNG_HIDDEN
+int lttng_trigger_assign(struct lttng_trigger *dst,
+               const struct lttng_trigger *src)
+{
+       int ret = 0;
+       enum lttng_trigger_status status;
+       /* todo some validation */
+
+       status = lttng_trigger_set_name(dst, src->name);
+       if (status != LTTNG_TRIGGER_STATUS_OK) {
+               ret = -1;
+               ERR("Failed to set name for trigger");
+               goto end;
+       }
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_set_key(struct lttng_trigger *trigger, uint64_t key)
+{
+       assert(trigger);
+       trigger->key.value = key;
+       trigger->key.set = true;
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_trigger_get_key(const struct lttng_trigger *trigger)
+{
+       assert(trigger);
+
+       assert(trigger->key.set == true);
+       return trigger->key.value;
+}
+
+LTTNG_HIDDEN
+int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset)
+{
+       int ret = 0;
+       char *generated_name = NULL;
+       assert(trigger->key.set);
+
+       ret = asprintf(&generated_name, "T%" PRIu64 "", trigger->key.value + offset);
+       if (ret < 0) {
+               ERR("Failed to generate trigger name");
+               ret = -1;
+               goto end;
+       }
+
+       if (trigger->name) {
+               free(trigger->name);
+       }
+       trigger->name = generated_name;
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_equal(
+               const struct lttng_trigger *a, const struct lttng_trigger *b)
+{
+       /* TODO: Optimization: for now a trigger with a firing policy that is
+        * not the same even if the conditions and actions is the same is
+        * treated as a "completely" different trigger. In a perfect world we
+        * would simply add a supplemental counter internally (sessiond side) to
+        * remove overhead on the tracer side.
+        */
+       if (a->firing_policy.type != b->firing_policy.type) {
+               return false;
+       }
+
+       if (a->firing_policy.threshold != b->firing_policy.threshold) {
+               return false;
+       }
+
+       /*
+        * Name is not taken into account since it is cosmetic only
+        */
+       if (!lttng_condition_is_equal(a->condition, b->condition)) {
+               return false;
+       }
+       if (!lttng_action_is_equal(a->action, b->action)) {
+               return false;
+       }
+
+       return true;
+}
+
+static void delete_trigger_array_element(void *ptr)
+{
+       struct lttng_trigger *trigger = ptr;
+       lttng_trigger_destroy(trigger);
+}
+
+LTTNG_HIDDEN
+struct lttng_triggers *lttng_triggers_create(void)
+{
+       struct lttng_triggers *triggers = NULL;
+
+       triggers = zmalloc(sizeof(*triggers));
+       if (!triggers) {
+               goto error;
+       }
+
+       lttng_dynamic_pointer_array_init(&triggers->array, delete_trigger_array_element);
+
+       return triggers;
+error:
+       free(triggers);
+       return NULL;
+}
+
+LTTNG_HIDDEN
+struct lttng_trigger *lttng_triggers_get_pointer_of_index(
+               const struct lttng_triggers *triggers, unsigned int index)
+{
+       assert(triggers);
+       if (index >= lttng_dynamic_pointer_array_get_count(&triggers->array)) {
+               return NULL;
+       }
+       return lttng_dynamic_pointer_array_get_pointer(&triggers->array, index);
+}
+
+LTTNG_HIDDEN
+int lttng_triggers_add(
+               struct lttng_triggers *triggers, struct lttng_trigger *trigger)
+{
+       assert(triggers);
+       assert(trigger);
+
+       return lttng_dynamic_pointer_array_add_pointer(&triggers->array, trigger);
+}
+
+const struct lttng_trigger *lttng_triggers_get_at_index(
+               const struct lttng_triggers *triggers, unsigned int index)
+{
+       assert(triggers);
+       return lttng_triggers_get_pointer_of_index(triggers, index);
+}
+
+enum lttng_trigger_status lttng_triggers_get_count(const struct lttng_triggers *triggers, unsigned int *count)
+{
+       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+       if (!triggers || !count) {
+               status = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       *count = lttng_dynamic_pointer_array_get_count(&triggers->array);
+end:
+       return status;
+}
+
+void lttng_triggers_destroy(struct lttng_triggers *triggers)
+{
+       if (!triggers) {
+               return;
+       }
+
+       lttng_dynamic_pointer_array_reset(&triggers->array);
+       free(triggers);
+}
+
+int lttng_triggers_serialize(const struct lttng_triggers *triggers,
+               struct lttng_dynamic_buffer *buffer)
+{
+       int ret;
+       unsigned int count;
+       size_t header_offset, size_before_payload;
+       struct lttng_triggers_comm triggers_comm = { 0 };
+       struct lttng_triggers_comm *header;
+       struct lttng_trigger *trigger;
+       enum lttng_trigger_status status;
+
+       header_offset = buffer->size;
+
+       status = lttng_triggers_get_count(triggers, &count);
+       if (status != LTTNG_TRIGGER_STATUS_OK) {
+               ret = LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       triggers_comm.count = count;
+
+       ret = lttng_dynamic_buffer_append(buffer, &triggers_comm,
+                       sizeof(triggers_comm));
+       if (ret) {
+               goto end;
+       }
+
+       size_before_payload = buffer->size;
+
+       for (int i = 0; i < count; i++) {
+               trigger = lttng_triggers_get_pointer_of_index(triggers, i);
+               if (!trigger) {
+                       assert(0);
+               }
+
+               ret = lttng_trigger_serialize(trigger, buffer, NULL);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       /* Update payload size. */
+       header = (struct lttng_triggers_comm *) ((char *) buffer->data + header_offset);
+       header->length = buffer->size - size_before_payload;
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_triggers_create_from_buffer(
+               const struct lttng_buffer_view *src_view,
+               struct lttng_triggers **triggers)
+{
+       ssize_t ret, offset = 0, trigger_size, triggers_size = 0;
+       const struct lttng_triggers_comm *triggers_comm;
+       struct lttng_buffer_view trigger_view;
+       struct lttng_triggers *local_triggers = NULL;
+
+       if (!src_view || !triggers) {
+               ret = -1;
+               goto error;
+       }
+
+       /* lttng_trigger_comms header */
+       triggers_comm = (const struct lttng_triggers_comm *) src_view->data;
+       offset += sizeof(*triggers_comm);
+
+       local_triggers = lttng_triggers_create();
+       if (!local_triggers) {
+               ret = -1;
+               goto error;
+       }
+
+       for (int i = 0; i < triggers_comm->count; i++) {
+               struct lttng_trigger *trigger = NULL;
+               trigger_view = lttng_buffer_view_from_view(src_view, offset, -1);
+               trigger_size = lttng_trigger_create_from_buffer(&trigger_view,
+                               &trigger);
+               if (trigger_size  < 0) {
+                       ret = trigger_size;
+                       goto error;
+               }
+               
+               /* Pass ownership of the trigger to the collection */
+               ret = lttng_triggers_add(local_triggers, trigger);
+               if (ret < 0) {
+                       assert(0);
+               }
+               trigger = NULL;
+
+               offset += trigger_size;
+               triggers_size += trigger_size;
+       }
+
+       /* Unexpected size of inner-elements; the buffer is corrupted. */
+       if ((ssize_t) triggers_comm->length != triggers_size) {
+               ret = -1;
+               goto error;
+       }
+
+       /* Pass ownership to caller */
+       *triggers = local_triggers;
+       local_triggers = NULL;
+
+       ret = offset;
+error:
+
+       lttng_triggers_destroy(local_triggers);
+       return ret;
+}
+
+LTTNG_HIDDEN
+const struct lttng_credentials *lttng_trigger_get_credentials(
+               const struct lttng_trigger *trigger)
+{
+       assert(trigger->creds.set);
+       return &(trigger->creds.credentials);
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_set_credentials(
+               struct lttng_trigger *trigger, uid_t uid, gid_t gid)
+{
+       trigger->creds.credentials.uid = uid;
+       trigger->creds.credentials.gid = gid;
+       trigger->creds.set = true;
+}
+
+enum lttng_trigger_status lttng_trigger_set_firing_policy(
+               struct lttng_trigger *trigger,
+               enum lttng_trigger_firing_policy_type policy_type,
+               unsigned long long threshold)
+{
+       enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK;
+       assert(trigger);
+
+       if (threshold < 1) {
+               ret = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       trigger->firing_policy.type = policy_type;
+       trigger->firing_policy.threshold = threshold;
+
+end:
+       return ret;
+}
+
+enum lttng_trigger_status lttng_trigger_get_firing_policy(
+               const struct lttng_trigger *trigger,
+               enum lttng_trigger_firing_policy_type *policy_type,
+               unsigned long long *threshold)
+{
+       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+       if (!trigger || !policy_type || !threshold) {
+               status = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       *policy_type = trigger->firing_policy.type;
+       *threshold = trigger->firing_policy.threshold;
+
+end:
+       return status;
+}
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_ready_to_fire(struct lttng_trigger *trigger)
+{
+       assert(trigger);
+       bool ready_to_fire = false;
+
+       trigger->firing_policy.current_count++;
+
+       switch (trigger->firing_policy.type) {
+       case LTTNG_TRIGGER_FIRE_EVERY_N:
+               if (trigger->firing_policy.current_count == trigger->firing_policy.threshold) {
+                       trigger->firing_policy.current_count = 0;
+                       ready_to_fire = true;
+               }
+               break;
+       case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N:
+               if (trigger->firing_policy.current_count == trigger->firing_policy.threshold) {
+                       /* TODO: remove the trigger of at least deactivate it on
+                        * the tracers side to remove any work overhead on the
+                        * traced application or kernel since the trigger will
+                        * never fire again.
+                        * Still this branch should be left here since event
+                        * could still be in the pipe. These will be discarded.
+                        */
+                       ready_to_fire = true;
+               }
+               break;
+       default:
+               assert(0);
+       };
+
+       return ready_to_fire;
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_get(struct lttng_trigger *trigger)
+{
+       urcu_ref_get(&trigger->ref);
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_put(struct lttng_trigger *trigger)
+{
+       if (!trigger) {
+               return;
+       }
+
+       urcu_ref_put(&trigger->ref , trigger_destroy_ref);
+}
+
+LTTNG_HIDDEN
+enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction(
+               const struct lttng_trigger *trigger)
+{
+       enum lttng_domain_type type = LTTNG_DOMAIN_NONE;
+       const struct lttng_event_rule *event_rule;
+       enum lttng_condition_status c_status;
+       enum lttng_condition_type c_type;
+
+       assert(trigger);
+       assert(trigger->condition);
+       c_type = lttng_condition_get_type(trigger->condition);
+       if (c_type == LTTNG_CONDITION_TYPE_UNKNOWN) {
+               assert(0);
+       }
+
+       switch (c_type) {
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               type = LTTNG_DOMAIN_NONE;
+               break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               c_status = lttng_condition_event_rule_get_rule(
+                               trigger->condition, &event_rule);
+               if (c_status != LTTNG_CONDITION_STATUS_OK) {
+                       /* The condition object is invalid */
+                       assert(0);
+               }
+
+               type = lttng_event_rule_get_domain_type(event_rule);
+               break;
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+               c_status = lttng_condition_buffer_usage_get_domain_type(
+                               trigger->condition, &type);
+               if (c_status != LTTNG_CONDITION_STATUS_OK) {
+                       /* The condition object is invalid */
+                       assert(0);
+               }
+               break;
+       default:
+               type = LTTNG_DOMAIN_NONE;
+               break;
+       }
+
+       return type;
+}
index 222b4a3d9ab2c874da7add2b4c9a02832661da6b..26eda52d97d1ad992c7b534b461c121eee9b6d00 100644 (file)
@@ -232,12 +232,9 @@ retry:
                if (errno == EINTR) {
                        goto retry;
                } else {
-                       /*
-                        * Only warn about EPIPE when quiet mode is
-                        * deactivated.
-                        * We consider EPIPE as expected.
-                        */
-                       if (errno != EPIPE || !lttng_opt_quiet) {
+                       /* We consider EPIPE and EAGAIN as expected. */
+                       if (!lttng_opt_quiet &&
+                                       (errno != EPIPE && errno != EAGAIN)) {
                                PERROR("recvmsg");
                        }
                        goto end;
@@ -320,12 +317,9 @@ retry:
                if (errno == EINTR) {
                        goto retry;
                } else {
-                       /*
-                        * Only warn about EPIPE when quiet mode is
-                        * deactivated.
-                        * We consider EPIPE as expected.
-                        */
-                       if (errno != EPIPE || !lttng_opt_quiet) {
+                       /* We consider EPIPE and EAGAIN as expected. */
+                       if (!lttng_opt_quiet &&
+                                       (errno != EPIPE && errno != EAGAIN)) {
                                PERROR("sendmsg");
                        }
                        goto end;
index 508c9e8d4480a8a99216e7a3792a6555dd534d3f..4cc881025656cb582ffbcbf701051539ed564809 100644 (file)
@@ -144,6 +144,41 @@ void lttng_userspace_probe_location_destroy(
        }
 }
 
+static
+bool lttng_userspace_probe_location_function_is_equal(const struct lttng_userspace_probe_location *_a,
+               const struct lttng_userspace_probe_location *_b)
+{
+       bool is_equal = false;
+       struct lttng_userspace_probe_location_function *a, *b;
+
+       a = container_of(_a, struct lttng_userspace_probe_location_function, parent);
+       b = container_of(_b, struct lttng_userspace_probe_location_function, parent);
+
+       /* The binary_fd is not checked since it does not hold "immutable"
+        * information.
+        */
+
+       if (a->instrumentation_type != b->instrumentation_type) {
+               goto end;
+       }
+
+       assert(a->function_name);
+       assert(b->function_name);
+       if (strcmp(a->function_name, b->function_name)) {
+               goto end;
+       }
+
+       assert(a->binary_path);
+       assert(b->binary_path);
+       if (strcmp(a->binary_path, b->binary_path)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
 static struct lttng_userspace_probe_location *
 lttng_userspace_probe_location_function_create_no_check(const char *binary_path,
                const char *function_name,
@@ -192,6 +227,7 @@ lttng_userspace_probe_location_function_create_no_check(const char *binary_path,
        ret = &location->parent;
        ret->lookup_method = lookup_method;
        ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION;
+       ret->equal = lttng_userspace_probe_location_function_is_equal;
        goto end;
 
 error:
@@ -206,6 +242,43 @@ end:
        return ret;
 }
 
+static
+bool lttng_userspace_probe_location_tracepoint_is_equal(const struct lttng_userspace_probe_location *_a,
+               const struct lttng_userspace_probe_location *_b)
+{
+       bool is_equal = false;
+       struct lttng_userspace_probe_location_tracepoint *a, *b;
+
+       a = container_of(_a, struct lttng_userspace_probe_location_tracepoint, parent);
+       b = container_of(_b, struct lttng_userspace_probe_location_tracepoint, parent);
+
+       /* The binary_fd is not checked since it does not hold "immutable"
+        * information.
+        */
+
+       assert(a->probe_name);
+       assert(b->probe_name);
+       if (strcmp(a->probe_name, b->probe_name)) {
+               goto end;
+       }
+
+       assert(a->provider_name);
+       assert(b->provider_name);
+       if (strcmp(a->provider_name, b->provider_name)) {
+               goto end;
+       }
+
+       assert(a->binary_path);
+       assert(b->binary_path);
+       if (strcmp(a->binary_path, b->binary_path)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
 static struct lttng_userspace_probe_location *
 lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_path,
                const char *provider_name, const char *probe_name,
@@ -261,6 +334,7 @@ lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_pat
        ret = &location->parent;
        ret->lookup_method = lookup_method;
        ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT;
+       ret->equal = lttng_userspace_probe_location_tracepoint_is_equal;
        goto end;
 
 error:
@@ -1711,3 +1785,70 @@ struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy(
 err:
        return new_location;
 }
+
+LTTNG_HIDDEN
+bool lttng_userspace_probe_location_is_equal(
+               const struct lttng_userspace_probe_location *a,
+               const struct lttng_userspace_probe_location *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       is_equal = a->equal ? a->equal(a, b) : true;
+end:
+       return is_equal;
+}
+
+LTTNG_HIDDEN
+int lttng_userspace_probe_location_set_binary_fd(
+               struct lttng_userspace_probe_location *location, int fd)
+{
+       int ret = 0;
+       const struct lttng_userspace_probe_location_lookup_method *lookup = NULL;
+       /*
+        * Set the file descriptor received from the client through the unix
+        * socket in the probe location.
+        */
+       lookup = lttng_userspace_probe_location_get_lookup_method(location);
+       if (!lookup) {
+               ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+               goto end;
+       }
+
+       /*
+        * From the kernel tracer's perspective, all userspace probe event types
+        * are all the same: a file and an offset.
+        */
+       switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+               ret = lttng_userspace_probe_location_function_set_binary_fd(
+                               location, fd);
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+               ret = lttng_userspace_probe_location_tracepoint_set_binary_fd(
+                               location, fd);
+               break;
+       default:
+               ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+               goto end;
+       }
+
+       if (ret) {
+               ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+               goto end;
+       }
+end:
+       return ret;
+}
index d9bacad0e7a7f32dd660e2d6db28b754b7545066..83d4dbe45e4a9395a3ba2bd5b31a2cf7b2fd7b0c 100644 (file)
@@ -1527,3 +1527,40 @@ int utils_change_working_directory(const char *path)
 end:
        return ret;
 }
+
+LTTNG_HIDDEN
+int utils_parse_unsigned_long_long(const char *str,
+               unsigned long long *value)
+{
+       int ret;
+       char *endptr;
+
+       assert(str);
+       assert(value);
+
+       errno = 0;
+       *value = strtoull(str, &endptr, 10);
+
+       /* Conversion failed.  Out of range? */
+       if (errno != 0) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Not the end of the string? */
+       if (*endptr) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Empty string? */
+       if (endptr == str) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+
+end:
+       return ret;
+}
index b6e5c97db345769c53408e0261989c35f3d71db3..d5dc9d01af1b467ac1a4aa91f208388bab81f654 100644 (file)
@@ -55,4 +55,16 @@ int utils_get_memory_available(size_t *value);
 int utils_get_memory_total(size_t *value);
 int utils_change_working_directory(const char *path);
 
+/*
+ * Parse `str` as an unsigned long long value.
+ *
+ * Return 0 on success.  Return -1 on failure which can be because:
+ *
+ * - `str` is zero length
+ * - `str` contains invalid
+ */
+LTTNG_HIDDEN
+int utils_parse_unsigned_long_long(const char *str,
+               unsigned long long *value);
+
 #endif /* _COMMON_UTILS_H */
index de390175b805a963ce93a8f8e8af47c22300b834..dcc12aa26c457a992107bbf73e87bdd5c093d78e 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-SUBDIRS = filter
+SUBDIRS =
 
 AM_CPPFLAGS += -I$(srcdir) -I$(builddir)
 
@@ -15,8 +15,7 @@ liblttng_ctl_la_LDFLAGS = \
 
 liblttng_ctl_la_LIBADD = \
                $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \
-               $(top_builddir)/src/common/libcommon.la \
-               $(top_builddir)/src/lib/lttng-ctl/filter/libfilter.la
+               $(top_builddir)/src/common/libcommon.la
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = lttng-ctl.pc
index f2a65885d74416c08885ffe7ebcf5793f683fb09..5fc77d0e402ad686479d5a8f66211f0617cba0bf 100644 (file)
@@ -632,7 +632,7 @@ enum lttng_notification_channel_status send_condition_command(
                goto end_unlock;
        }
 
-       ret = lttng_condition_serialize(condition, &buffer);
+       ret = lttng_condition_serialize(condition, &buffer, NULL);
        if (ret) {
                status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID;
                goto end_unlock;
diff --git a/src/lib/lttng-ctl/filter/Makefile.am b/src/lib/lttng-ctl/filter/Makefile.am
deleted file mode 100644 (file)
index ca85cda..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-
-AM_CPPFLAGS += -I$(srcdir) -I$(builddir)
-
-noinst_PROGRAMS = filter-grammar-test
-noinst_LTLIBRARIES = libfilter.la
-noinst_HEADERS = filter-ast.h \
-               filter-symbols.h
-
-BUILT_SOURCES = filter-parser.h
-
-libfilter_la_SOURCES = \
-       filter-parser.y filter-lexer.l \
-       filter-visitor-xml.c \
-       filter-visitor-generate-ir.c \
-       filter-visitor-ir-check-binary-op-nesting.c \
-       filter-visitor-ir-validate-string.c \
-       filter-visitor-ir-validate-globbing.c \
-       filter-visitor-ir-normalize-glob-patterns.c \
-       filter-visitor-generate-bytecode.c \
-       filter-ast.h \
-       filter-bytecode.h \
-       filter-ir.h \
-       memstream.h
-libfilter_la_CFLAGS = -include filter-symbols.h $(AM_CFLAGS)
-libfilter_la_LIBADD = $(top_builddir)/src/common/string-utils/libstring-utils.la
-
-AM_YFLAGS = -t -d -v
-
-# start with empty files to clean
-CLEANFILES =
-
-if HAVE_BISON
-# we have bison: we can clean the generated parser files
-CLEANFILES += filter-parser.c filter-parser.h filter-parser.output
-else # HAVE_BISON
-# create target used to stop the build if we want to build the parser,
-# but we don't have the necessary tool to do so
-ERR_MSG = "Error: Cannot build target because bison is missing."
-ERR_MSG += "Make sure bison is installed and run the configure script again."
-
-filter-parser.c filter-parser.h: filter-parser.y
-       @echo $(ERR_MSG)
-       @false
-
-all-local: filter-parser.c filter-parser.h
-endif # HAVE_BISON
-
-if HAVE_FLEX
-# we have flex: we can clean the generated lexer files
-CLEANFILES += filter-lexer.c
-else # HAVE_FLEX
-# create target used to stop the build if we want to build the lexer,
-# but we don't have the necessary tool to do so
-ERR_MSG = "Error: Cannot build target because flex is missing."
-ERR_MSG += "Make sure flex is installed and run the configure script again."
-
-filter-lexer.c: filter-lexer.l
-       @echo $(ERR_MSG)
-       @false
-
-all-local: filter-lexer.c
-endif # HAVE_FLEX
-
-filter_grammar_test_SOURCES = filter-grammar-test.c
-filter_grammar_test_LDADD = libfilter.la
diff --git a/src/lib/lttng-ctl/filter/filter-ast.h b/src/lib/lttng-ctl/filter/filter-ast.h
deleted file mode 100644 (file)
index fd322df..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-#ifndef _FILTER_AST_H
-#define _FILTER_AST_H
-
-/*
- * filter-ast.h
- *
- * LTTng filter AST
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-/*
- * Note: filter-ast.h should be included before filter-parser.h.
- */
-
-#include <urcu/list.h>
-#include <stdint.h>
-
-#define printf_debug(fmt, args...)                                     \
-       do {                                                            \
-               if (filter_parser_debug)                                \
-                       fprintf(stdout, "[debug] " fmt, ## args);       \
-       } while (0)
-
-// the parameter name (of the reentrant 'yyparse' function)
-// data is a pointer to a 'SParserParam' structure
-//#define YYPARSE_PARAM        parser_ctx
-
-#ifndef YY_TYPEDEF_YY_SCANNER_T
-#define YY_TYPEDEF_YY_SCANNER_T
-typedef void* yyscan_t;
-#endif
-
-extern int filter_parser_debug;
-
-struct filter_node;
-struct filter_parser;
-
-enum node_type {
-       NODE_UNKNOWN = 0,
-       NODE_ROOT,
-
-       NODE_EXPRESSION,
-       NODE_OP,
-       NODE_UNARY_OP,
-
-       NR_NODE_TYPES,
-};
-
-enum op_type {
-       AST_OP_UNKNOWN = 0,
-       AST_OP_MUL,
-       AST_OP_DIV,
-       AST_OP_MOD,
-       AST_OP_PLUS,
-       AST_OP_MINUS,
-       AST_OP_BIT_RSHIFT,
-       AST_OP_BIT_LSHIFT,
-       AST_OP_AND,
-       AST_OP_OR,
-       AST_OP_BIT_AND,
-       AST_OP_BIT_OR,
-       AST_OP_BIT_XOR,
-
-       AST_OP_EQ,
-       AST_OP_NE,
-       AST_OP_GT,
-       AST_OP_LT,
-       AST_OP_GE,
-       AST_OP_LE,
-};
-
-enum unary_op_type {
-       AST_UNARY_UNKNOWN = 0,
-       AST_UNARY_PLUS,
-       AST_UNARY_MINUS,
-       AST_UNARY_NOT,
-       AST_UNARY_BIT_NOT,
-};
-
-enum ast_link_type {
-       AST_LINK_UNKNOWN = 0,
-       AST_LINK_DOT,
-       AST_LINK_RARROW,
-       AST_LINK_BRACKET,
-};
-
-struct filter_node {
-       /*
-        * Parent node is only set on demand by specific visitor.
-        */
-       struct filter_node *parent;
-       struct cds_list_head gc;
-
-       enum node_type type;
-       union {
-               struct {
-               } unknown;
-               struct {
-                       struct filter_node *child;
-               } root;
-               struct {
-                       enum {
-                               AST_EXP_UNKNOWN = 0,
-                               AST_EXP_STRING,
-                               AST_EXP_CONSTANT,
-                               AST_EXP_FLOAT_CONSTANT,
-                               AST_EXP_IDENTIFIER,
-                               AST_EXP_GLOBAL_IDENTIFIER,
-                               AST_EXP_NESTED,
-                       } type;
-                       enum ast_link_type post_op;     /* reverse */
-                       enum ast_link_type pre_op;      /* forward */
-                       union {
-                               char *string;
-                               uint64_t constant;
-                               double float_constant;
-                               char *identifier;
-                               /*
-                                * child can be nested.
-                                */
-                               struct filter_node *child;
-                       } u;
-                       /* prev: backward dot/arrow chain (postfix expression) */
-                       struct filter_node *prev;
-                       /* next: forward dot/arrow chain, generated by a visitor. */
-                       struct filter_node *next;
-                       /* next_bracket: linked bracket chain (prefix expression) */
-                       struct filter_node *next_bracket;
-               } expression;
-               struct {
-                       enum op_type type;
-                       struct filter_node *lchild;
-                       struct filter_node *rchild;
-               } op;
-               struct {
-                       enum unary_op_type type;
-                       struct filter_node *child;
-               } unary_op;
-       } u;
-};
-
-struct filter_ast {
-       struct filter_node root;
-       struct cds_list_head allocated_nodes;
-};
-
-const char *node_type(struct filter_node *node);
-
-struct ir_op;
-
-struct filter_parser_ctx {
-       yyscan_t scanner;
-       struct filter_ast *ast;
-       struct cds_list_head allocated_strings;
-       struct ir_op *ir_root;
-       struct lttng_filter_bytecode_alloc *bytecode;
-       struct lttng_filter_bytecode_alloc *bytecode_reloc;
-};
-
-struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input);
-void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx);
-int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx);
-
-static inline
-struct filter_ast *filter_parser_get_ast(struct filter_parser_ctx *parser_ctx)
-{
-       return parser_ctx->ast;
-}
-
-int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream,
-                       int indent);
-int filter_visitor_ir_generate(struct filter_parser_ctx *ctx);
-void filter_ir_free(struct filter_parser_ctx *ctx);
-int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx);
-void filter_bytecode_free(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx);
-
-#endif /* _FILTER_AST_H */
diff --git a/src/lib/lttng-ctl/filter/filter-bytecode.h b/src/lib/lttng-ctl/filter/filter-bytecode.h
deleted file mode 100644 (file)
index 053bb08..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-#ifndef _FILTER_BYTECODE_H
-#define _FILTER_BYTECODE_H
-
-/*
- * filter-bytecode.h
- *
- * LTTng filter bytecode
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/sessiond-comm/sessiond-comm.h>
-#include <common/macros.h>
-
-#include "filter-ast.h"
-
-/*
- * offsets are absolute from start of bytecode.
- */
-
-struct field_ref {
-       /* Initially, symbol offset. After link, field offset. */
-       uint16_t offset;
-} LTTNG_PACKED;
-
-struct get_symbol {
-       /* Symbol offset. */
-       uint16_t offset;
-} LTTNG_PACKED;
-
-struct get_index_u16 {
-       uint16_t index;
-} LTTNG_PACKED;
-
-struct get_index_u64 {
-       uint64_t index;
-} LTTNG_PACKED;
-
-struct literal_numeric {
-       int64_t v;
-} LTTNG_PACKED;
-
-struct literal_double {
-       double v;
-} LTTNG_PACKED;
-
-struct literal_string {
-       char string[0];
-} LTTNG_PACKED;
-
-enum filter_op {
-       FILTER_OP_UNKNOWN                       = 0,
-
-       FILTER_OP_RETURN                        = 1,
-
-       /* binary */
-       FILTER_OP_MUL                           = 2,
-       FILTER_OP_DIV                           = 3,
-       FILTER_OP_MOD                           = 4,
-       FILTER_OP_PLUS                          = 5,
-       FILTER_OP_MINUS                         = 6,
-       FILTER_OP_BIT_RSHIFT                    = 7,
-       FILTER_OP_BIT_LSHIFT                    = 8,
-       FILTER_OP_BIT_AND                       = 9,
-       FILTER_OP_BIT_OR                        = 10,
-       FILTER_OP_BIT_XOR                       = 11,
-
-       /* binary comparators */
-       FILTER_OP_EQ                            = 12,
-       FILTER_OP_NE                            = 13,
-       FILTER_OP_GT                            = 14,
-       FILTER_OP_LT                            = 15,
-       FILTER_OP_GE                            = 16,
-       FILTER_OP_LE                            = 17,
-
-       /* string binary comparator: apply to  */
-       FILTER_OP_EQ_STRING                     = 18,
-       FILTER_OP_NE_STRING                     = 19,
-       FILTER_OP_GT_STRING                     = 20,
-       FILTER_OP_LT_STRING                     = 21,
-       FILTER_OP_GE_STRING                     = 22,
-       FILTER_OP_LE_STRING                     = 23,
-
-       /* s64 binary comparator */
-       FILTER_OP_EQ_S64                        = 24,
-       FILTER_OP_NE_S64                        = 25,
-       FILTER_OP_GT_S64                        = 26,
-       FILTER_OP_LT_S64                        = 27,
-       FILTER_OP_GE_S64                        = 28,
-       FILTER_OP_LE_S64                        = 29,
-
-       /* double binary comparator */
-       FILTER_OP_EQ_DOUBLE                     = 30,
-       FILTER_OP_NE_DOUBLE                     = 31,
-       FILTER_OP_GT_DOUBLE                     = 32,
-       FILTER_OP_LT_DOUBLE                     = 33,
-       FILTER_OP_GE_DOUBLE                     = 34,
-       FILTER_OP_LE_DOUBLE                     = 35,
-
-       /* Mixed S64-double binary comparators */
-       FILTER_OP_EQ_DOUBLE_S64                 = 36,
-       FILTER_OP_NE_DOUBLE_S64                 = 37,
-       FILTER_OP_GT_DOUBLE_S64                 = 38,
-       FILTER_OP_LT_DOUBLE_S64                 = 39,
-       FILTER_OP_GE_DOUBLE_S64                 = 40,
-       FILTER_OP_LE_DOUBLE_S64                 = 41,
-
-       FILTER_OP_EQ_S64_DOUBLE                 = 42,
-       FILTER_OP_NE_S64_DOUBLE                 = 43,
-       FILTER_OP_GT_S64_DOUBLE                 = 44,
-       FILTER_OP_LT_S64_DOUBLE                 = 45,
-       FILTER_OP_GE_S64_DOUBLE                 = 46,
-       FILTER_OP_LE_S64_DOUBLE                 = 47,
-
-       /* unary */
-       FILTER_OP_UNARY_PLUS                    = 48,
-       FILTER_OP_UNARY_MINUS                   = 49,
-       FILTER_OP_UNARY_NOT                     = 50,
-       FILTER_OP_UNARY_PLUS_S64                = 51,
-       FILTER_OP_UNARY_MINUS_S64               = 52,
-       FILTER_OP_UNARY_NOT_S64                 = 53,
-       FILTER_OP_UNARY_PLUS_DOUBLE             = 54,
-       FILTER_OP_UNARY_MINUS_DOUBLE            = 55,
-       FILTER_OP_UNARY_NOT_DOUBLE              = 56,
-
-       /* logical */
-       FILTER_OP_AND                           = 57,
-       FILTER_OP_OR                            = 58,
-
-       /* load field ref */
-       FILTER_OP_LOAD_FIELD_REF                = 59,
-       FILTER_OP_LOAD_FIELD_REF_STRING         = 60,
-       FILTER_OP_LOAD_FIELD_REF_SEQUENCE       = 61,
-       FILTER_OP_LOAD_FIELD_REF_S64            = 62,
-       FILTER_OP_LOAD_FIELD_REF_DOUBLE         = 63,
-
-       /* load immediate from operand */
-       FILTER_OP_LOAD_STRING                   = 64,
-       FILTER_OP_LOAD_S64                      = 65,
-       FILTER_OP_LOAD_DOUBLE                   = 66,
-
-       /* cast */
-       FILTER_OP_CAST_TO_S64                   = 67,
-       FILTER_OP_CAST_DOUBLE_TO_S64            = 68,
-       FILTER_OP_CAST_NOP                      = 69,
-
-       /* get context ref */
-       FILTER_OP_GET_CONTEXT_REF               = 70,
-       FILTER_OP_GET_CONTEXT_REF_STRING        = 71,
-       FILTER_OP_GET_CONTEXT_REF_S64           = 72,
-       FILTER_OP_GET_CONTEXT_REF_DOUBLE        = 73,
-
-       /* load userspace field ref */
-       FILTER_OP_LOAD_FIELD_REF_USER_STRING    = 74,
-       FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE  = 75,
-
-       /*
-        * load immediate star globbing pattern (literal string)
-        * from immediate
-        */
-       FILTER_OP_LOAD_STAR_GLOB_STRING         = 76,
-
-       /* globbing pattern binary operator: apply to */
-       FILTER_OP_EQ_STAR_GLOB_STRING           = 77,
-       FILTER_OP_NE_STAR_GLOB_STRING           = 78,
-
-       /*
-        * Instructions for recursive traversal through composed types.
-        */
-       FILTER_OP_GET_CONTEXT_ROOT              = 79,
-       FILTER_OP_GET_APP_CONTEXT_ROOT          = 80,
-       FILTER_OP_GET_PAYLOAD_ROOT              = 81,
-
-       FILTER_OP_GET_SYMBOL                    = 82,
-       FILTER_OP_GET_SYMBOL_FIELD              = 83,
-       FILTER_OP_GET_INDEX_U16                 = 84,
-       FILTER_OP_GET_INDEX_U64                 = 85,
-
-       FILTER_OP_LOAD_FIELD                    = 86,
-       FILTER_OP_LOAD_FIELD_S8                 = 87,
-       FILTER_OP_LOAD_FIELD_S16                = 88,
-       FILTER_OP_LOAD_FIELD_S32                = 89,
-       FILTER_OP_LOAD_FIELD_S64                = 90,
-       FILTER_OP_LOAD_FIELD_U8                 = 91,
-       FILTER_OP_LOAD_FIELD_U16                = 92,
-       FILTER_OP_LOAD_FIELD_U32                = 93,
-       FILTER_OP_LOAD_FIELD_U64                = 94,
-       FILTER_OP_LOAD_FIELD_STRING             = 95,
-       FILTER_OP_LOAD_FIELD_SEQUENCE           = 96,
-       FILTER_OP_LOAD_FIELD_DOUBLE             = 97,
-
-       FILTER_OP_UNARY_BIT_NOT                 = 98,
-
-       FILTER_OP_RETURN_S64                    = 99,
-
-       NR_FILTER_OPS,
-};
-
-typedef uint8_t filter_opcode_t;
-
-struct load_op {
-       filter_opcode_t op;
-       char data[0];
-       /* data to load. Size known by enum filter_opcode and null-term char. */
-} LTTNG_PACKED;
-
-struct binary_op {
-       filter_opcode_t op;
-} LTTNG_PACKED;
-
-struct unary_op {
-       filter_opcode_t op;
-} LTTNG_PACKED;
-
-/* skip_offset is absolute from start of bytecode */
-struct logical_op {
-       filter_opcode_t op;
-       uint16_t skip_offset;   /* bytecode insn, if skip second test */
-} LTTNG_PACKED;
-
-struct cast_op {
-       filter_opcode_t op;
-} LTTNG_PACKED;
-
-struct return_op {
-       filter_opcode_t op;
-} LTTNG_PACKED;
-
-struct lttng_filter_bytecode_alloc {
-       uint32_t alloc_len;
-       struct lttng_filter_bytecode b;
-};
-
-static inline
-unsigned int bytecode_get_len(struct lttng_filter_bytecode *bytecode)
-{
-       return bytecode->len;
-}
-
-#endif /* _FILTER_BYTECODE_H */
diff --git a/src/lib/lttng-ctl/filter/filter-grammar-test.c b/src/lib/lttng-ctl/filter/filter-grammar-test.c
deleted file mode 100644 (file)
index 2804053..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * filter-grammar-test.c
- *
- * LTTng filter grammar test
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-bytecode.h"
-
-int main(int argc, char **argv)
-{
-       struct filter_parser_ctx *ctx;
-       int ret;
-       int print_xml = 0, generate_ir = 0, generate_bytecode = 0,
-               print_bytecode = 0;
-       int i;
-
-       for (i = 1; i < argc; i++) {
-               if (strcmp(argv[i], "-p") == 0)
-                       print_xml = 1;
-               else if (strcmp(argv[i], "-i") == 0)
-                       generate_ir = 1;
-               else if (strcmp(argv[i], "-b") == 0)
-                       generate_bytecode = 1;
-               else if (strcmp(argv[i], "-d") == 0)
-                       filter_parser_debug = 1;
-               else if (strcmp(argv[i], "-B") == 0)
-                       print_bytecode = 1;
-       }
-
-       ctx = filter_parser_ctx_alloc(stdin);
-       if (!ctx) {
-               fprintf(stderr, "Error allocating parser\n");
-               goto alloc_error;
-       }
-       ret = filter_parser_ctx_append_ast(ctx);
-       if (ret) {
-               fprintf(stderr, "Parse error\n");
-               goto parse_error;
-       }
-       if (print_xml) {
-               ret = filter_visitor_print_xml(ctx, stdout, 0);
-               if (ret) {
-                       fflush(stdout);
-                       fprintf(stderr, "XML print error\n");
-                       goto parse_error;
-               }
-       }
-       if (generate_ir) {
-               printf("Generating IR... ");
-               fflush(stdout);
-               ret = filter_visitor_ir_generate(ctx);
-               if (ret) {
-                       fprintf(stderr, "Generate IR error\n");
-                       goto parse_error;
-               }
-               printf("done\n");
-
-               printf("Validating IR... ");
-               fflush(stdout);
-               ret = filter_visitor_ir_check_binary_op_nesting(ctx);
-               if (ret) {
-                       goto parse_error;
-               }
-               printf("done\n");
-       }
-       if (generate_bytecode) {
-               printf("Generating bytecode... ");
-               fflush(stdout);
-               ret = filter_visitor_bytecode_generate(ctx);
-               if (ret) {
-                       fprintf(stderr, "Generate bytecode error\n");
-                       goto parse_error;
-               }
-               printf("done\n");
-               printf("Size of bytecode generated: %u bytes.\n",
-                       bytecode_get_len(&ctx->bytecode->b));
-       }
-
-       if (print_bytecode) {
-               unsigned int bytecode_len, len, i;
-
-               len = bytecode_get_len(&ctx->bytecode->b);
-               bytecode_len = ctx->bytecode->b.reloc_table_offset;
-               printf("Bytecode:\n");
-               for (i = 0; i < bytecode_len; i++) {
-                       printf("0x%X ",
-                               ((uint8_t *) ctx->bytecode->b.data)[i]);
-               }
-               printf("\n");
-               printf("Reloc table:\n");
-               for (i = bytecode_len; i < len;) {
-                       printf("{ 0x%X, ",
-                               *(uint16_t *) &ctx->bytecode->b.data[i]);
-                       i += sizeof(uint16_t);
-                       printf("%s } ", &((char *) ctx->bytecode->b.data)[i]);
-                       i += strlen(&((char *) ctx->bytecode->b.data)[i]) + 1;
-               }
-               printf("\n");
-       }
-
-       filter_bytecode_free(ctx);
-       filter_ir_free(ctx);
-       filter_parser_ctx_free(ctx);
-       return 0;
-
-parse_error:
-       filter_bytecode_free(ctx);
-       filter_ir_free(ctx);
-       filter_parser_ctx_free(ctx);
-alloc_error:
-       exit(EXIT_FAILURE);
-}
diff --git a/src/lib/lttng-ctl/filter/filter-ir.h b/src/lib/lttng-ctl/filter/filter-ir.h
deleted file mode 100644 (file)
index d62c0ee..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-#ifndef _FILTER_IR_H
-#define _FILTER_IR_H
-
-/*
- * filter-ir.h
- *
- * LTTng filter ir
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include "filter-ast.h"
-
-enum ir_op_signedness {
-       IR_SIGN_UNKNOWN = 0,
-       IR_SIGNED,
-       IR_UNSIGNED,
-       IR_SIGN_DYN,            /* signedness determined dynamically */
-};
-
-enum ir_data_type {
-       IR_DATA_UNKNOWN = 0,
-       IR_DATA_STRING,
-       IR_DATA_NUMERIC,        /* numeric and boolean */
-       IR_DATA_FLOAT,
-       IR_DATA_FIELD_REF,
-       IR_DATA_GET_CONTEXT_REF,
-       IR_DATA_EXPRESSION,
-};
-
-enum ir_op_type {
-       IR_OP_UNKNOWN = 0,
-       IR_OP_ROOT,
-       IR_OP_LOAD,
-       IR_OP_UNARY,
-       IR_OP_BINARY,
-       IR_OP_LOGICAL,
-};
-
-/* left or right child */
-enum ir_side {
-       IR_SIDE_UNKNOWN = 0,
-       IR_LEFT,
-       IR_RIGHT,
-};
-
-enum ir_load_string_type {
-       /* Plain, no globbing at all: `hello world`. */
-       IR_LOAD_STRING_TYPE_PLAIN = 0,
-
-       /* Star at the end only: `hello *`. */
-       IR_LOAD_STRING_TYPE_GLOB_STAR_END,
-
-       /* At least one star, anywhere, but not at the end only: `he*wor*`. */
-       IR_LOAD_STRING_TYPE_GLOB_STAR,
-};
-
-struct ir_op_root {
-       struct ir_op *child;
-};
-
-enum ir_load_expression_type {
-       IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT,
-       IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT,
-       IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT,
-       IR_LOAD_EXPRESSION_GET_SYMBOL,
-       IR_LOAD_EXPRESSION_GET_INDEX,
-       IR_LOAD_EXPRESSION_LOAD_FIELD,
-};
-
-struct ir_load_expression_op {
-       struct ir_load_expression_op *next;
-       enum ir_load_expression_type type;
-       union {
-               char *symbol;
-               uint64_t index;
-       } u;
-};
-
-struct ir_load_expression {
-       struct ir_load_expression_op *child;
-};
-
-struct ir_op_load {
-       union {
-               struct {
-                       enum ir_load_string_type type;
-                       char *value;
-               } string;
-               int64_t num;
-               double flt;
-               char *ref;
-               struct ir_load_expression *expression;
-       } u;
-};
-
-struct ir_op_unary {
-       enum unary_op_type type;
-       struct ir_op *child;
-};
-
-struct ir_op_binary {
-       enum op_type type;
-       struct ir_op *left;
-       struct ir_op *right;
-};
-
-struct ir_op_logical {
-       enum op_type type;
-       struct ir_op *left;
-       struct ir_op *right;
-};
-
-struct ir_op {
-       /* common to all ops */
-       enum ir_op_type op;
-       enum ir_data_type data_type;
-       enum ir_op_signedness signedness;
-       enum ir_side side;
-
-       union {
-               struct ir_op_root root;
-               struct ir_op_load load;
-               struct ir_op_unary unary;
-               struct ir_op_binary binary;
-               struct ir_op_logical logical;
-       } u;
-};
-
-#endif /* _FILTER_IR_H */
diff --git a/src/lib/lttng-ctl/filter/filter-lexer.l b/src/lib/lttng-ctl/filter/filter-lexer.l
deleted file mode 100644 (file)
index 4ef4519..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-%{
-/*
- * filter-lexer.l
- *
- * LTTng filter lexer
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-
-extern
-void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src);
-
-static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
-       __attribute__((unused));
-static int input (yyscan_t yyscanner) __attribute__((unused));
-
-%}
-
-%x comment_ml comment_sl string_lit char_const
-%option reentrant yylineno noyywrap bison-bridge
-%option extra-type="struct filter_parser_ctx *"
-       /* bison-locations */
-
-D                              [0-9]
-L                              [a-zA-Z_]
-H                              [a-fA-F0-9]
-E                              ([Ee][+-]?{D}+)
-P                              ([Pp][+-]?{D}+)
-FS                             (f|F|l|L)
-IS                             ((u|U)|(u|U)?(l|L|ll|LL)|(l|L|ll|LL)(u|U))
-
-INTEGER_SUFFIX                 [ \n\t]*(U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu)
-DIGIT                          [0-9]
-NONDIGIT                       [a-zA-Z_]
-HEXDIGIT                       [0-9A-Fa-f]
-OCTALDIGIT                     [0-7]
-UCHARLOWERCASE                 \\u{HEXDIGIT}{4}
-UCHARUPPERCASE                 \\U{HEXDIGIT}{8}
-ID_EXTRA_CHAR                  (":")
-ID_NONDIGIT                    {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}|{ID_EXTRA_CHAR}
-IDENTIFIER                     {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})*
-ESCSEQ                         \\(\'|\"|\?|\\|a|b|f|n|r|t|v|{OCTALDIGIT}{1,3}|u{HEXDIGIT}{4}|U{HEXDIGIT}{8}|x{HEXDIGIT}+)
-%%
-
-                               /*
-                                * Using start conditions to deal with comments
-                                * and strings.
-                                */ 
-
-"/*"                           BEGIN(comment_ml);
-<comment_ml>[^*\n]*            /* eat anything that's not a '*' */
-<comment_ml>"*"+[^*/\n]*       /* eat up '*'s not followed by '/'s */
-<comment_ml>\n                 ++yylineno;
-<comment_ml>"*"+"/"            BEGIN(INITIAL);
-
-"//"                           BEGIN(comment_sl);
-<comment_sl>[^\n]*\n           ++yylineno; BEGIN(INITIAL);
-
-L\'                            BEGIN(char_const); return CHARACTER_CONSTANT_START;
-\'                             BEGIN(char_const); return CHARACTER_CONSTANT_START;
-<char_const>\'                 BEGIN(INITIAL); return SQUOTE;
-
-L\"                            BEGIN(string_lit); return STRING_LITERAL_START;
-\"                             BEGIN(string_lit); return STRING_LITERAL_START;
-<string_lit>\"                 BEGIN(INITIAL); return DQUOTE;
-
-<char_const,string_lit>ESCSEQ  return ESCSEQ;
-<char_const,string_lit>\n      ; /* ignore */
-<char_const,string_lit>.       setstring(yyextra, yylval, yytext); return CHAR_STRING_TOKEN;
-
-
-0[xX]{H}+{IS}?                 setstring(yyextra, yylval, yytext); return HEXADECIMAL_CONSTANT;
-0[0-7]*{IS}?                   setstring(yyextra, yylval, yytext); return OCTAL_CONSTANT;
-[1-9]{D}*{IS}?                 setstring(yyextra, yylval, yytext); return DECIMAL_CONSTANT;
-
-{D}+{E}{FS}?                   setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-{D}*"."{D}+{E}?{FS}?           setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-{D}+"."{D}*{E}?{FS}?           setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-0[xX]{H}+{P}{FS}?              setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-0[xX]{H}*"."{H}+{P}?{FS}?      setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-0[xX]{H}+"."{H}*{P}?{FS}?      setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-
-"["                            return LSBRAC;
-"]"                            return RSBRAC;
-"("                            return LPAREN;
-")"                            return RPAREN;
-"{"                            return LBRAC;
-"}"                            return RBRAC;
-"->"                           return RARROW;
-
-"*"                            return STAR;
-"+"                            return PLUS;
-"-"                            return MINUS;
-
-"%"                            return MOD_OP;
-"/"                            return DIV_OP;
-">>"                           return RIGHT_OP;
-"<<"                           return LEFT_OP;
-
-"=="                           return EQ_OP;
-"!="                           return NE_OP;
-"<="                           return LE_OP;
-">="                           return GE_OP;
-"<"                            return LT_OP;
-">"                            return GT_OP;
-"&&"                           return AND_OP;
-"||"                           return OR_OP;
-"!"                            return NOT_OP;
-
-":="                           return ASSIGN;
-":"                            return COLON;
-";"                            return SEMICOLON;
-"..."                          return DOTDOTDOT;
-"."                            return DOT;
-"="                            return EQUAL;
-","                            return COMMA;
-"^"                            return XOR_BIN;
-"&"                            return AND_BIN;
-"|"                            return OR_BIN;
-"~"                            return NOT_BIN;
-"$"{IDENTIFIER}                        printf_debug("<GLOBAL_IDENTIFIER %s>\n", yytext); setstring(yyextra, yylval, yytext); return GLOBAL_IDENTIFIER;
-{IDENTIFIER}                   printf_debug("<IDENTIFIER %s>\n", yytext); setstring(yyextra, yylval, yytext); return IDENTIFIER;
-[ \t\n]+                       ; /* ignore */
-.                              return ERROR;
-%%
diff --git a/src/lib/lttng-ctl/filter/filter-parser.y b/src/lib/lttng-ctl/filter/filter-parser.y
deleted file mode 100644 (file)
index d1ea3a1..0000000
+++ /dev/null
@@ -1,677 +0,0 @@
-%{
-/*
- * filter-parser.y
- *
- * LTTng filter expression parser
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Grammar inspired from http://www.quut.com/c/ANSI-C-grammar-y.html
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-
-#include <common/macros.h>
-
-#define WIDTH_u64_SCANF_IS_A_BROKEN_API        "20"
-#define WIDTH_o64_SCANF_IS_A_BROKEN_API        "22"
-#define WIDTH_x64_SCANF_IS_A_BROKEN_API        "17"
-#define WIDTH_lg_SCANF_IS_A_BROKEN_API "4096"  /* Hugely optimistic approximation */
-
-LTTNG_HIDDEN
-int yydebug;
-LTTNG_HIDDEN
-int filter_parser_debug = 0;
-
-LTTNG_HIDDEN
-int yyparse(struct filter_parser_ctx *parser_ctx, yyscan_t scanner);
-LTTNG_HIDDEN
-int yylex(union YYSTYPE *yyval, yyscan_t scanner);
-LTTNG_HIDDEN
-int yylex_init_extra(struct filter_parser_ctx *parser_ctx, yyscan_t * ptr_yy_globals);
-LTTNG_HIDDEN
-int yylex_destroy(yyscan_t yyparser_ctx);
-LTTNG_HIDDEN
-void yyrestart(FILE * in_str, yyscan_t parser_ctx);
-
-struct gc_string {
-       struct cds_list_head gc;
-       size_t alloclen;
-       char s[];
-};
-
-static const char *node_type_to_str[] = {
-       [ NODE_UNKNOWN ] = "NODE_UNKNOWN",
-       [ NODE_ROOT ] = "NODE_ROOT",
-       [ NODE_EXPRESSION ] = "NODE_EXPRESSION",
-       [ NODE_OP ] = "NODE_OP",
-       [ NODE_UNARY_OP ] = "NODE_UNARY_OP",
-};
-
-LTTNG_HIDDEN
-const char *node_type(struct filter_node *node)
-{
-       if (node->type < NR_NODE_TYPES)
-               return node_type_to_str[node->type];
-       else
-               return NULL;
-}
-
-static struct gc_string *gc_string_alloc(struct filter_parser_ctx *parser_ctx,
-                                        size_t len)
-{
-       struct gc_string *gstr;
-       size_t alloclen;
-
-       /* TODO: could be faster with find first bit or glib Gstring */
-       /* sizeof long to account for malloc header (int or long ?) */
-       for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + len;
-            alloclen *= 2);
-
-       gstr = zmalloc(alloclen);
-       if (!gstr) {
-               goto end;
-       }
-       cds_list_add(&gstr->gc, &parser_ctx->allocated_strings);
-       gstr->alloclen = alloclen;
-end:
-       return gstr;
-}
-
-/*
- * note: never use gc_string_append on a string that has external references.
- * gsrc will be garbage collected immediately, and gstr might be.
- * Should only be used to append characters to a string literal or constant.
- */
-LTTNG_HIDDEN
-struct gc_string *gc_string_append(struct filter_parser_ctx *parser_ctx,
-                                  struct gc_string *gstr,
-                                  struct gc_string *gsrc)
-{
-       size_t newlen = strlen(gsrc->s) + strlen(gstr->s) + 1;
-       size_t alloclen;
-
-       /* TODO: could be faster with find first bit or glib Gstring */
-       /* sizeof long to account for malloc header (int or long ?) */
-       for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + newlen;
-            alloclen *= 2);
-
-       if (alloclen > gstr->alloclen) {
-               struct gc_string *newgstr;
-
-               newgstr = gc_string_alloc(parser_ctx, newlen);
-               strcpy(newgstr->s, gstr->s);
-               strcat(newgstr->s, gsrc->s);
-               cds_list_del(&gstr->gc);
-               free(gstr);
-               gstr = newgstr;
-       } else {
-               strcat(gstr->s, gsrc->s);
-       }
-       cds_list_del(&gsrc->gc);
-       free(gsrc);
-       return gstr;
-}
-
-LTTNG_HIDDEN
-void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src)
-{
-       lvalp->gs = gc_string_alloc(parser_ctx, strlen(src) + 1);
-       strcpy(lvalp->gs->s, src);
-}
-
-static struct filter_node *make_node(struct filter_parser_ctx *scanner,
-                                 enum node_type type)
-{
-       struct filter_ast *ast = filter_parser_get_ast(scanner);
-       struct filter_node *node;
-
-       node = zmalloc(sizeof(*node));
-       if (!node)
-               return NULL;
-       memset(node, 0, sizeof(*node));
-       node->type = type;
-       cds_list_add(&node->gc, &ast->allocated_nodes);
-
-       switch (type) {
-       case NODE_ROOT:
-               fprintf(stderr, "[error] %s: trying to create root node\n", __func__);
-               break;
-
-       case NODE_EXPRESSION:
-               break;
-       case NODE_OP:
-               break;
-       case NODE_UNARY_OP:
-               break;
-
-       case NODE_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown node type %d\n", __func__,
-                       (int) type);
-               break;
-       }
-
-       return node;
-}
-
-static struct filter_node *make_op_node(struct filter_parser_ctx *scanner,
-                       enum op_type type,
-                       struct filter_node *lchild,
-                       struct filter_node *rchild)
-{
-       struct filter_ast *ast = filter_parser_get_ast(scanner);
-       struct filter_node *node;
-
-       node = zmalloc(sizeof(*node));
-       if (!node)
-               return NULL;
-       memset(node, 0, sizeof(*node));
-       node->type = NODE_OP;
-       cds_list_add(&node->gc, &ast->allocated_nodes);
-       node->u.op.type = type;
-       node->u.op.lchild = lchild;
-       node->u.op.rchild = rchild;
-       return node;
-}
-
-LTTNG_HIDDEN
-void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str)
-{
-       fprintf(stderr, "error %s\n", str);
-}
-LTTNG_HIDDEN
-int yywrap(void)
-{
-       return 1;
-} 
-
-#define parse_error(parser_ctx, str)                           \
-do {                                                           \
-       yyerror(parser_ctx, parser_ctx->scanner, YY_("parse error: " str "\n"));        \
-       YYERROR;                                                \
-} while (0)
-
-static void free_strings(struct cds_list_head *list)
-{
-       struct gc_string *gstr, *tmp;
-
-       cds_list_for_each_entry_safe(gstr, tmp, list, gc)
-               free(gstr);
-}
-
-static struct filter_ast *filter_ast_alloc(void)
-{
-       struct filter_ast *ast;
-
-       ast = zmalloc(sizeof(*ast));
-       if (!ast)
-               return NULL;
-       memset(ast, 0, sizeof(*ast));
-       CDS_INIT_LIST_HEAD(&ast->allocated_nodes);
-       ast->root.type = NODE_ROOT;
-       return ast;
-}
-
-static void filter_ast_free(struct filter_ast *ast)
-{
-       struct filter_node *node, *tmp;
-
-       cds_list_for_each_entry_safe(node, tmp, &ast->allocated_nodes, gc)
-               free(node);
-       free(ast);
-}
-
-LTTNG_HIDDEN
-int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx)
-{
-       return yyparse(parser_ctx, parser_ctx->scanner);
-}
-
-LTTNG_HIDDEN
-struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input)
-{
-       struct filter_parser_ctx *parser_ctx;
-       int ret;
-
-       yydebug = filter_parser_debug;
-
-       parser_ctx = zmalloc(sizeof(*parser_ctx));
-       if (!parser_ctx)
-               return NULL;
-       memset(parser_ctx, 0, sizeof(*parser_ctx));
-
-       ret = yylex_init_extra(parser_ctx, &parser_ctx->scanner);
-       if (ret) {
-               fprintf(stderr, "yylex_init error\n");
-               goto cleanup_parser_ctx;
-       }
-       /* Start processing new stream */
-       yyrestart(input, parser_ctx->scanner);
-
-       parser_ctx->ast = filter_ast_alloc();
-       if (!parser_ctx->ast)
-               goto cleanup_lexer;
-       CDS_INIT_LIST_HEAD(&parser_ctx->allocated_strings);
-
-       if (yydebug)
-               fprintf(stdout, "parser_ctx input is a%s.\n",
-                       isatty(fileno(input)) ? "n interactive tty" :
-                                               " noninteractive file");
-
-       return parser_ctx;
-
-cleanup_lexer:
-       ret = yylex_destroy(parser_ctx->scanner);
-       if (!ret)
-               fprintf(stderr, "yylex_destroy error\n");
-cleanup_parser_ctx:
-       free(parser_ctx);
-       return NULL;
-}
-
-LTTNG_HIDDEN
-void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx)
-{
-       int ret;
-
-       free_strings(&parser_ctx->allocated_strings);
-       filter_ast_free(parser_ctx->ast);
-       ret = yylex_destroy(parser_ctx->scanner);
-       if (ret)
-               fprintf(stderr, "yylex_destroy error\n");
-       free(parser_ctx);
-}
-
-%}
-
-%define api.pure
-       /* %locations */
-%parse-param {struct filter_parser_ctx *parser_ctx}
-%parse-param {yyscan_t scanner}
-%lex-param {yyscan_t scanner}
-%start translation_unit
-%token CHARACTER_CONSTANT_START SQUOTE STRING_LITERAL_START DQUOTE
-%token ESCSEQ CHAR_STRING_TOKEN
-%token DECIMAL_CONSTANT OCTAL_CONSTANT HEXADECIMAL_CONSTANT FLOAT_CONSTANT
-%token LSBRAC RSBRAC LPAREN RPAREN LBRAC RBRAC RARROW
-%token STAR PLUS MINUS
-%token MOD_OP DIV_OP RIGHT_OP LEFT_OP
-%token EQ_OP NE_OP LE_OP GE_OP LT_OP GT_OP AND_OP OR_OP NOT_OP
-%token ASSIGN COLON SEMICOLON DOTDOTDOT DOT EQUAL COMMA
-%token XOR_BIN AND_BIN OR_BIN NOT_BIN
-
-%token <gs> IDENTIFIER GLOBAL_IDENTIFIER
-%token ERROR
-%union
-{
-       long long ll;
-       char c;
-       struct gc_string *gs;
-       struct filter_node *n;
-}
-
-%type <gs> s_char s_char_sequence c_char c_char_sequence
-
-%type <n> primary_expression
-%type <n> prefix_expression
-%type <n> prefix_expression_rec
-%type <n> postfix_expression
-%type <n> unary_expression
-%type <n> unary_operator
-%type <n> multiplicative_expression
-%type <n> additive_expression
-%type <n> shift_expression
-%type <n> relational_expression
-%type <n> equality_expression
-%type <n> and_expression
-%type <n> exclusive_or_expression
-%type <n> inclusive_or_expression
-%type <n> logical_and_expression
-%type <n> logical_or_expression
-%type <n> expression
-%type <n> identifiers
-
-%%
-
-
-/* 1.5 Constants */
-
-c_char_sequence:
-               c_char
-               {       $$ = $1;                                        }
-       |       c_char_sequence c_char
-               {       $$ = gc_string_append(parser_ctx, $1, $2);              }
-       ;
-
-c_char:
-               CHAR_STRING_TOKEN
-               {       $$ = yylval.gs;                                 }
-       |       ESCSEQ
-               {
-                       parse_error(parser_ctx, "escape sequences not supported yet");
-               }
-       ;
-
-/* 1.6 String literals */
-
-s_char_sequence:
-               s_char
-               {       $$ = $1;                                        }
-       |       s_char_sequence s_char
-               {       $$ = gc_string_append(parser_ctx, $1, $2);              }
-       ;
-
-s_char:
-               CHAR_STRING_TOKEN
-               {       $$ = yylval.gs;                                 }
-       |       ESCSEQ
-               {
-                       parse_error(parser_ctx, "escape sequences not supported yet");
-               }
-       ;
-
-primary_expression:
-               DECIMAL_CONSTANT
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_CONSTANT;
-                       if (sscanf(yylval.gs->s, "%" WIDTH_u64_SCANF_IS_A_BROKEN_API SCNu64,
-                                       &$$->u.expression.u.constant) != 1) {
-                               parse_error(parser_ctx, "cannot scanf decimal constant");
-                       }
-               }
-       |       OCTAL_CONSTANT
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_CONSTANT;
-                       if (!strcmp(yylval.gs->s, "0")) {
-                               $$->u.expression.u.constant = 0;
-                       } else if (sscanf(yylval.gs->s, "0%" WIDTH_o64_SCANF_IS_A_BROKEN_API SCNo64,
-                                       &$$->u.expression.u.constant) != 1) {
-                               parse_error(parser_ctx, "cannot scanf octal constant");
-                       }
-               }
-       |       HEXADECIMAL_CONSTANT
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_CONSTANT;
-                       if (sscanf(yylval.gs->s, "0x%" WIDTH_x64_SCANF_IS_A_BROKEN_API SCNx64,
-                                       &$$->u.expression.u.constant) != 1) {
-                               parse_error(parser_ctx, "cannot scanf hexadecimal constant");
-                       }
-               }
-       |       FLOAT_CONSTANT
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_FLOAT_CONSTANT;
-                       if (sscanf(yylval.gs->s, "%" WIDTH_lg_SCANF_IS_A_BROKEN_API "lg",
-                                       &$$->u.expression.u.float_constant) != 1) {
-                               parse_error(parser_ctx, "cannot scanf float constant");
-                       }
-               }
-       |       STRING_LITERAL_START DQUOTE
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_STRING;
-                       $$->u.expression.u.string = "";
-               }
-       |       STRING_LITERAL_START s_char_sequence DQUOTE
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_STRING;
-                       $$->u.expression.u.string = $2->s;
-               }
-       |       CHARACTER_CONSTANT_START c_char_sequence SQUOTE
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_STRING;
-                       $$->u.expression.u.string = $2->s;
-               }
-       |       LPAREN expression RPAREN
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_NESTED;
-                       $$->u.expression.u.child = $2;
-               }
-       ;
-
-identifiers
-       :       IDENTIFIER
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_IDENTIFIER;
-                       $$->u.expression.u.identifier = yylval.gs->s;
-               }
-       |       GLOBAL_IDENTIFIER
-               {
-                       $$ = make_node(parser_ctx, NODE_EXPRESSION);
-                       $$->u.expression.type = AST_EXP_GLOBAL_IDENTIFIER;
-                       $$->u.expression.u.identifier = yylval.gs->s;
-               }
-       ;
-
-prefix_expression_rec
-       : LSBRAC unary_expression RSBRAC
-               {
-                       $$ = $2;
-               }
-       | LSBRAC unary_expression RSBRAC prefix_expression_rec
-               {
-                       $$ = $2;
-                       $$->u.expression.pre_op = AST_LINK_BRACKET;
-                       $$->u.expression.prev = $4;
-               }
-       ;
-
-prefix_expression
-       : identifiers
-               {
-                       $$ = $1;
-               }
-       | identifiers prefix_expression_rec
-               {
-                       $$ = $1;
-                       $$->u.expression.pre_op = AST_LINK_BRACKET;
-                       $$->u.expression.next_bracket = $2;
-               }
-       ;
-
-postfix_expression
-       : prefix_expression
-               {
-                       $$ = $1;
-               }
-       | postfix_expression DOT prefix_expression
-               {
-                       $$ = $3;
-                       $$->u.expression.post_op = AST_LINK_DOT;
-                       $$->u.expression.prev = $1;
-               }
-       | postfix_expression RARROW prefix_expression
-               {
-                       $$ = $3;
-                       $$->u.expression.post_op = AST_LINK_RARROW;
-                       $$->u.expression.prev = $1;
-               }
-       ;
-
-unary_expression
-       : postfix_expression
-               {       $$ = $1;                                        }
-       | primary_expression
-               {       $$ = $1;                                        }
-       | unary_operator unary_expression
-               {
-                       $$ = $1;
-                       $$->u.unary_op.child = $2;
-               }
-       ;
-
-unary_operator
-       : PLUS
-               {
-                       $$ = make_node(parser_ctx, NODE_UNARY_OP);
-                       $$->u.unary_op.type = AST_UNARY_PLUS;
-               }
-       | MINUS
-               {
-                       $$ = make_node(parser_ctx, NODE_UNARY_OP);
-                       $$->u.unary_op.type = AST_UNARY_MINUS;
-               }
-       | NOT_OP
-               {
-                       $$ = make_node(parser_ctx, NODE_UNARY_OP);
-                       $$->u.unary_op.type = AST_UNARY_NOT;
-               }
-       | NOT_BIN
-               {
-                       $$ = make_node(parser_ctx, NODE_UNARY_OP);
-                       $$->u.unary_op.type = AST_UNARY_BIT_NOT;
-               }
-       ;
-
-multiplicative_expression
-       : unary_expression
-               {       $$ = $1;                                        }
-       | multiplicative_expression STAR unary_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_MUL, $1, $3);
-               }
-       | multiplicative_expression DIV_OP unary_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_DIV, $1, $3);
-               }
-       | multiplicative_expression MOD_OP unary_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_MOD, $1, $3);
-               }
-       ;
-
-additive_expression
-       : multiplicative_expression
-               {       $$ = $1;                                        }
-       | additive_expression PLUS multiplicative_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_PLUS, $1, $3);
-               }
-       | additive_expression MINUS multiplicative_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_MINUS, $1, $3);
-               }
-       ;
-
-shift_expression
-       : additive_expression
-               {       $$ = $1;                                        }
-       | shift_expression LEFT_OP additive_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_BIT_LSHIFT, $1, $3);
-               }
-       | shift_expression RIGHT_OP additive_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_BIT_RSHIFT, $1, $3);
-               }
-       ;
-
-and_expression
-       : shift_expression
-               {       $$ = $1;                                        }
-       | and_expression AND_BIN shift_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_BIT_AND, $1, $3);
-               }
-       ;
-
-exclusive_or_expression
-       : and_expression
-               {       $$ = $1;                                        }
-       | exclusive_or_expression XOR_BIN and_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_BIT_XOR, $1, $3);
-               }
-       ;
-
-inclusive_or_expression
-       : exclusive_or_expression
-               {       $$ = $1;                                        }
-       | inclusive_or_expression OR_BIN exclusive_or_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_BIT_OR, $1, $3);
-               }
-       ;
-
-relational_expression
-       : inclusive_or_expression
-               {       $$ = $1;                                        }
-       | relational_expression LT_OP inclusive_or_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_LT, $1, $3);
-               }
-       | relational_expression GT_OP inclusive_or_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_GT, $1, $3);
-               }
-       | relational_expression LE_OP inclusive_or_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_LE, $1, $3);
-               }
-       | relational_expression GE_OP inclusive_or_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_GE, $1, $3);
-               }
-       ;
-
-equality_expression
-       : relational_expression
-               {       $$ = $1;                                        }
-       | equality_expression EQ_OP relational_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_EQ, $1, $3);
-               }
-       | equality_expression NE_OP relational_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_NE, $1, $3);
-               }
-       ;
-
-logical_and_expression
-       : equality_expression
-               {       $$ = $1;                                        }
-       | logical_and_expression AND_OP equality_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_AND, $1, $3);
-               }
-       ;
-
-logical_or_expression
-       : logical_and_expression
-               {       $$ = $1;                                        }
-       | logical_or_expression OR_OP logical_and_expression
-               {
-                       $$ = make_op_node(parser_ctx, AST_OP_OR, $1, $3);
-               }
-       ;
-
-expression
-       : logical_or_expression
-               {       $$ = $1;                                        }
-       ;
-
-translation_unit
-       : expression
-               {
-                       parser_ctx->ast->root.u.root.child = $1;
-               }
-       ;
diff --git a/src/lib/lttng-ctl/filter/filter-symbols.h b/src/lib/lttng-ctl/filter/filter-symbols.h
deleted file mode 100644 (file)
index 031776d..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef _FILTER_SYMBOLS_H
-#define _FILTER_SYMBOLS_H
-
-/*
- * filter-symbols.h
- *
- * LTTng filter flex/bison symbol prefixes
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#define yy_create_buffer lttng_yy_create_buffer
-#define yy_delete_buffer lttng_yy_delete_buffer
-#define yy_flush_buffer lttng_yy_flush_buffer
-#define yy_scan_buffer lttng_yy_scan_buffer
-#define yy_scan_bytes lttng_yy_scan_bytes
-#define yy_scan_string lttng_yy_scan_string
-#define yy_switch_to_buffer lttng_yy_switch_to_buffer
-#define yyalloc lttng_yyalloc
-#define yyfree lttng_yyfree
-#define yyget_column lttng_yyget_column
-#define yyget_debug lttng_yyget_debug
-#define yyget_extra lttng_yyget_extra
-#define yyget_in lttng_yyget_in
-#define yyget_leng lttng_yyget_leng
-#define yyget_lineno lttng_yyget_lineno
-#define yyget_lval lttng_yyget_lval
-#define yyget_out lttng_yyget_out
-#define yyget_text lttng_yyget_text
-#define yylex_init lttng_yylex_init
-#define yypop_buffer_state lttng_yypop_buffer_state
-#define yypush_buffer_state lttng_yypush_buffer_state
-#define yyrealloc lttng_yyrealloc
-#define yyset_column lttng_yyset_column
-#define yyset_debug lttng_yyset_debug
-#define yyset_extra lttng_yyset_extra
-#define yyset_in lttng_yyset_in
-#define yyset_lineno lttng_yyset_lineno
-#define yyset_lval lttng_yyset_lval
-#define yyset_out lttng_yyset_out
-
-#endif /* _FILTER_SYMBOLS_H */
diff --git a/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c b/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c
deleted file mode 100644 (file)
index 699273c..0000000
+++ /dev/null
@@ -1,830 +0,0 @@
-/*
- * filter-visitor-generate-bytecode.c
- *
- * LTTng filter bytecode generation
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <common/align.h>
-#include <common/compat/string.h>
-
-#include "filter-bytecode.h"
-#include "filter-ir.h"
-#include "filter-ast.h"
-
-#include <common/macros.h>
-
-#ifndef max_t
-#define max_t(type, a, b)      ((type) ((a) > (b) ? (a) : (b)))
-#endif
-
-#define INIT_ALLOC_SIZE                4
-
-static
-int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx,
-               struct ir_op *node);
-
-static inline int get_count_order(unsigned int count)
-{
-       int order;
-
-       order = lttng_fls(count) - 1;
-       if (count & (count - 1))
-               order++;
-       return order;
-}
-
-static
-int bytecode_init(struct lttng_filter_bytecode_alloc **fb)
-{
-       uint32_t alloc_len;
-
-       alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + INIT_ALLOC_SIZE;
-       *fb = calloc(alloc_len, 1);
-       if (!*fb) {
-               return -ENOMEM;
-       } else {
-               (*fb)->alloc_len = alloc_len;
-               return 0;
-       }
-}
-
-static
-int32_t bytecode_reserve(struct lttng_filter_bytecode_alloc **fb, uint32_t align, uint32_t len)
-{
-       int32_t ret;
-       uint32_t padding = offset_align((*fb)->b.len, align);
-       uint32_t new_len = (*fb)->b.len + padding + len;
-       uint32_t new_alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + new_len;
-       uint32_t old_alloc_len = (*fb)->alloc_len;
-
-       if (new_len > LTTNG_FILTER_MAX_LEN)
-               return -EINVAL;
-
-       if (new_alloc_len > old_alloc_len) {
-               struct lttng_filter_bytecode_alloc *newptr;
-
-               new_alloc_len =
-                       max_t(uint32_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1);
-               newptr = realloc(*fb, new_alloc_len);
-               if (!newptr)
-                       return -ENOMEM;
-               *fb = newptr;
-               /* We zero directly the memory from start of allocation. */
-               memset(&((char *) *fb)[old_alloc_len], 0, new_alloc_len - old_alloc_len);
-               (*fb)->alloc_len = new_alloc_len;
-       }
-       (*fb)->b.len += padding;
-       ret = (*fb)->b.len;
-       (*fb)->b.len += len;
-       return ret;
-}
-
-static
-int bytecode_push(struct lttng_filter_bytecode_alloc **fb, const void *data,
-               uint32_t align, uint32_t len)
-{
-       int32_t offset;
-
-       offset = bytecode_reserve(fb, align, len);
-       if (offset < 0)
-               return offset;
-       memcpy(&(*fb)->b.data[offset], data, len);
-       return 0;
-}
-
-static
-int bytecode_push_logical(struct lttng_filter_bytecode_alloc **fb,
-               struct logical_op *data,
-               uint32_t align, uint32_t len,
-               uint16_t *skip_offset)
-{
-       int32_t offset;
-
-       offset = bytecode_reserve(fb, align, len);
-       if (offset < 0)
-               return offset;
-       memcpy(&(*fb)->b.data[offset], data, len);
-       *skip_offset =
-               (void *) &((struct logical_op *) &(*fb)->b.data[offset])->skip_offset
-                       - (void *) &(*fb)->b.data[0];
-       return 0;
-}
-
-static
-int bytecode_patch(struct lttng_filter_bytecode_alloc **fb,
-               const void *data,
-               uint16_t offset,
-               uint32_t len)
-{
-       if (offset >= (*fb)->b.len) {
-               return -EINVAL;
-       }
-       memcpy(&(*fb)->b.data[offset], data, len);
-       return 0;
-}
-
-static
-int visit_node_root(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
-       int ret;
-       struct return_op insn;
-
-       /* Visit child */
-       ret = recursive_visit_gen_bytecode(ctx, node->u.root.child);
-       if (ret)
-               return ret;
-
-       /* Generate end of bytecode instruction */
-       insn.op = FILTER_OP_RETURN;
-       return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
-}
-
-static
-int append_str(char **s, const char *append)
-{
-       char *old = *s;
-       char *new;
-       size_t oldlen = (old == NULL) ? 0 : strlen(old);
-       size_t appendlen = strlen(append);
-
-       new = calloc(oldlen + appendlen + 1, 1);
-       if (!new) {
-               return -ENOMEM;
-       }
-       if (oldlen) {
-               strcpy(new, old);
-       }
-       strcat(new, append);
-       *s = new;
-       free(old);
-       return 0;
-}
-
-/*
- * 1: match
- * 0: no match
- * < 0: error
- */
-static
-int load_expression_legacy_match(const struct ir_load_expression *exp,
-               enum filter_op *op_type,
-               char **symbol)
-{
-       const struct ir_load_expression_op *op;
-       bool need_dot = false;
-
-       op = exp->child;
-       switch (op->type) {
-       case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
-               *op_type = FILTER_OP_GET_CONTEXT_REF;
-               if (append_str(symbol, "$ctx.")) {
-                       return -ENOMEM;
-               }
-               need_dot = false;
-               break;
-       case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
-               *op_type = FILTER_OP_GET_CONTEXT_REF;
-               if (append_str(symbol, "$app.")) {
-                       return -ENOMEM;
-               }
-               need_dot = false;
-               break;
-       case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
-               *op_type = FILTER_OP_LOAD_FIELD_REF;
-               need_dot = false;
-               break;
-
-       case IR_LOAD_EXPRESSION_GET_SYMBOL:
-       case IR_LOAD_EXPRESSION_GET_INDEX:
-       case IR_LOAD_EXPRESSION_LOAD_FIELD:
-       default:
-               return 0;       /* no match */
-       }
-
-       for (;;) {
-               op = op->next;
-               if (!op) {
-                       return 0;       /* no match */
-               }
-               switch (op->type) {
-               case IR_LOAD_EXPRESSION_LOAD_FIELD:
-                       goto end;
-               case IR_LOAD_EXPRESSION_GET_SYMBOL:
-                       if (need_dot && append_str(symbol, ".")) {
-                               return -ENOMEM;
-                       }
-                       if (append_str(symbol, op->u.symbol)) {
-                               return -ENOMEM;
-                       }
-                       break;
-               default:
-                       return 0;        /* no match */
-               }
-               need_dot = true;
-       }
-end:
-       return 1;       /* Legacy match */
-}
-
-/*
- * 1: legacy match
- * 0: no legacy match
- * < 0: error
- */
-static
-int visit_node_load_expression_legacy(struct filter_parser_ctx *ctx,
-               const struct ir_load_expression *exp,
-               const struct ir_load_expression_op *op)
-{
-       struct load_op *insn = NULL;
-       uint32_t insn_len = sizeof(struct load_op)
-               + sizeof(struct field_ref);
-       struct field_ref ref_offset;
-       uint32_t reloc_offset_u32;
-       uint16_t reloc_offset;
-       enum filter_op op_type;
-       char *symbol = NULL;
-       int ret;
-
-       ret = load_expression_legacy_match(exp, &op_type, &symbol);
-       if (ret <= 0) {
-               goto end;
-       }
-       insn = calloc(insn_len, 1);
-       if (!insn) {
-               ret = -ENOMEM;
-               goto end;
-       }
-       insn->op = op_type;
-       ref_offset.offset = (uint16_t) -1U;
-       memcpy(insn->data, &ref_offset, sizeof(ref_offset));
-       /* reloc_offset points to struct load_op */
-       reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
-       if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
-               ret = -EINVAL;
-               goto end;
-       }
-       reloc_offset = (uint16_t) reloc_offset_u32;
-       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-       if (ret) {
-               goto end;
-       }
-       /* append reloc */
-       ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
-                               1, sizeof(reloc_offset));
-       if (ret) {
-               goto end;
-       }
-       ret = bytecode_push(&ctx->bytecode_reloc, symbol,
-                               1, strlen(symbol) + 1);
-       if (ret) {
-               goto end;
-       }
-       ret = 1;        /* legacy */
-end:
-       free(insn);
-       free(symbol);
-       return ret;
-}
-
-static
-int visit_node_load_expression(struct filter_parser_ctx *ctx,
-               const struct ir_op *node)
-{
-       struct ir_load_expression *exp;
-       struct ir_load_expression_op *op;
-       int ret;
-
-       exp = node->u.load.u.expression;
-       if (!exp) {
-               return -EINVAL;
-       }
-       op = exp->child;
-       if (!op) {
-               return -EINVAL;
-       }
-
-       /*
-        * TODO: if we remove legacy load for application contexts, we
-        * need to update session bytecode parser as well.
-        */
-       ret = visit_node_load_expression_legacy(ctx, exp, op);
-       if (ret < 0) {
-               return ret;
-       }
-       if (ret > 0) {
-               return 0;       /* legacy */
-       }
-
-       for (; op != NULL; op = op->next) {
-               switch (op->type) {
-               case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
-               {
-                       struct load_op *insn;
-                       uint32_t insn_len = sizeof(struct load_op);
-                       int ret;
-
-                       insn = calloc(insn_len, 1);
-                       if (!insn)
-                               return -ENOMEM;
-                       insn->op = FILTER_OP_GET_CONTEXT_ROOT;
-                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-                       free(insn);
-                       if (ret) {
-                               return ret;
-                       }
-                       break;
-               }
-               case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
-               {
-                       struct load_op *insn;
-                       uint32_t insn_len = sizeof(struct load_op);
-                       int ret;
-
-                       insn = calloc(insn_len, 1);
-                       if (!insn)
-                               return -ENOMEM;
-                       insn->op = FILTER_OP_GET_APP_CONTEXT_ROOT;
-                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-                       free(insn);
-                       if (ret) {
-                               return ret;
-                       }
-                       break;
-               }
-               case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
-               {
-                       struct load_op *insn;
-                       uint32_t insn_len = sizeof(struct load_op);
-                       int ret;
-
-                       insn = calloc(insn_len, 1);
-                       if (!insn)
-                               return -ENOMEM;
-                       insn->op = FILTER_OP_GET_PAYLOAD_ROOT;
-                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-                       free(insn);
-                       if (ret) {
-                               return ret;
-                       }
-                       break;
-               }
-               case IR_LOAD_EXPRESSION_GET_SYMBOL:
-               {
-                       struct load_op *insn;
-                       uint32_t insn_len = sizeof(struct load_op)
-                               + sizeof(struct get_symbol);
-                       struct get_symbol symbol_offset;
-                       uint32_t reloc_offset_u32;
-                       uint16_t reloc_offset;
-                       uint32_t bytecode_reloc_offset_u32;
-                       int ret;
-
-                       insn = calloc(insn_len, 1);
-                       if (!insn)
-                               return -ENOMEM;
-                       insn->op = FILTER_OP_GET_SYMBOL;
-                       bytecode_reloc_offset_u32 =
-                                       bytecode_get_len(&ctx->bytecode_reloc->b)
-                                       + sizeof(reloc_offset);
-                       symbol_offset.offset =
-                                       (uint16_t) bytecode_reloc_offset_u32;
-                       memcpy(insn->data, &symbol_offset,
-                                       sizeof(symbol_offset));
-                       /* reloc_offset points to struct load_op */
-                       reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
-                       if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
-                               free(insn);
-                               return -EINVAL;
-                       }
-                       reloc_offset = (uint16_t) reloc_offset_u32;
-                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-                       if (ret) {
-                               free(insn);
-                               return ret;
-                       }
-                       /* append reloc */
-                       ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
-                                       1, sizeof(reloc_offset));
-                       if (ret) {
-                               free(insn);
-                               return ret;
-                       }
-                       ret = bytecode_push(&ctx->bytecode_reloc,
-                                       op->u.symbol,
-                                       1, strlen(op->u.symbol) + 1);
-                       free(insn);
-                       if (ret) {
-                               return ret;
-                       }
-                       break;
-               }
-               case IR_LOAD_EXPRESSION_GET_INDEX:
-               {
-                       struct load_op *insn;
-                       uint32_t insn_len = sizeof(struct load_op)
-                               + sizeof(struct get_index_u64);
-                       struct get_index_u64 index;
-                       int ret;
-
-                       insn = calloc(insn_len, 1);
-                       if (!insn)
-                               return -ENOMEM;
-                       insn->op = FILTER_OP_GET_INDEX_U64;
-                       index.index = op->u.index;
-                       memcpy(insn->data, &index, sizeof(index));
-                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-                       free(insn);
-                       if (ret) {
-                               return ret;
-                       }
-                       break;
-               }
-               case IR_LOAD_EXPRESSION_LOAD_FIELD:
-               {
-                       struct load_op *insn;
-                       uint32_t insn_len = sizeof(struct load_op);
-                       int ret;
-
-                       insn = calloc(insn_len, 1);
-                       if (!insn)
-                               return -ENOMEM;
-                       insn->op = FILTER_OP_LOAD_FIELD;
-                       ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-                       free(insn);
-                       if (ret) {
-                               return ret;
-                       }
-                       break;
-               }
-               }
-       }
-       return 0;
-}
-
-static
-int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
-       int ret;
-
-       switch (node->data_type) {
-       case IR_DATA_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] Unknown data type in %s\n",
-                       __func__);
-               return -EINVAL;
-
-       case IR_DATA_STRING:
-       {
-               struct load_op *insn;
-               uint32_t insn_len = sizeof(struct load_op)
-                       + strlen(node->u.load.u.string.value) + 1;
-
-               insn = calloc(insn_len, 1);
-               if (!insn)
-                       return -ENOMEM;
-
-               switch (node->u.load.u.string.type) {
-               case IR_LOAD_STRING_TYPE_GLOB_STAR:
-                       /*
-                        * We explicitly tell the interpreter here that
-                        * this load is a full star globbing pattern so
-                        * that the appropriate matching function can be
-                        * called. Also, see comment below.
-                        */
-                       insn->op = FILTER_OP_LOAD_STAR_GLOB_STRING;
-                       break;
-               default:
-                       /*
-                        * This is the "legacy" string, which includes
-                        * star globbing patterns with a star only at
-                        * the end. Both "plain" and "star at the end"
-                        * literal strings are handled at the same place
-                        * by the tracer's filter bytecode interpreter,
-                        * whereas full star globbing patterns (stars
-                        * can be anywhere in the string) is a special
-                        * case.
-                        */
-                       insn->op = FILTER_OP_LOAD_STRING;
-                       break;
-               }
-
-               strcpy(insn->data, node->u.load.u.string.value);
-               ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-               free(insn);
-               return ret;
-       }
-       case IR_DATA_NUMERIC:
-       {
-               struct load_op *insn;
-               uint32_t insn_len = sizeof(struct load_op)
-                       + sizeof(struct literal_numeric);
-
-               insn = calloc(insn_len, 1);
-               if (!insn)
-                       return -ENOMEM;
-               insn->op = FILTER_OP_LOAD_S64;
-               memcpy(insn->data, &node->u.load.u.num, sizeof(int64_t));
-               ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-               free(insn);
-               return ret;
-       }
-       case IR_DATA_FLOAT:
-       {
-               struct load_op *insn;
-               uint32_t insn_len = sizeof(struct load_op)
-                       + sizeof(struct literal_double);
-
-               insn = calloc(insn_len, 1);
-               if (!insn)
-                       return -ENOMEM;
-               insn->op = FILTER_OP_LOAD_DOUBLE;
-               memcpy(insn->data, &node->u.load.u.flt, sizeof(double));
-               ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
-               free(insn);
-               return ret;
-       }
-       case IR_DATA_EXPRESSION:
-               return visit_node_load_expression(ctx, node);
-       }
-}
-
-static
-int visit_node_unary(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
-       int ret;
-       struct unary_op insn;
-
-       /* Visit child */
-       ret = recursive_visit_gen_bytecode(ctx, node->u.unary.child);
-       if (ret)
-               return ret;
-
-       /* Generate end of bytecode instruction */
-       switch (node->u.unary.type) {
-       case AST_UNARY_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] Unknown unary node type in %s\n",
-                       __func__);
-               return -EINVAL;
-       case AST_UNARY_PLUS:
-               /* Nothing to do. */
-               return 0;
-       case AST_UNARY_MINUS:
-               insn.op = FILTER_OP_UNARY_MINUS;
-               return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
-       case AST_UNARY_NOT:
-               insn.op = FILTER_OP_UNARY_NOT;
-               return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
-       case AST_UNARY_BIT_NOT:
-               insn.op = FILTER_OP_UNARY_BIT_NOT;
-               return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
-       }
-}
-
-/*
- * Binary comparator nesting is disallowed. This allows fitting into
- * only 2 registers.
- */
-static
-int visit_node_binary(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
-       int ret;
-       struct binary_op insn;
-
-       /* Visit child */
-       ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left);
-       if (ret)
-               return ret;
-       ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right);
-       if (ret)
-               return ret;
-
-       switch (node->u.binary.type) {
-       case AST_OP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] Unknown unary node type in %s\n",
-                       __func__);
-               return -EINVAL;
-
-       case AST_OP_AND:
-       case AST_OP_OR:
-               fprintf(stderr, "[error] Unexpected logical node type in %s\n",
-                       __func__);
-               return -EINVAL;
-
-       case AST_OP_MUL:
-               insn.op = FILTER_OP_MUL;
-               break;
-       case AST_OP_DIV:
-               insn.op = FILTER_OP_DIV;
-               break;
-       case AST_OP_MOD:
-               insn.op = FILTER_OP_MOD;
-               break;
-       case AST_OP_PLUS:
-               insn.op = FILTER_OP_PLUS;
-               break;
-       case AST_OP_MINUS:
-               insn.op = FILTER_OP_MINUS;
-               break;
-       case AST_OP_BIT_RSHIFT:
-               insn.op = FILTER_OP_BIT_RSHIFT;
-               break;
-       case AST_OP_BIT_LSHIFT:
-               insn.op = FILTER_OP_BIT_LSHIFT;
-               break;
-       case AST_OP_BIT_AND:
-               insn.op = FILTER_OP_BIT_AND;
-               break;
-       case AST_OP_BIT_OR:
-               insn.op = FILTER_OP_BIT_OR;
-               break;
-       case AST_OP_BIT_XOR:
-               insn.op = FILTER_OP_BIT_XOR;
-               break;
-
-       case AST_OP_EQ:
-               insn.op = FILTER_OP_EQ;
-               break;
-       case AST_OP_NE:
-               insn.op = FILTER_OP_NE;
-               break;
-       case AST_OP_GT:
-               insn.op = FILTER_OP_GT;
-               break;
-       case AST_OP_LT:
-               insn.op = FILTER_OP_LT;
-               break;
-       case AST_OP_GE:
-               insn.op = FILTER_OP_GE;
-               break;
-       case AST_OP_LE:
-               insn.op = FILTER_OP_LE;
-               break;
-       }
-       return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
-}
-
-/*
- * A logical op always return a s64 (1 or 0).
- */
-static
-int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
-       int ret;
-       struct logical_op insn;
-       uint16_t skip_offset_loc;
-       uint16_t target_loc;
-
-       /* Visit left child */
-       ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left);
-       if (ret)
-               return ret;
-       /* Cast to s64 if float or field ref */
-       if ((node->u.binary.left->data_type == IR_DATA_FIELD_REF
-                               || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
-                               || node->u.binary.left->data_type == IR_DATA_EXPRESSION)
-                       || node->u.binary.left->data_type == IR_DATA_FLOAT) {
-               struct cast_op cast_insn;
-
-               if (node->u.binary.left->data_type == IR_DATA_FIELD_REF
-                               || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
-                               || node->u.binary.left->data_type == IR_DATA_EXPRESSION) {
-                       cast_insn.op = FILTER_OP_CAST_TO_S64;
-               } else {
-                       cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;
-               }
-               ret = bytecode_push(&ctx->bytecode, &cast_insn,
-                                       1, sizeof(cast_insn));
-               if (ret)
-                       return ret;
-       }
-       switch (node->u.logical.type) {
-       default:
-               fprintf(stderr, "[error] Unknown node type in %s\n",
-                       __func__);
-               return -EINVAL;
-
-       case AST_OP_AND:
-               insn.op = FILTER_OP_AND;
-               break;
-       case AST_OP_OR:
-               insn.op = FILTER_OP_OR;
-               break;
-       }
-       insn.skip_offset = (uint16_t) -1UL;     /* Temporary */
-       ret = bytecode_push_logical(&ctx->bytecode, &insn, 1, sizeof(insn),
-                       &skip_offset_loc);
-       if (ret)
-               return ret;
-       /* Visit right child */
-       ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right);
-       if (ret)
-               return ret;
-       /* Cast to s64 if float or field ref */
-       if ((node->u.binary.right->data_type == IR_DATA_FIELD_REF
-                               || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
-                               || node->u.binary.right->data_type == IR_DATA_EXPRESSION)
-                       || node->u.binary.right->data_type == IR_DATA_FLOAT) {
-               struct cast_op cast_insn;
-
-               if (node->u.binary.right->data_type == IR_DATA_FIELD_REF
-                               || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
-                               || node->u.binary.right->data_type == IR_DATA_EXPRESSION) {
-                       cast_insn.op = FILTER_OP_CAST_TO_S64;
-               } else {
-                       cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;
-               }
-               ret = bytecode_push(&ctx->bytecode, &cast_insn,
-                                       1, sizeof(cast_insn));
-               if (ret)
-                       return ret;
-       }
-       /* We now know where the logical op can skip. */
-       target_loc = (uint16_t) bytecode_get_len(&ctx->bytecode->b);
-       ret = bytecode_patch(&ctx->bytecode,
-                       &target_loc,                    /* Offset to jump to */
-                       skip_offset_loc,                /* Where to patch */
-                       sizeof(uint16_t));
-       return ret;
-}
-
-/*
- * Postorder traversal of the tree. We need the children result before
- * we can evaluate the parent.
- */
-static
-int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx,
-               struct ir_op *node)
-{
-       switch (node->op) {
-       case IR_OP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] Unknown node type in %s\n",
-                       __func__);
-               return -EINVAL;
-
-       case IR_OP_ROOT:
-               return visit_node_root(ctx, node);
-       case IR_OP_LOAD:
-               return visit_node_load(ctx, node);
-       case IR_OP_UNARY:
-               return visit_node_unary(ctx, node);
-       case IR_OP_BINARY:
-               return visit_node_binary(ctx, node);
-       case IR_OP_LOGICAL:
-               return visit_node_logical(ctx, node);
-       }
-}
-
-LTTNG_HIDDEN
-void filter_bytecode_free(struct filter_parser_ctx *ctx)
-{
-       if (!ctx) {
-               return;
-       }
-
-       if (ctx->bytecode) {
-               free(ctx->bytecode);
-               ctx->bytecode = NULL;
-       }
-
-       if (ctx->bytecode_reloc) {
-               free(ctx->bytecode_reloc);
-               ctx->bytecode_reloc = NULL;
-       }
-}
-
-LTTNG_HIDDEN
-int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx)
-{
-       int ret;
-
-       ret = bytecode_init(&ctx->bytecode);
-       if (ret)
-               return ret;
-       ret = bytecode_init(&ctx->bytecode_reloc);
-       if (ret)
-               goto error;
-       ret = recursive_visit_gen_bytecode(ctx, ctx->ir_root);
-       if (ret)
-               goto error;
-
-       /* Finally, append symbol table to bytecode */
-       ctx->bytecode->b.reloc_table_offset = bytecode_get_len(&ctx->bytecode->b);
-       return bytecode_push(&ctx->bytecode, ctx->bytecode_reloc->b.data,
-                       1, bytecode_get_len(&ctx->bytecode_reloc->b));
-
-error:
-       filter_bytecode_free(ctx);
-       return ret;
-}
diff --git a/src/lib/lttng-ctl/filter/filter-visitor-generate-ir.c b/src/lib/lttng-ctl/filter/filter-visitor-generate-ir.c
deleted file mode 100644 (file)
index 024aac6..0000000
+++ /dev/null
@@ -1,909 +0,0 @@
-/*
- * filter-visitor-generate-ir.c
- *
- * LTTng filter generate intermediate representation
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-#include <common/macros.h>
-#include <common/string-utils/string-utils.h>
-
-static
-struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx,
-               struct filter_node *node, enum ir_side side);
-
-static
-struct ir_op *make_op_root(struct ir_op *child, enum ir_side side)
-{
-       struct ir_op *op;
-
-       op = calloc(sizeof(struct ir_op), 1);
-       if (!op)
-               return NULL;
-       switch (child->data_type) {
-       case IR_DATA_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] Unknown root child data type\n");
-               free(op);
-               return NULL;
-       case IR_DATA_STRING:
-               fprintf(stderr, "[error] String cannot be root data type\n");
-               free(op);
-               return NULL;
-       case IR_DATA_NUMERIC:
-       case IR_DATA_FIELD_REF:
-       case IR_DATA_GET_CONTEXT_REF:
-       case IR_DATA_EXPRESSION:
-               /* ok */
-               break;
-       }
-       op->op = IR_OP_ROOT;
-       op->side = side;
-       op->data_type = child->data_type;
-       op->signedness = child->signedness;
-       op->u.root.child = child;
-       return op;
-}
-
-static
-enum ir_load_string_type get_literal_string_type(const char *string)
-{
-       assert(string);
-
-       if (strutils_is_star_glob_pattern(string)) {
-               if (strutils_is_star_at_the_end_only_glob_pattern(string)) {
-                       return IR_LOAD_STRING_TYPE_GLOB_STAR_END;
-               }
-
-               return IR_LOAD_STRING_TYPE_GLOB_STAR;
-       }
-
-       return IR_LOAD_STRING_TYPE_PLAIN;
-}
-
-static
-struct ir_op *make_op_load_string(char *string, enum ir_side side)
-{
-       struct ir_op *op;
-
-       op = calloc(sizeof(struct ir_op), 1);
-       if (!op)
-               return NULL;
-       op->op = IR_OP_LOAD;
-       op->data_type = IR_DATA_STRING;
-       op->signedness = IR_SIGN_UNKNOWN;
-       op->side = side;
-       op->u.load.u.string.type = get_literal_string_type(string);
-       op->u.load.u.string.value = strdup(string);
-       if (!op->u.load.u.string.value) {
-               free(op);
-               return NULL;
-       }
-       return op;
-}
-
-static
-struct ir_op *make_op_load_numeric(int64_t v, enum ir_side side)
-{
-       struct ir_op *op;
-
-       op = calloc(sizeof(struct ir_op), 1);
-       if (!op)
-               return NULL;
-       op->op = IR_OP_LOAD;
-       op->data_type = IR_DATA_NUMERIC;
-       /* TODO: for now, all numeric values are signed */
-       op->signedness = IR_SIGNED;
-       op->side = side;
-       op->u.load.u.num = v;
-       return op;
-}
-
-static
-struct ir_op *make_op_load_float(double v, enum ir_side side)
-{
-       struct ir_op *op;
-
-       op = calloc(sizeof(struct ir_op), 1);
-       if (!op)
-               return NULL;
-       op->op = IR_OP_LOAD;
-       op->data_type = IR_DATA_FLOAT;
-       op->signedness = IR_SIGN_UNKNOWN;
-       op->side = side;
-       op->u.load.u.flt = v;
-       return op;
-}
-
-static
-void free_load_expression(struct ir_load_expression *load_expression)
-{
-       struct ir_load_expression_op *exp_op;
-
-       if (!load_expression)
-               return;
-       exp_op = load_expression->child;
-       for (;;) {
-               struct ir_load_expression_op *prev_exp_op;
-
-               if (!exp_op)
-                       break;
-               switch (exp_op->type) {
-               case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
-               case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
-               case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
-               case IR_LOAD_EXPRESSION_GET_INDEX:
-               case IR_LOAD_EXPRESSION_LOAD_FIELD:
-                       break;
-               case IR_LOAD_EXPRESSION_GET_SYMBOL:
-                       free(exp_op->u.symbol);
-                       break;
-               }
-               prev_exp_op = exp_op;
-               exp_op = exp_op->next;
-               free(prev_exp_op);
-       }
-       free(load_expression);
-}
-
-/*
- * Returns the first node of the chain, after initializing the next
- * pointers.
- */
-static
-struct filter_node *load_expression_get_forward_chain(struct filter_node *node)
-{
-       struct filter_node *prev_node;
-
-       for (;;) {
-               assert(node->type == NODE_EXPRESSION);
-               prev_node = node;
-               node = node->u.expression.prev;
-               if (!node) {
-                       break;
-               }
-               node->u.expression.next = prev_node;
-       }
-       return prev_node;
-}
-
-static
-struct ir_load_expression *create_load_expression(struct filter_node *node)
-{
-       struct ir_load_expression *load_exp;
-       struct ir_load_expression_op *load_exp_op, *prev_op;
-       char *str;
-
-       /* Get forward chain. */
-       node = load_expression_get_forward_chain(node);
-       if (!node)
-               return NULL;
-       load_exp = calloc(sizeof(struct ir_load_expression), 1);
-       if (!load_exp)
-               return NULL;
-
-       /* Root */
-       load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
-       if (!load_exp_op)
-               goto error;
-       load_exp->child = load_exp_op;
-       str = node->u.expression.u.string;
-       if (!strcmp(str, "$ctx")) {
-               load_exp_op->type = IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT;
-               node = node->u.expression.next;
-               if (!node) {
-                       fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str);
-                       goto error;
-               }
-               str = node->u.expression.u.string;
-       } else if (!strcmp(str, "$app")) {
-               load_exp_op->type = IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT;
-               node = node->u.expression.next;
-               if (!node) {
-                       fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str);
-                       goto error;
-               }
-               str = node->u.expression.u.string;
-       } else if (str[0] == '$') {
-               fprintf(stderr, "[error] Unexpected identifier \'%s\'\n", str);
-               goto error;
-       } else {
-               load_exp_op->type = IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT;
-       }
-
-       for (;;) {
-               struct filter_node *bracket_node;
-
-               prev_op = load_exp_op;
-               load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
-               if (!load_exp_op)
-                       goto error;
-               prev_op->next = load_exp_op;
-               load_exp_op->type = IR_LOAD_EXPRESSION_GET_SYMBOL;
-               load_exp_op->u.symbol = strdup(str);
-               if (!load_exp_op->u.symbol)
-                       goto error;
-
-               /* Explore brackets from current node. */
-               for (bracket_node = node->u.expression.next_bracket;
-                               bracket_node != NULL;
-                               bracket_node = bracket_node->u.expression.next_bracket) {
-                       prev_op = load_exp_op;
-                       load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
-                       if (!load_exp_op)
-                               goto error;
-                       prev_op->next = load_exp_op;
-                       load_exp_op->type = IR_LOAD_EXPRESSION_GET_INDEX;
-                       load_exp_op->u.index = bracket_node->u.expression.u.constant;
-               }
-               /* Go to next chain element. */
-               node = node->u.expression.next;
-               if (!node)
-                       break;
-               str = node->u.expression.u.string;
-       }
-       /* Add final load field */
-       prev_op = load_exp_op;
-       load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
-       if (!load_exp_op)
-               goto error;
-       prev_op->next = load_exp_op;
-       load_exp_op->type = IR_LOAD_EXPRESSION_LOAD_FIELD;
-       return load_exp;
-
-error:
-       free_load_expression(load_exp);
-       return NULL;
-}
-
-static
-struct ir_op *make_op_load_expression(struct filter_node *node,
-               enum ir_side side)
-{
-       struct ir_op *op;
-
-       op = calloc(sizeof(struct ir_op), 1);
-       if (!op)
-               return NULL;
-       op->op = IR_OP_LOAD;
-       op->data_type = IR_DATA_EXPRESSION;
-       op->signedness = IR_SIGN_DYN;
-       op->side = side;
-       op->u.load.u.expression = create_load_expression(node);
-       if (!op->u.load.u.expression) {
-               goto error;
-       }
-       return op;
-
-error:
-       free_load_expression(op->u.load.u.expression);
-       free(op);
-       return NULL;
-}
-
-static
-struct ir_op *make_op_unary(enum unary_op_type unary_op_type,
-                       const char *op_str, enum ir_op_signedness signedness,
-                       struct ir_op *child, enum ir_side side)
-{
-       struct ir_op *op = NULL;
-
-       if (child->data_type == IR_DATA_STRING) {
-               fprintf(stderr, "[error] unary operation '%s' not allowed on string literal\n", op_str);
-               goto error;
-       }
-
-       op = calloc(sizeof(struct ir_op), 1);
-       if (!op)
-               return NULL;
-       op->op = IR_OP_UNARY;
-       op->data_type = child->data_type;
-       op->signedness = signedness;
-       op->side = side;
-       op->u.unary.type = unary_op_type;
-       op->u.unary.child = child;
-       return op;
-
-error:
-       free(op);
-       return NULL;
-}
-
-/*
- * unary + is pretty much useless.
- */
-static
-struct ir_op *make_op_unary_plus(struct ir_op *child, enum ir_side side)
-{
-       return make_op_unary(AST_UNARY_PLUS, "+", child->signedness,
-                       child, side);
-}
-
-static
-struct ir_op *make_op_unary_minus(struct ir_op *child, enum ir_side side)
-{
-       return make_op_unary(AST_UNARY_MINUS, "-", child->signedness,
-                       child, side);
-}
-
-static
-struct ir_op *make_op_unary_not(struct ir_op *child, enum ir_side side)
-{
-       return make_op_unary(AST_UNARY_NOT, "!", child->signedness,
-                       child, side);
-}
-
-static
-struct ir_op *make_op_unary_bit_not(struct ir_op *child, enum ir_side side)
-{
-       return make_op_unary(AST_UNARY_BIT_NOT, "~", child->signedness,
-                       child, side);
-}
-
-static
-struct ir_op *make_op_binary_compare(enum op_type bin_op_type,
-               const char *op_str, struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       struct ir_op *op = NULL;
-
-       if (left->data_type == IR_DATA_UNKNOWN
-               || right->data_type == IR_DATA_UNKNOWN) {
-               fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str);
-               goto error;
-
-       }
-       if ((left->data_type == IR_DATA_STRING
-               && (right->data_type == IR_DATA_NUMERIC || right->data_type == IR_DATA_FLOAT))
-               || ((left->data_type == IR_DATA_NUMERIC || left->data_type == IR_DATA_FLOAT) &&
-                       right->data_type == IR_DATA_STRING)) {
-               fprintf(stderr, "[error] binary operation '%s' operand type mismatch\n", op_str);
-               goto error;
-       }
-
-       op = calloc(sizeof(struct ir_op), 1);
-       if (!op)
-               return NULL;
-       op->op = IR_OP_BINARY;
-       op->u.binary.type = bin_op_type;
-       op->u.binary.left = left;
-       op->u.binary.right = right;
-
-       /* we return a boolean, represented as signed numeric */
-       op->data_type = IR_DATA_NUMERIC;
-       op->signedness = IR_SIGNED;
-       op->side = side;
-
-       return op;
-
-error:
-       free(op);
-       return NULL;
-}
-
-static
-struct ir_op *make_op_binary_eq(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_compare(AST_OP_EQ, "==", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_ne(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_compare(AST_OP_NE, "!=", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_gt(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_compare(AST_OP_GT, ">", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_lt(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_compare(AST_OP_LT, "<", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_ge(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_compare(AST_OP_GE, ">=", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_le(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_compare(AST_OP_LE, "<=", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_logical(enum op_type bin_op_type,
-               const char *op_str, struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       struct ir_op *op = NULL;
-
-       if (left->data_type == IR_DATA_UNKNOWN
-               || right->data_type == IR_DATA_UNKNOWN) {
-               fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str);
-               goto error;
-
-       }
-       if (left->data_type == IR_DATA_STRING
-               || right->data_type == IR_DATA_STRING) {
-               fprintf(stderr, "[error] logical binary operation '%s' cannot have string operand\n", op_str);
-               goto error;
-       }
-
-       op = calloc(sizeof(struct ir_op), 1);
-       if (!op)
-               return NULL;
-       op->op = IR_OP_LOGICAL;
-       op->u.binary.type = bin_op_type;
-       op->u.binary.left = left;
-       op->u.binary.right = right;
-
-       /* we return a boolean, represented as signed numeric */
-       op->data_type = IR_DATA_NUMERIC;
-       op->signedness = IR_SIGNED;
-       op->side = side;
-
-       return op;
-
-error:
-       free(op);
-       return NULL;
-}
-
-static
-struct ir_op *make_op_binary_bitwise(enum op_type bin_op_type,
-               const char *op_str, struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       struct ir_op *op = NULL;
-
-       if (left->data_type == IR_DATA_UNKNOWN
-               || right->data_type == IR_DATA_UNKNOWN) {
-               fprintf(stderr, "[error] bitwise binary operation '%s' has unknown operand type\n", op_str);
-               goto error;
-
-       }
-       if (left->data_type == IR_DATA_STRING
-               || right->data_type == IR_DATA_STRING) {
-               fprintf(stderr, "[error] bitwise binary operation '%s' cannot have string operand\n", op_str);
-               goto error;
-       }
-       if (left->data_type == IR_DATA_FLOAT
-               || right->data_type == IR_DATA_FLOAT) {
-               fprintf(stderr, "[error] bitwise binary operation '%s' cannot have floating point operand\n", op_str);
-               goto error;
-       }
-
-       op = calloc(sizeof(struct ir_op), 1);
-       if (!op)
-               return NULL;
-       op->op = IR_OP_BINARY;
-       op->u.binary.type = bin_op_type;
-       op->u.binary.left = left;
-       op->u.binary.right = right;
-
-       /* we return a signed numeric */
-       op->data_type = IR_DATA_NUMERIC;
-       op->signedness = IR_SIGNED;
-       op->side = side;
-
-       return op;
-
-error:
-       free(op);
-       return NULL;
-}
-
-static
-struct ir_op *make_op_binary_logical_and(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_logical(AST_OP_AND, "&&", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_logical_or(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_logical(AST_OP_OR, "||", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_rshift(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_bitwise(AST_OP_BIT_RSHIFT, ">>", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_lshift(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_bitwise(AST_OP_BIT_LSHIFT, "<<", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_and(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_bitwise(AST_OP_BIT_AND, "&", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_or(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_bitwise(AST_OP_BIT_OR, "|", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_xor(struct ir_op *left, struct ir_op *right,
-               enum ir_side side)
-{
-       return make_op_binary_bitwise(AST_OP_BIT_XOR, "^", left, right, side);
-}
-
-static
-void filter_free_ir_recursive(struct ir_op *op)
-{
-       if (!op)
-               return;
-       switch (op->op) {
-       case IR_OP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] Unknown op type in %s\n",
-                       __func__);
-               break;
-       case IR_OP_ROOT:
-               filter_free_ir_recursive(op->u.root.child);
-               break;
-       case IR_OP_LOAD:
-               switch (op->data_type) {
-               case IR_DATA_STRING:
-                       free(op->u.load.u.string.value);
-                       break;
-               case IR_DATA_FIELD_REF:         /* fall-through */
-               case IR_DATA_GET_CONTEXT_REF:
-                       free(op->u.load.u.ref);
-                       break;
-               case IR_DATA_EXPRESSION:
-                       free_load_expression(op->u.load.u.expression);
-               default:
-                       break;
-               }
-               break;
-       case IR_OP_UNARY:
-               filter_free_ir_recursive(op->u.unary.child);
-               break;
-       case IR_OP_BINARY:
-               filter_free_ir_recursive(op->u.binary.left);
-               filter_free_ir_recursive(op->u.binary.right);
-               break;
-       case IR_OP_LOGICAL:
-               filter_free_ir_recursive(op->u.logical.left);
-               filter_free_ir_recursive(op->u.logical.right);
-               break;
-       }
-       free(op);
-}
-
-static
-struct ir_op *make_expression(struct filter_parser_ctx *ctx,
-               struct filter_node *node, enum ir_side side)
-{
-       switch (node->u.expression.type) {
-       case AST_EXP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown expression type\n", __func__);
-               return NULL;
-
-       case AST_EXP_STRING:
-               return make_op_load_string(node->u.expression.u.string, side);
-       case AST_EXP_CONSTANT:
-               return make_op_load_numeric(node->u.expression.u.constant,
-                                       side);
-       case AST_EXP_FLOAT_CONSTANT:
-               return make_op_load_float(node->u.expression.u.float_constant,
-                                       side);
-       case AST_EXP_IDENTIFIER:
-       case AST_EXP_GLOBAL_IDENTIFIER:
-               return make_op_load_expression(node, side);
-       case AST_EXP_NESTED:
-               return generate_ir_recursive(ctx, node->u.expression.u.child,
-                                       side);
-       }
-}
-
-static
-struct ir_op *make_op(struct filter_parser_ctx *ctx,
-               struct filter_node *node, enum ir_side side)
-{
-       struct ir_op *op = NULL, *lchild, *rchild;
-       const char *op_str = "?";
-
-       switch (node->u.op.type) {
-       case AST_OP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown binary op type\n", __func__);
-               return NULL;
-
-       /*
-        * The following binary operators other than comparators and
-        * logical and/or are not supported yet.
-        */
-       case AST_OP_MUL:
-               op_str = "*";
-               goto error_not_supported;
-       case AST_OP_DIV:
-               op_str = "/";
-               goto error_not_supported;
-       case AST_OP_MOD:
-               op_str = "%";
-               goto error_not_supported;
-       case AST_OP_PLUS:
-               op_str = "+";
-               goto error_not_supported;
-       case AST_OP_MINUS:
-               op_str = "-";
-               goto error_not_supported;
-
-       case AST_OP_BIT_RSHIFT:
-       case AST_OP_BIT_LSHIFT:
-       case AST_OP_BIT_AND:
-       case AST_OP_BIT_OR:
-       case AST_OP_BIT_XOR:
-               lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
-               if (!lchild)
-                       return NULL;
-               rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT);
-               if (!rchild) {
-                       filter_free_ir_recursive(lchild);
-                       return NULL;
-               }
-               break;
-
-       case AST_OP_EQ:
-       case AST_OP_NE:
-       case AST_OP_GT:
-       case AST_OP_LT:
-       case AST_OP_GE:
-       case AST_OP_LE:
-               lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
-               if (!lchild)
-                       return NULL;
-               rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT);
-               if (!rchild) {
-                       filter_free_ir_recursive(lchild);
-                       return NULL;
-               }
-               break;
-
-       case AST_OP_AND:
-       case AST_OP_OR:
-               /*
-                * Both children considered as left, since we need to
-                * populate R0.
-                */
-               lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
-               if (!lchild)
-                       return NULL;
-               rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_LEFT);
-               if (!rchild) {
-                       filter_free_ir_recursive(lchild);
-                       return NULL;
-               }
-               break;
-       }
-
-       switch (node->u.op.type) {
-       case AST_OP_AND:
-               op = make_op_binary_logical_and(lchild, rchild, side);
-               break;
-       case AST_OP_OR:
-               op = make_op_binary_logical_or(lchild, rchild, side);
-               break;
-       case AST_OP_EQ:
-               op = make_op_binary_eq(lchild, rchild, side);
-               break;
-       case AST_OP_NE:
-               op = make_op_binary_ne(lchild, rchild, side);
-               break;
-       case AST_OP_GT:
-               op = make_op_binary_gt(lchild, rchild, side);
-               break;
-       case AST_OP_LT:
-               op = make_op_binary_lt(lchild, rchild, side);
-               break;
-       case AST_OP_GE:
-               op = make_op_binary_ge(lchild, rchild, side);
-               break;
-       case AST_OP_LE:
-               op = make_op_binary_le(lchild, rchild, side);
-               break;
-       case AST_OP_BIT_RSHIFT:
-               op = make_op_binary_bitwise_rshift(lchild, rchild, side);
-               break;
-       case AST_OP_BIT_LSHIFT:
-               op = make_op_binary_bitwise_lshift(lchild, rchild, side);
-               break;
-       case AST_OP_BIT_AND:
-               op = make_op_binary_bitwise_and(lchild, rchild, side);
-               break;
-       case AST_OP_BIT_OR:
-               op = make_op_binary_bitwise_or(lchild, rchild, side);
-               break;
-       case AST_OP_BIT_XOR:
-               op = make_op_binary_bitwise_xor(lchild, rchild, side);
-               break;
-       default:
-               break;
-       }
-
-       if (!op) {
-               filter_free_ir_recursive(rchild);
-               filter_free_ir_recursive(lchild);
-       }
-       return op;
-
-error_not_supported:
-       fprintf(stderr, "[error] %s: binary operation '%s' not supported\n",
-               __func__, op_str);
-       return NULL;
-}
-
-static
-struct ir_op *make_unary_op(struct filter_parser_ctx *ctx,
-               struct filter_node *node, enum ir_side side)
-{
-       switch (node->u.unary_op.type) {
-       case AST_UNARY_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown unary op type\n", __func__);
-               return NULL;
-
-       case AST_UNARY_PLUS:
-       {
-               struct ir_op *op, *child;
-
-               child = generate_ir_recursive(ctx, node->u.unary_op.child,
-                                       side);
-               if (!child)
-                       return NULL;
-               op = make_op_unary_plus(child, side);
-               if (!op) {
-                       filter_free_ir_recursive(child);
-                       return NULL;
-               }
-               return op;
-       }
-       case AST_UNARY_MINUS:
-       {
-               struct ir_op *op, *child;
-
-               child = generate_ir_recursive(ctx, node->u.unary_op.child,
-                                       side);
-               if (!child)
-                       return NULL;
-               op = make_op_unary_minus(child, side);
-               if (!op) {
-                       filter_free_ir_recursive(child);
-                       return NULL;
-               }
-               return op;
-       }
-       case AST_UNARY_NOT:
-       {
-               struct ir_op *op, *child;
-
-               child = generate_ir_recursive(ctx, node->u.unary_op.child,
-                                       side);
-               if (!child)
-                       return NULL;
-               op = make_op_unary_not(child, side);
-               if (!op) {
-                       filter_free_ir_recursive(child);
-                       return NULL;
-               }
-               return op;
-       }
-       case AST_UNARY_BIT_NOT:
-       {
-               struct ir_op *op, *child;
-
-               child = generate_ir_recursive(ctx, node->u.unary_op.child,
-                                       side);
-               if (!child)
-                       return NULL;
-               op = make_op_unary_bit_not(child, side);
-               if (!op) {
-                       filter_free_ir_recursive(child);
-                       return NULL;
-               }
-               return op;
-       }
-       }
-
-       return NULL;
-}
-
-static
-struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx,
-               struct filter_node *node, enum ir_side side)
-{
-       switch (node->type) {
-       case NODE_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown node type\n", __func__);
-               return NULL;
-
-       case NODE_ROOT:
-       {
-               struct ir_op *op, *child;
-
-               child = generate_ir_recursive(ctx, node->u.root.child,
-                                       side);
-               if (!child)
-                       return NULL;
-               op = make_op_root(child, side);
-               if (!op) {
-                       filter_free_ir_recursive(child);
-                       return NULL;
-               }
-               return op;
-       }
-       case NODE_EXPRESSION:
-               return make_expression(ctx, node, side);
-       case NODE_OP:
-               return make_op(ctx, node, side);
-       case NODE_UNARY_OP:
-               return make_unary_op(ctx, node, side);
-       }
-       return 0;
-}
-
-LTTNG_HIDDEN
-void filter_ir_free(struct filter_parser_ctx *ctx)
-{
-       filter_free_ir_recursive(ctx->ir_root);
-       ctx->ir_root = NULL;
-}
-
-LTTNG_HIDDEN
-int filter_visitor_ir_generate(struct filter_parser_ctx *ctx)
-{
-       struct ir_op *op;
-
-       op = generate_ir_recursive(ctx, &ctx->ast->root, IR_LEFT);
-       if (!op) {
-               return -EINVAL;
-       }
-       ctx->ir_root = op;
-       return 0;
-}
diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-comparator.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-comparator.c
deleted file mode 100644 (file)
index bcbb69e..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * filter-visitor-ir-check-binary-comparator.c
- *
- * LTTng filter IR check binary comparator
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-static
-int check_bin_comparator(struct ir_op *node)
-{
-       switch (node->op) {
-       case IR_OP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
-               return -EINVAL;
-
-       case IR_OP_ROOT:
-               return check_bin_comparator(node->u.root.child);
-       case IR_OP_LOAD:
-               return 0;
-       case IR_OP_UNARY:
-               return check_bin_comparator(node->u.unary.child);
-       case IR_OP_BINARY:
-       {
-               int ret;
-
-               if (node->u.binary.left->data_type == IR_DATA_STRING
-                               || node->u.binary.right->data_type
-                                       == IR_DATA_STRING) {
-                       if (node->u.binary.type != AST_OP_EQ
-                                       && node->u.binary.type != AST_OP_NE) {
-                               fprintf(stderr, "[error] Only '==' and '!=' comparators are allowed for strings\n");
-                               return -EINVAL;
-                       }
-               }
-
-               ret = check_bin_comparator(node->u.binary.left);
-               if (ret)
-                       return ret;
-               return check_bin_comparator(node->u.binary.right);
-       }
-       case IR_OP_LOGICAL:
-       {
-               int ret;
-
-               ret = check_bin_comparator(node->u.logical.left);
-               if (ret)
-                       return ret;
-               return check_bin_comparator(node->u.logical.right);
-       }
-       }
-}
-
-int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx)
-{
-       return check_bin_comparator(ctx->ir_root);
-}
diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-op-nesting.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-op-nesting.c
deleted file mode 100644 (file)
index 04a5fbd..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * filter-visitor-ir-check-binary-op-nesting.c
- *
- * LTTng filter IR check binary op nesting
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-#include <common/macros.h>
-
-static
-int check_bin_op_nesting_recursive(struct ir_op *node, int nesting)
-{
-       switch (node->op) {
-       case IR_OP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
-               return -EINVAL;
-
-       case IR_OP_ROOT:
-               return check_bin_op_nesting_recursive(node->u.root.child,
-                               nesting);
-       case IR_OP_LOAD:
-               return 0;
-       case IR_OP_UNARY:
-               return check_bin_op_nesting_recursive(node->u.unary.child,
-                               nesting);
-       case IR_OP_BINARY:
-       {
-               int ret;
-
-               ret = check_bin_op_nesting_recursive(node->u.binary.left,
-                               nesting + 1);
-               if (ret)
-                       return ret;
-               return check_bin_op_nesting_recursive(node->u.binary.right,
-                               nesting + 1);
-       }
-       case IR_OP_LOGICAL:
-       {
-               int ret;
-
-               ret = check_bin_op_nesting_recursive(node->u.logical.left,
-                               nesting);
-               if (ret)
-                       return ret;
-               return check_bin_op_nesting_recursive(node->u.logical.right,
-                               nesting);
-       }
-       }
-}
-
-LTTNG_HIDDEN
-int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx)
-{
-       return check_bin_op_nesting_recursive(ctx->ir_root, 0);
-}
diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-normalize-glob-patterns.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-normalize-glob-patterns.c
deleted file mode 100644 (file)
index 5e2778e..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * filter-visitor-ir-normalize-glob-patterns.c
- *
- * LTTng filter IR normalize string
- *
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-
-#include <common/macros.h>
-#include <common/string-utils/string-utils.h>
-
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-static
-int normalize_glob_patterns(struct ir_op *node)
-{
-       switch (node->op) {
-       case IR_OP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
-               return -EINVAL;
-
-       case IR_OP_ROOT:
-               return normalize_glob_patterns(node->u.root.child);
-       case IR_OP_LOAD:
-       {
-               if (node->data_type == IR_DATA_STRING) {
-                       enum ir_load_string_type type =
-                               node->u.load.u.string.type;
-                       if (type == IR_LOAD_STRING_TYPE_GLOB_STAR_END ||
-                                       type == IR_LOAD_STRING_TYPE_GLOB_STAR) {
-                               assert(node->u.load.u.string.value);
-                               strutils_normalize_star_glob_pattern(
-                                       node->u.load.u.string.value);
-                       }
-               }
-
-               return 0;
-       }
-       case IR_OP_UNARY:
-               return normalize_glob_patterns(node->u.unary.child);
-       case IR_OP_BINARY:
-       {
-               int ret = normalize_glob_patterns(node->u.binary.left);
-
-               if (ret)
-                       return ret;
-               return normalize_glob_patterns(node->u.binary.right);
-       }
-       case IR_OP_LOGICAL:
-       {
-               int ret;
-
-               ret = normalize_glob_patterns(node->u.logical.left);
-               if (ret)
-                       return ret;
-               return normalize_glob_patterns(node->u.logical.right);
-       }
-       }
-}
-
-/*
- * This function normalizes all the globbing literal strings with
- * utils_normalize_glob_pattern(). See the documentation of
- * utils_normalize_glob_pattern() for more details.
- */
-LTTNG_HIDDEN
-int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx)
-{
-       return normalize_glob_patterns(ctx->ir_root);
-}
diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-globbing.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-globbing.c
deleted file mode 100644 (file)
index 3e749a4..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * filter-visitor-ir-validate-globbing.c
- *
- * LTTng filter IR validate globbing
- *
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-
-#include <common/macros.h>
-
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-static
-int validate_globbing(struct ir_op *node)
-{
-       int ret;
-
-       switch (node->op) {
-       case IR_OP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
-               return -EINVAL;
-
-       case IR_OP_ROOT:
-               return validate_globbing(node->u.root.child);
-       case IR_OP_LOAD:
-               return 0;
-       case IR_OP_UNARY:
-               return validate_globbing(node->u.unary.child);
-       case IR_OP_BINARY:
-       {
-               struct ir_op *left = node->u.binary.left;
-               struct ir_op *right = node->u.binary.right;
-
-               if (left->op == IR_OP_LOAD && right->op == IR_OP_LOAD &&
-                               left->data_type == IR_DATA_STRING &&
-                               right->data_type == IR_DATA_STRING) {
-                       /* Test 1. */
-                       if (left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
-                                       right->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
-                               fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
-                               return -1;
-                       }
-
-                       if (right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
-                                       left->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
-                               fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
-                               return -1;
-                       }
-               }
-
-               if ((left->op == IR_OP_LOAD && left->data_type == IR_DATA_STRING) ||
-                               (right->op == IR_OP_LOAD && right->data_type == IR_DATA_STRING)) {
-                       if ((left->op == IR_OP_LOAD && left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR) ||
-                                       (right->op == IR_OP_LOAD && right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR)) {
-                               /* Test 2. */
-                               if (node->u.binary.type != AST_OP_EQ &&
-                                               node->u.binary.type != AST_OP_NE) {
-                                       fprintf(stderr, "[error] Only the `==` and `!=` operators are allowed with a globbing pattern\n");
-                                       return -1;
-                               }
-                       }
-               }
-
-               ret = validate_globbing(left);
-               if (ret) {
-                       return ret;
-               }
-
-               return validate_globbing(right);
-       }
-       case IR_OP_LOGICAL:
-               ret = validate_globbing(node->u.logical.left);
-               if (ret)
-                       return ret;
-               return validate_globbing(node->u.logical.right);
-       }
-}
-
-/*
- * This function recursively validates that:
- *
- * 1. When there's a binary operation between two literal strings,
- *    if one of them has the IR_LOAD_STRING_TYPE_GLOB_STAR type,
- *    the other one has the IR_LOAD_STRING_TYPE_PLAIN type.
- *
- *    In other words, you cannot compare two globbing patterns, except
- *    for two globbing patterns with only a star at the end for backward
- *    compatibility reasons.
- *
- * 2. When there's a binary operation between two literal strings, if
- *    one of them is a (full) star globbing pattern, the binary
- *    operation is either == or !=.
- */
-LTTNG_HIDDEN
-int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx)
-{
-       return validate_globbing(ctx->ir_root);
-}
diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-string.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-string.c
deleted file mode 100644 (file)
index 1df83b0..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * filter-visitor-ir-validate-string.c
- *
- * LTTng filter IR validate string
- *
- * Copyright 2014 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-
-#include <common/macros.h>
-
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-enum parse_char_result {
-       PARSE_CHAR_UNKNOWN = -2,
-       PARSE_CHAR_WILDCARD = -1,
-       PARSE_CHAR_NORMAL = 0,
-};
-
-static
-enum parse_char_result parse_char(const char **p)
-{
-       switch (**p) {
-       case '\\':
-               (*p)++;
-               switch (**p) {
-               case '\\':
-               case '*':
-                       return PARSE_CHAR_NORMAL;
-               default:
-                       return PARSE_CHAR_UNKNOWN;
-               }
-       case '*':
-               return PARSE_CHAR_WILDCARD;
-       default:
-               return PARSE_CHAR_NORMAL;
-       }
-}
-
-static
-int validate_string(struct ir_op *node)
-{
-       switch (node->op) {
-       case IR_OP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown op type\n", __func__);
-               return -EINVAL;
-
-       case IR_OP_ROOT:
-               return validate_string(node->u.root.child);
-       case IR_OP_LOAD:
-       {
-               int ret = 0;
-
-               if (node->data_type == IR_DATA_STRING) {
-                       const char *str;
-
-                       assert(node->u.load.u.string.value);
-                       str = node->u.load.u.string.value;
-
-                       for (;;) {
-                               enum parse_char_result res;
-
-                               if (!(*str)) {
-                                       break;
-                               }
-
-                               res = parse_char(&str);
-                               str++;
-
-                               switch (res) {
-                               case PARSE_CHAR_UNKNOWN:
-                                       ret = -EINVAL;
-                                       fprintf(stderr,
-                                               "Unsupported escape character detected.\n");
-                                       goto end_load;
-                               case PARSE_CHAR_NORMAL:
-                               default:
-                                       break;
-                               }
-                       }
-               }
-end_load:
-               return ret;
-       }
-       case IR_OP_UNARY:
-               return validate_string(node->u.unary.child);
-       case IR_OP_BINARY:
-       {
-               int ret = validate_string(node->u.binary.left);
-
-               if (ret)
-                       return ret;
-               return validate_string(node->u.binary.right);
-       }
-       case IR_OP_LOGICAL:
-       {
-               int ret;
-
-               ret = validate_string(node->u.logical.left);
-               if (ret)
-                       return ret;
-               return validate_string(node->u.logical.right);
-       }
-       }
-}
-
-LTTNG_HIDDEN
-int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx)
-{
-       return validate_string(ctx->ir_root);
-}
diff --git a/src/lib/lttng-ctl/filter/filter-visitor-xml.c b/src/lib/lttng-ctl/filter/filter-visitor-xml.c
deleted file mode 100644 (file)
index d5ff0c1..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * filter-visitor-xml.c
- *
- * LTTng filter XML pretty printer visitor
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-
-#include <common/macros.h>
-
-#define fprintf_dbg(fd, fmt, args...)  fprintf(fd, "%s: " fmt, __func__, ## args)
-
-static
-int recursive_visit_print(struct filter_node *node, FILE *stream, int indent);
-
-static
-void print_tabs(FILE *fd, int depth)
-{
-       int i;
-
-       for (i = 0; i < depth; i++)
-               fprintf(fd, "\t");
-}
-
-static
-int recursive_visit_print_expression(struct filter_node *node,
-               FILE *stream, int indent)
-{
-       struct filter_node *iter_node;
-
-       if (!node) {
-               fprintf(stderr, "[error] %s: NULL child\n", __func__);
-               return -EINVAL;
-       }
-       switch (node->u.expression.type) {
-       case AST_EXP_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown expression\n", __func__);
-               return -EINVAL;
-       case AST_EXP_STRING:
-               print_tabs(stream, indent);
-               fprintf(stream, "<string value=\"%s\"/>\n",
-                       node->u.expression.u.string);
-               break;
-       case AST_EXP_CONSTANT:
-               print_tabs(stream, indent);
-               fprintf(stream, "<constant value=\"%" PRIu64 "\"/>\n",
-                       node->u.expression.u.constant);
-               break;
-       case AST_EXP_FLOAT_CONSTANT:
-               print_tabs(stream, indent);
-               fprintf(stream, "<float_constant value=\"%lg\"/>\n",
-                       node->u.expression.u.float_constant);
-               break;
-       case AST_EXP_IDENTIFIER:                /* fall-through */
-       case AST_EXP_GLOBAL_IDENTIFIER:
-               print_tabs(stream, indent);
-               fprintf(stream, "<%s value=\"%s\"/>\n",
-                       node->u.expression.type == AST_EXP_IDENTIFIER ?
-                               "identifier" : "global_identifier",
-                       node->u.expression.u.identifier);
-               iter_node = node->u.expression.next;
-               while (iter_node) {
-                       print_tabs(stream, indent);
-                       fprintf(stream, "<bracket>\n");
-                       if (recursive_visit_print_expression(iter_node,
-                                       stream, indent + 1)) {
-                               return -EINVAL;
-                       }
-                       print_tabs(stream, indent);
-                       fprintf(stream, "</bracket>\n");
-                       iter_node = iter_node->u.expression.next;
-
-               }
-               break;
-       case AST_EXP_NESTED:
-               return recursive_visit_print(node->u.expression.u.child,
-                               stream, indent + 1);
-       }
-       return 0;
-}
-
-
-static
-int recursive_visit_print(struct filter_node *node, FILE *stream, int indent)
-{
-       int ret;
-
-       if (!node) {
-               fprintf(stderr, "[error] %s: NULL child\n", __func__);
-               return -EINVAL;
-       }
-       switch (node->type) {
-       case NODE_UNKNOWN:
-       default:
-               fprintf(stderr, "[error] %s: unknown node type\n", __func__);
-               return -EINVAL;
-       case NODE_ROOT:
-               print_tabs(stream, indent);
-               fprintf(stream, "<root>\n");
-               ret = recursive_visit_print(node->u.root.child, stream,
-                                       indent + 1);
-               print_tabs(stream, indent);
-               fprintf(stream, "</root>\n");
-               return ret;
-       case NODE_EXPRESSION:
-               print_tabs(stream, indent);
-               fprintf(stream, "<expression>\n");
-               ret = recursive_visit_print_expression(node, stream,
-                                       indent + 1);
-               print_tabs(stream, indent);
-               fprintf(stream, "</expression>\n");
-               return ret;
-       case NODE_OP:
-               print_tabs(stream, indent);
-               fprintf(stream, "<op type=");
-               switch (node->u.op.type) {
-               case AST_OP_UNKNOWN:
-               default:
-                       fprintf(stderr, "[error] %s: unknown op\n", __func__);
-                       return -EINVAL;
-               case AST_OP_MUL:
-                       fprintf(stream, "\"*\"");
-                       break;
-               case AST_OP_DIV:
-                       fprintf(stream, "\"/\"");
-                       break;
-               case AST_OP_MOD:
-                       fprintf(stream, "\"%%\"");
-                       break;
-               case AST_OP_PLUS:
-                       fprintf(stream, "\"+\"");
-                       break;
-               case AST_OP_MINUS:
-                       fprintf(stream, "\"-\"");
-                       break;
-               case AST_OP_BIT_RSHIFT:
-                       fprintf(stream, "\">>\"");
-                       break;
-               case AST_OP_BIT_LSHIFT:
-                       fprintf(stream, "\"<<\"");
-                       break;
-               case AST_OP_AND:
-                       fprintf(stream, "\"&&\"");
-                       break;
-               case AST_OP_OR:
-                       fprintf(stream, "\"||\"");
-                       break;
-               case AST_OP_BIT_AND:
-                       fprintf(stream, "\"&\"");
-                       break;
-               case AST_OP_BIT_OR:
-                       fprintf(stream, "\"|\"");
-                       break;
-               case AST_OP_BIT_XOR:
-                       fprintf(stream, "\"^\"");
-                       break;
-
-               case AST_OP_EQ:
-                       fprintf(stream, "\"==\"");
-                       break;
-               case AST_OP_NE:
-                       fprintf(stream, "\"!=\"");
-                       break;
-               case AST_OP_GT:
-                       fprintf(stream, "\">\"");
-                       break;
-               case AST_OP_LT:
-                       fprintf(stream, "\"<\"");
-                       break;
-               case AST_OP_GE:
-                       fprintf(stream, "\">=\"");
-                       break;
-               case AST_OP_LE:
-                       fprintf(stream, "\"<=\"");
-                       break;
-               }
-               fprintf(stream, ">\n");
-               ret = recursive_visit_print(node->u.op.lchild,
-                                       stream, indent + 1);
-               if (ret)
-                       return ret;
-               ret = recursive_visit_print(node->u.op.rchild,
-                                       stream, indent + 1);
-               if (ret)
-                       return ret;
-               print_tabs(stream, indent);
-               fprintf(stream, "</op>\n");
-               return ret;
-       case NODE_UNARY_OP:
-               print_tabs(stream, indent);
-               fprintf(stream, "<unary_op type=");
-               switch (node->u.unary_op.type) {
-               case AST_UNARY_UNKNOWN:
-               default:
-                       fprintf(stderr, "[error] %s: unknown unary_op\n", __func__);
-                       return -EINVAL;
-               case AST_UNARY_PLUS:
-                       fprintf(stream, "\"+\"");
-                       break;
-               case AST_UNARY_MINUS:
-                       fprintf(stream, "\"-\"");
-                       break;
-               case AST_UNARY_NOT:
-                       fprintf(stream, "\"!\"");
-                       break;
-               case AST_UNARY_BIT_NOT:
-                       fprintf(stream, "\"~\"");
-                       break;
-               }
-               fprintf(stream, ">\n");
-               ret = recursive_visit_print(node->u.unary_op.child,
-                                       stream, indent + 1);
-               print_tabs(stream, indent);
-               fprintf(stream, "</unary_op>\n");
-               return ret;
-       }
-       return 0;
-}
-
-LTTNG_HIDDEN
-int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream,
-                       int indent)
-{
-       return recursive_visit_print(&ctx->ast->root, stream, indent);
-}
diff --git a/src/lib/lttng-ctl/filter/memstream.h b/src/lib/lttng-ctl/filter/memstream.h
deleted file mode 100644 (file)
index cd246ee..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2012 (C) Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: MIT
- *
- */
-
-#ifndef _LTTNG_CTL_MEMSTREAM_H
-#define _LTTNG_CTL_MEMSTREAM_H
-
-#ifdef LTTNG_HAVE_FMEMOPEN
-#include <stdio.h>
-
-static inline
-FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
-{
-       return fmemopen(buf, size, mode);
-}
-
-#else /* LTTNG_HAVE_FMEMOPEN */
-
-#include <stdlib.h>
-#include <stdio.h>
-
-/*
- * Fallback for systems which don't have fmemopen. Copy buffer to a
- * temporary file, and use that file as FILE * input.
- */
-static inline
-FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
-{
-       char tmpname[PATH_MAX];
-       size_t len;
-       FILE *fp;
-       int ret;
-
-       /*
-        * Support reading only.
-        */
-       if (strcmp(mode, "rb") != 0) {
-               return NULL;
-       }
-       strncpy(tmpname, "/tmp/lttng-tmp-XXXXXX", PATH_MAX);
-       ret = mkstemp(tmpname);
-       if (ret < 0) {
-               return NULL;
-       }
-       /*
-        * We need to write to the file.
-        */
-       fp = fdopen(ret, "w+");
-       if (!fp) {
-               goto error_unlink;
-       }
-       /* Copy the entire buffer to the file */
-       len = fwrite(buf, sizeof(char), size, fp);
-       if (len != size) {
-               goto error_close;
-       }
-       ret = fseek(fp, 0L, SEEK_SET);
-       if (ret < 0) {
-               PERROR("fseek");
-               goto error_close;
-       }
-       /* We keep the handle open, but can unlink the file on the VFS. */
-       ret = unlink(tmpname);
-       if (ret < 0) {
-               PERROR("unlink");
-       }
-       return fp;
-
-error_close:
-       ret = fclose(fp);
-       if (ret < 0) {
-               PERROR("close");
-       }
-error_unlink:
-       ret = unlink(tmpname);
-       if (ret < 0) {
-               PERROR("unlink");
-       }
-       return NULL;
-}
-
-#endif /* LTTNG_HAVE_FMEMOPEN */
-
-#endif /* _LTTNG_CTL_MEMSTREAM_H */
index d6a3e4f135bee8696fbada9be6840e34f1df8109..91108c166b9825227248fa1e7bb00ce59b7ea7c5 100644 (file)
@@ -62,6 +62,7 @@ const char *sessiond_thread_name[NR_HEALTH_SESSIOND_TYPES] = {
        [ HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH ] = "Session daemon application registration dispatcher",
        [ HEALTH_SESSIOND_TYPE_ROTATION ] = "Session daemon rotation manager",
        [ HEALTH_SESSIOND_TYPE_TIMER ] = "Session daemon timer manager",
+       [ HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR ] = "Session daemon trigger action executor",
 };
 
 static
index 8c14e1fdcfd327227f3c97ab7e5331f11a12d0b2..ed91148f6760aed8ed89e7ddf96b8a121fa68c83 100644 (file)
@@ -58,6 +58,23 @@ int lttng_ctl_ask_sessiond_fds_no_cmd_header(struct lttcomm_session_msg *lsm,
        return lttng_ctl_ask_sessiond_fds_varlen(lsm, fds, nb_fd, NULL,
                0, NULL, NULL, NULL);
 }
+
+/*
+ * Calls lttng_ctl_ask_sessiond_fds_varlen() with fds with no expected command
+ * header and with varlen data.
+ */
+static inline int lttng_ctl_ask_sessiond_fds_varlen_no_cmd_header(
+               struct lttcomm_session_msg *lsm,
+               const int *fds,
+               size_t nb_fd,
+               void *vardata,
+               size_t vardata_len,
+               void **user_payload_buf)
+{
+       return lttng_ctl_ask_sessiond_fds_varlen(lsm, fds, nb_fd, vardata,
+                       vardata_len, user_payload_buf, NULL, NULL);
+}
+
 /*
  * Use this if no variable length data needs to be sent.
  */
index 4e49120732da5f5753e7322c5e60d99163e5881c..fff602305ce9a13c2fb0b4dd1e350bf57af78f36 100644 (file)
 #include <lttng/destruction-handle.h>
 #include <lttng/tracker-internal.h>
 
-#include "filter/filter-ast.h"
-#include "filter/filter-parser.h"
-#include "filter/filter-bytecode.h"
-#include "filter/memstream.h"
+#include <common/filter/filter-ast.h>
+#include <common/filter/filter-parser.h>
+#include <common/filter/filter-bytecode.h>
+#include <common/filter/memstream.h>
 #include "lttng-ctl-helper.h"
 
-#ifdef DEBUG
-static const int print_xml = 1;
-#define dbg_printf(fmt, args...)       \
-       printf("[debug liblttng-ctl] " fmt, ## args)
-#else
-static const int print_xml = 0;
-#define dbg_printf(fmt, args...)                               \
-do {                                                           \
-       /* do nothing but check printf format */                \
-       if (0)                                                  \
-               printf("[debug liblttnctl] " fmt, ## args);     \
-} while (0)
-#endif
-
 #define COPY_DOMAIN_PACKED(dst, src)                           \
 do {                                                           \
        struct lttng_domain _tmp_domain;                        \
@@ -896,7 +882,7 @@ static char *set_agent_filter(const char *filter, struct lttng_event *ev)
 
        /* Add loglevel filtering if any for the JUL domain. */
        if (ev->loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) {
-               char *op;
+               const char *op;
 
                if (ev->loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE) {
                        op = ">=";
@@ -930,133 +916,6 @@ error:
        return NULL;
 }
 
-/*
- * Generate the filter bytecode from a given filter expression string. Put the
- * newly allocated parser context in ctxp and populate the lsm object with the
- * expression len.
- *
- * Return 0 on success else a LTTNG_ERR_* code and ctxp is untouched.
- */
-static int generate_filter(char *filter_expression,
-               struct lttcomm_session_msg *lsm, struct filter_parser_ctx **ctxp)
-{
-       int ret;
-       struct filter_parser_ctx *ctx = NULL;
-       FILE *fmem = NULL;
-
-       assert(filter_expression);
-       assert(lsm);
-       assert(ctxp);
-
-       /*
-        * Casting const to non-const, as the underlying function will use it in
-        * read-only mode.
-        */
-       fmem = lttng_fmemopen((void *) filter_expression,
-                       strlen(filter_expression), "r");
-       if (!fmem) {
-               fprintf(stderr, "Error opening memory as stream\n");
-               ret = -LTTNG_ERR_FILTER_NOMEM;
-               goto error;
-       }
-       ctx = filter_parser_ctx_alloc(fmem);
-       if (!ctx) {
-               fprintf(stderr, "Error allocating parser\n");
-               ret = -LTTNG_ERR_FILTER_NOMEM;
-               goto filter_alloc_error;
-       }
-       ret = filter_parser_ctx_append_ast(ctx);
-       if (ret) {
-               fprintf(stderr, "Parse error\n");
-               ret = -LTTNG_ERR_FILTER_INVAL;
-               goto parse_error;
-       }
-       if (print_xml) {
-               ret = filter_visitor_print_xml(ctx, stdout, 0);
-               if (ret) {
-                       fflush(stdout);
-                       fprintf(stderr, "XML print error\n");
-                       ret = -LTTNG_ERR_FILTER_INVAL;
-                       goto parse_error;
-               }
-       }
-
-       dbg_printf("Generating IR... ");
-       fflush(stdout);
-       ret = filter_visitor_ir_generate(ctx);
-       if (ret) {
-               fprintf(stderr, "Generate IR error\n");
-               ret = -LTTNG_ERR_FILTER_INVAL;
-               goto parse_error;
-       }
-       dbg_printf("done\n");
-
-       dbg_printf("Validating IR... ");
-       fflush(stdout);
-       ret = filter_visitor_ir_check_binary_op_nesting(ctx);
-       if (ret) {
-               ret = -LTTNG_ERR_FILTER_INVAL;
-               goto parse_error;
-       }
-
-       /* Normalize globbing patterns in the expression. */
-       ret = filter_visitor_ir_normalize_glob_patterns(ctx);
-       if (ret) {
-               ret = -LTTNG_ERR_FILTER_INVAL;
-               goto parse_error;
-       }
-
-       /* Validate strings used as literals in the expression. */
-       ret = filter_visitor_ir_validate_string(ctx);
-       if (ret) {
-               ret = -LTTNG_ERR_FILTER_INVAL;
-               goto parse_error;
-       }
-
-       /* Validate globbing patterns in the expression. */
-       ret = filter_visitor_ir_validate_globbing(ctx);
-       if (ret) {
-               ret = -LTTNG_ERR_FILTER_INVAL;
-               goto parse_error;
-       }
-
-       dbg_printf("done\n");
-
-       dbg_printf("Generating bytecode... ");
-       fflush(stdout);
-       ret = filter_visitor_bytecode_generate(ctx);
-       if (ret) {
-               fprintf(stderr, "Generate bytecode error\n");
-               ret = -LTTNG_ERR_FILTER_INVAL;
-               goto parse_error;
-       }
-       dbg_printf("done\n");
-       dbg_printf("Size of bytecode generated: %u bytes.\n",
-                       bytecode_get_len(&ctx->bytecode->b));
-
-       lsm->u.enable.bytecode_len = sizeof(ctx->bytecode->b)
-               + bytecode_get_len(&ctx->bytecode->b);
-       lsm->u.enable.expression_len = strlen(filter_expression) + 1;
-
-       /* No need to keep the memory stream. */
-       if (fclose(fmem) != 0) {
-               PERROR("fclose");
-       }
-
-       *ctxp = ctx;
-       return 0;
-
-parse_error:
-       filter_ir_free(ctx);
-       filter_parser_ctx_free(ctx);
-filter_alloc_error:
-       if (fclose(fmem) != 0) {
-               PERROR("fclose");
-       }
-error:
-       return ret;
-}
-
 /*
  * Enable event(s) for a channel, possibly with exclusions and a filter.
  * If no event name is specified, all events are enabled.
@@ -1160,10 +1019,14 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle,
                        }
                }
 
-               ret = generate_filter(filter_expression, &lsm, &ctx);
+               ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx);
                if (ret) {
                        goto filter_error;
                }
+
+               lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b)
+                       + bytecode_get_len(&ctx->bytecode->b);
+               lsm.u.enable.expression_len = strlen(filter_expression) + 1;
        }
 
        ret = lttng_dynamic_buffer_set_capacity(&send_buffer,
@@ -1369,10 +1232,14 @@ int lttng_disable_event_ext(struct lttng_handle *handle,
                        }
                }
 
-               ret = generate_filter(filter_expression, &lsm, &ctx);
+               ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx);
                if (ret) {
                        goto filter_error;
                }
+
+               lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b)
+                       + bytecode_get_len(&ctx->bytecode->b);
+               lsm.u.enable.expression_len = strlen(filter_expression) + 1;
        }
 
        varlen_data = zmalloc(lsm.u.disable.bytecode_len
@@ -3054,8 +2921,15 @@ end:
 int lttng_register_trigger(struct lttng_trigger *trigger)
 {
        int ret;
+       int reply_ret;
        struct lttcomm_session_msg lsm;
        struct lttng_dynamic_buffer buffer;
+       void *reply = NULL;
+       struct lttng_buffer_view reply_view;
+       struct lttng_trigger *reply_trigger = NULL;
+       bool send_fd = false;
+       int fd_to_send;
+       enum lttng_domain_type domain_type;
 
        lttng_dynamic_buffer_init(&buffer);
        if (!trigger) {
@@ -3068,23 +2942,91 @@ int lttng_register_trigger(struct lttng_trigger *trigger)
                goto end;
        }
 
-       ret = lttng_trigger_serialize(trigger, &buffer);
+       domain_type = lttng_trigger_get_underlying_domain_type_restriction(
+                       trigger);
+
+       ret = lttng_trigger_serialize(trigger, &buffer, &fd_to_send);
        if (ret < 0) {
                ret = -LTTNG_ERR_UNK;
                goto end;
        }
 
+       if (getenv("LTTNG_REGISTER_TRIGGER_DRY_RUN")) {
+               /*
+                * Don't really send the request, just deserialize, validate
+                * that it is equal to the original trigger (to test
+                * serialization and deserialization), and return.
+                */
+               struct lttng_buffer_view bv;
+               ssize_t sz;
+
+               bv = lttng_buffer_view_from_dynamic_buffer(&buffer, 0, -1);
+               sz = lttng_trigger_create_from_buffer(&bv, &reply_trigger);
+               if (sz != bv.size) {
+                       ret = -LTTNG_ERR_UNK;
+                       goto end;
+               }
+
+               if (!reply_trigger) {
+                       ret = -LTTNG_ERR_UNK;
+                       goto end;
+               }
+
+               if (!lttng_trigger_is_equal(trigger, reply_trigger)) {
+                       ret = -LTTNG_ERR_UNK;
+                       goto end;
+               }
+
+               /* Give it a dummy name. */
+               lttng_trigger_set_name(trigger, "yop");
+
+               ret = 0;
+               goto end;
+       }
+
+       send_fd = fd_to_send >= 0;
+
        memset(&lsm, 0, sizeof(lsm));
        lsm.cmd_type = LTTNG_REGISTER_TRIGGER;
+       lsm.domain.type = domain_type;
        lsm.u.trigger.length = (uint32_t) buffer.size;
-       ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, buffer.data,
-                       buffer.size, NULL);
+       reply_ret = lttng_ctl_ask_sessiond_fds_varlen_no_cmd_header(&lsm,
+                       send_fd ? &fd_to_send : NULL,
+                       send_fd ? 1 : 0,
+                       buffer.data,
+                       buffer.size,
+                       &reply);
+       if (reply_ret < 0) {
+               ret = reply_ret;
+               goto end;
+       } else if (reply_ret == 0) {
+               /* Socket unexpectedly closed by the session daemon. */
+               ret = -LTTNG_ERR_FATAL;
+               goto end;
+       }
+
+       reply_view = lttng_buffer_view_init(reply, 0, reply_ret);
+       ret = lttng_trigger_create_from_buffer(&reply_view, &reply_trigger);
+       if (ret < 0) {
+               ret = -LTTNG_ERR_FATAL;
+               goto end;
+       }
+
+       ret = lttng_trigger_assign(trigger, reply_trigger);
+       if (ret < 0) {
+               ret = -LTTNG_ERR_FATAL;
+               goto end;
+       }
+
+       ret = 0;
 end:
+       free(reply);
        lttng_dynamic_buffer_reset(&buffer);
+       lttng_trigger_destroy(reply_trigger);
        return ret;
 }
 
-int lttng_unregister_trigger(struct lttng_trigger *trigger)
+int lttng_unregister_trigger(const struct lttng_trigger *trigger)
 {
        int ret;
        struct lttcomm_session_msg lsm;
@@ -3101,7 +3043,7 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger)
                goto end;
        }
 
-       ret = lttng_trigger_serialize(trigger, &buffer);
+       ret = lttng_trigger_serialize(trigger, &buffer, NULL);
        if (ret < 0) {
                ret = -LTTNG_ERR_UNK;
                goto end;
@@ -3117,6 +3059,49 @@ end:
        return ret;
 }
 
+/*
+ * Ask the session daemon for all registered triggers.
+ * Allocate a lttng_triggers collection.
+ * On error, returns a negative value.
+ */
+int lttng_list_triggers(struct lttng_triggers **triggers)
+{
+       int ret;
+       int reply_ret;
+       struct lttcomm_session_msg lsm;
+       struct lttng_buffer_view reply_view;
+       struct lttng_triggers *local_triggers = NULL;
+       void *reply = NULL;
+
+       memset(&lsm, 0, sizeof(lsm));
+       lsm.cmd_type = LTTNG_LIST_TRIGGERS;
+
+       reply_ret = lttng_ctl_ask_sessiond(&lsm, &reply);
+       if (reply_ret < 0) {
+               ret = reply_ret;
+               goto end;
+       } else if (reply_ret == 0) {
+               /* Socket unexpectedly closed by the session daemon. */
+               ret = -LTTNG_ERR_FATAL;
+               goto end;
+       }
+
+       reply_view = lttng_buffer_view_init(reply, 0, reply_ret);
+       ret = lttng_triggers_create_from_buffer(&reply_view, &local_triggers);
+       if (ret < 0) {
+               ret = -LTTNG_ERR_FATAL;
+               goto end;
+       }
+
+       *triggers = local_triggers;
+       local_triggers = NULL;
+       ret = 0;
+end:
+       free(reply);
+       free(local_triggers);
+       return ret;
+}
+
 static int lttng_track_untrack_id(struct lttng_handle *handle,
                enum lttng_tracker_type tracker_type,
                const struct lttng_tracker_id *id,
index 0aebf157539ae27631dc8513b7ac0b434362b2f6..8d3ec201363f595b2520294b9608b16cc3336b2d 100644 (file)
@@ -238,29 +238,29 @@ void lttng_snapshot_output_destroy(struct lttng_snapshot_output *obj)
  * Getter family functions of snapshot output.
  */
 
-uint32_t lttng_snapshot_output_get_id(struct lttng_snapshot_output *output)
+uint32_t lttng_snapshot_output_get_id(const struct lttng_snapshot_output *output)
 {
        return output->id;
 }
 
 const char *lttng_snapshot_output_get_name(
-               struct lttng_snapshot_output *output)
+               const struct lttng_snapshot_output *output)
 {
        return output->name;
 }
 
-const char *lttng_snapshot_output_get_data_url(struct lttng_snapshot_output *output)
+const char *lttng_snapshot_output_get_data_url(const struct lttng_snapshot_output *output)
 {
        return output->data_url;
 }
 
-const char *lttng_snapshot_output_get_ctrl_url(struct lttng_snapshot_output *output)
+const char *lttng_snapshot_output_get_ctrl_url(const struct lttng_snapshot_output *output)
 {
        return output->ctrl_url;
 }
 
 uint64_t lttng_snapshot_output_get_maxsize(
-               struct lttng_snapshot_output *output)
+               const struct lttng_snapshot_output *output)
 {
        return output->max_size;
 }
@@ -323,3 +323,126 @@ int lttng_snapshot_output_set_data_url(const char *url,
        lttng_ctl_copy_string(output->data_url, url, sizeof(output->data_url));
        return 0;
 }
+
+int lttng_snapshot_output_set_local_path(const char *path,
+               struct lttng_snapshot_output *output)
+{
+       int ret;
+       struct lttng_uri *uris = NULL;
+       ssize_t num_uris;
+
+       if (!path || !output) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       num_uris = uri_parse_str_urls(path, NULL, &uris);
+       if (num_uris != 1) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (uris[0].dtype != LTTNG_DST_PATH) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = lttng_strncpy(output->ctrl_url, path, sizeof(output->ctrl_url));
+       if (ret != 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+end:
+       free(uris);
+       return ret;
+}
+
+int lttng_snapshot_output_set_network_url(const char *url,
+               struct lttng_snapshot_output *output)
+{
+       int ret;
+       struct lttng_uri *uris = NULL;
+       ssize_t num_uris;
+
+       if (!url || !output) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       num_uris = uri_parse_str_urls(url, NULL, &uris);
+       if (num_uris != 2) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (uris[0].dtype != LTTNG_DST_IPV4 &&
+                       uris[0].dtype != LTTNG_DST_IPV6) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (uris[1].dtype != LTTNG_DST_IPV4 &&
+                       uris[1].dtype != LTTNG_DST_IPV6) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = lttng_strncpy(output->ctrl_url, url, sizeof(output->ctrl_url));
+       if (ret != 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+end:
+       free(uris);
+       return ret;
+}
+
+int lttng_snapshot_output_set_network_urls(
+               const char *ctrl_url, const char *data_url,
+               struct lttng_snapshot_output *output)
+{
+       int ret;
+       struct lttng_uri *uris = NULL;
+       ssize_t num_uris;
+
+       if (!ctrl_url || !data_url || !output) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       num_uris = uri_parse_str_urls(ctrl_url, data_url, &uris);
+       if (num_uris != 2) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (uris[0].dtype != LTTNG_DST_IPV4 &&
+                       uris[0].dtype != LTTNG_DST_IPV6) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (uris[1].dtype != LTTNG_DST_IPV4 &&
+                       uris[1].dtype != LTTNG_DST_IPV6) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = lttng_strncpy(output->ctrl_url, ctrl_url, sizeof(output->ctrl_url));
+       if (ret != 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = lttng_strncpy(output->data_url, data_url, sizeof(output->data_url));
+       if (ret != 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+end:
+       free(uris);
+       return ret;
+}
index cbac90da792f43a17ad1e8d5ed6fbb9792004ebb..6020964f1a381c6e4bc195242141bb516965b16f 100644 (file)
@@ -27,8 +27,14 @@ TESTS = tools/filtering/test_invalid_filter \
        tools/crash/test_crash \
        tools/regen-metadata/test_ust \
        tools/regen-statedump/test_ust \
-       tools/notification/test_notification_ust \
-       tools/notification/test_notification_kernel \
+       tools/notification/test_notification_ust_error \
+       tools/notification/test_notification_ust_buffer_usage \
+       tools/notification/test_notification_ust_event_rule_condition_exclusion \
+       tools/notification/test_notification_kernel_error \
+       tools/notification/test_notification_kernel_buffer_usage \
+       tools/notification/test_notification_kernel_instrumentation \
+       tools/notification/test_notification_kernel_syscall \
+       tools/notification/test_notification_kernel_userspace_probe \
        tools/notification/test_notification_multi_app \
        tools/rotation/test_ust \
        tools/rotation/test_kernel \
@@ -39,7 +45,10 @@ TESTS = tools/filtering/test_invalid_filter \
        tools/notification/test_notification_multi_app \
        tools/clear/test_ust \
        tools/clear/test_kernel \
-       tools/tracker/test_event_tracker
+       tools/tracker/test_event_tracker \
+       tools/trigger/test_add_trigger_cli \
+       tools/trigger/test_list_triggers_cli \
+       tools/trigger/test_remove_trigger_cli
 
 if HAVE_LIBLTTNG_UST_CTL
 SUBDIRS += ust
index 33b8cb5d5d100d29ac18777ba68ae996e1ced057..42d554cb28468d9023e5368f0a6cb5a1bd2e093b 100644 (file)
@@ -46,7 +46,7 @@ struct ppoll_thread_data {
        int value;
 };
 
-void test_select_big(void)
+static void test_select_big(void)
 {
        fd_set rfds, wfds, exfds;
        struct timeval tv;
@@ -95,7 +95,7 @@ end:
        return;
 }
 
-void test_pselect(void)
+static void test_pselect(void)
 {
        fd_set rfds;
        struct timespec tv;
@@ -128,7 +128,7 @@ void test_pselect(void)
 
 }
 
-void test_select(void)
+static void test_select(void)
 {
        fd_set rfds;
        struct timeval tv;
@@ -161,7 +161,7 @@ void test_select(void)
 
 }
 
-void test_poll(void)
+static void test_poll(void)
 {
        struct pollfd ufds[NB_FD];
        char buf[BUF_SIZE];
@@ -185,7 +185,7 @@ void test_poll(void)
        }
 }
 
-void test_ppoll(void)
+static void test_ppoll(void)
 {
        struct pollfd ufds[NB_FD];
        char buf[BUF_SIZE];
@@ -217,7 +217,7 @@ void test_ppoll(void)
        }
 }
 
-void test_ppoll_big(void)
+static void test_ppoll_big(void)
 {
        struct pollfd ufds[MAX_FDS];
        char buf[BUF_SIZE];
@@ -256,7 +256,7 @@ void test_ppoll_big(void)
        return;
 }
 
-void test_epoll(void)
+static void test_epoll(void)
 {
        int ret, epollfd;
        char buf[BUF_SIZE];
@@ -298,7 +298,7 @@ end:
        return;
 }
 
-void test_pepoll(void)
+static void test_pepoll(void)
 {
        int ret, epollfd;
        char buf[BUF_SIZE];
@@ -340,7 +340,7 @@ end:
        return;
 }
 
-void run_working_cases(void)
+static void run_working_cases(void)
 {
        int ret;
        int pipe_fds[2];
@@ -386,7 +386,7 @@ end:
  * segfault (eventually with a "*** stack smashing detected ***" message).
  * The event should contain an array of 100 FDs filled with garbage.
  */
-void ppoll_fds_buffer_overflow(void)
+static void ppoll_fds_buffer_overflow(void)
 {
        struct pollfd ufds[NB_FD];
        char buf[BUF_SIZE];
@@ -417,7 +417,7 @@ void ppoll_fds_buffer_overflow(void)
  * cleanly fail with a "Invalid argument".
  * The event should contain an empty array of FDs and overflow = 1.
  */
-void ppoll_fds_ulong_max(void)
+static void ppoll_fds_ulong_max(void)
 {
        struct pollfd ufds[NB_FD];
        char buf[BUF_SIZE];
@@ -447,7 +447,7 @@ void ppoll_fds_ulong_max(void)
  * Pass an invalid file descriptor to pselect6(). The syscall should return
  * -EBADF. The recorded event should contain a "ret = -EBADF (-9)".
  */
-void pselect_invalid_fd(void)
+static void pselect_invalid_fd(void)
 {
        fd_set rfds;
        int ret;
@@ -493,7 +493,7 @@ error:
  * Invalid pointer as writefds, should output a ppoll event
  * with 0 FDs.
  */
-void pselect_invalid_pointer(void)
+static void pselect_invalid_pointer(void)
 {
        fd_set rfds;
        int ret;
@@ -524,7 +524,7 @@ void pselect_invalid_pointer(void)
  * Pass an invalid pointer to epoll_pwait, should fail with
  * "Bad address", the event returns 0 FDs.
  */
-void epoll_pwait_invalid_pointer(void)
+static void epoll_pwait_invalid_pointer(void)
 {
        int ret, epollfd;
        char buf[BUF_SIZE];
@@ -568,7 +568,7 @@ end:
  * Set maxevents to INT_MAX, should output "Invalid argument"
  * The event should return an empty array.
  */
-void epoll_pwait_int_max(void)
+static void epoll_pwait_int_max(void)
 {
        int ret, epollfd;
        char buf[BUF_SIZE];
@@ -607,7 +607,7 @@ end:
        return;
 }
 
-void *ppoll_writer(void *arg)
+static void *ppoll_writer(void *arg)
 {
        struct ppoll_thread_data *data = (struct ppoll_thread_data *) arg;
 
@@ -620,7 +620,7 @@ void *ppoll_writer(void *arg)
        return NULL;
 }
 
-void do_ppoll(int *fds, struct pollfd *ufds)
+static void do_ppoll(int *fds, struct pollfd *ufds)
 {
        int i, ret;
        struct timespec ts;
@@ -649,7 +649,7 @@ void do_ppoll(int *fds, struct pollfd *ufds)
        }
 }
 
-void stress_ppoll(int *fds, int value)
+static void stress_ppoll(int *fds, int value)
 {
        pthread_t writer;
        int iter, ret;
@@ -690,7 +690,7 @@ end:
  *
  * ppoll should work as expected and the trace should be readable at the end.
  */
-void ppoll_concurrent_write(void)
+static void ppoll_concurrent_write(void)
 {
        int i, ret, fds[MAX_FDS];
 
@@ -715,7 +715,7 @@ void ppoll_concurrent_write(void)
        return;
 }
 
-void *epoll_pwait_writer(void *addr)
+static void *epoll_pwait_writer(void *addr)
 {
        srand(time(NULL));
 
@@ -732,7 +732,7 @@ void *epoll_pwait_writer(void *addr)
  * buffer allocated for the returned data. This should randomly segfault.
  * The trace should be readable and no kernel OOPS should occur.
  */
-void epoll_pwait_concurrent_munmap(void)
+static void epoll_pwait_concurrent_munmap(void)
 {
        int ret, epollfd, i, fds[MAX_FDS];
        char buf[BUF_SIZE];
@@ -814,16 +814,7 @@ end:
        return;
 }
 
-void usage(poptContext optCon, int exitcode, char *error, char *addl)
-{
-       poptPrintUsage(optCon, stderr, 0);
-       if (error) {
-               fprintf(stderr, "%s: %s\n", error, addl);
-       }
-       exit(exitcode);
-}
-
-void print_list(void)
+static void print_list(void)
 {
        fprintf(stderr, "Test list (-t X):\n");
        fprintf(stderr, "\t1: Working cases for select, pselect6, poll, ppoll "
index 22691e5e2d1b0053794872502e866d0462868970..d561a647959e6a12d3769379fbdb84a33a523aaa 100644 (file)
@@ -2,4 +2,4 @@
 
 SUBDIRS = streaming filtering health tracefile-limits snapshots live exclusion save-load mi \
                wildcard crash regen-metadata regen-statedump notification rotation \
-               base-path metadata working-directory relayd-grouping clear tracker
+               base-path metadata working-directory relayd-grouping clear tracker trigger
index 6cd808c6d7ac13101c9ac87a5aa26614d0f7dea9..ed653b72f9a6c94705c8b618b7f33f72a06af36b 100755 (executable)
@@ -16,7 +16,7 @@ TESTAPP_NAME="gen-ust-nevents"
 TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
 NR_ITER=100
 NR_USEC_WAIT=1
-NUM_TESTS=149
+NUM_TESTS=178
 
 source $TESTDIR/utils/utils.sh
 
@@ -25,12 +25,45 @@ function enable_ust_lttng_all_event_exclusion()
        sess_name="$1"
        exclusion="$2"
 
-       $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event -a -s $sess_name -u -x "$exclusion"
+       $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event -u "tp:*" -s $sess_name -x "$exclusion" > /dev/null
 }
 
 function run_apps
 {
-        $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT >/dev/null 2>&1
+       $TESTAPP_BIN --iter $NR_ITER --wait $NR_USEC_WAIT >/dev/null 2>&1
+       ok $? "Running test application"
+}
+
+# Testing for the absence of an event when testing exclusion is tricky. An
+# event could be absent because our exclusion mechanism works but also because
+# the event was not generate in the first place. This function test the ability
+# of our test suite to generate events.
+function dry_run
+{
+       trace_path=$(mktemp -d)
+
+       # Create session
+       create_lttng_session_ok $SESSION_NAME $trace_path
+
+       $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event -u "tp:*" -s $SESSION_NAME > /dev/null
+       ok $? "Enabling events without exclusion"
+
+       # Trace apps
+       start_lttng_tracing_ok $SESSION_NAME
+       run_apps
+       stop_lttng_tracing_ok $SESSION_NAME
+
+       nb_events=$(babeltrace $trace_path | wc -l)
+       if [ "$nb_events" -ne "0" ]; then
+               ok 0 "Events were found during the dry run without exclusion"
+       else
+               fail "No events were found during the dry run without exclusion"
+       fi
+
+       rm -rf $trace_path
+
+       # Destroy session
+       destroy_lttng_session_ok $SESSION_NAME
 }
 
 function test_exclusion
@@ -53,7 +86,7 @@ function test_exclusion
        # Destroy session
        destroy_lttng_session_ok $SESSION_NAME
 
-       stats=`babeltrace $trace_path | $STATS_BIN --tracepoint "$event_name_expected_to_be_missing" | grep -v index`
+       stats=`babeltrace $trace_path | $STATS_BIN --tracepoint "$event_name_expected_to_be_missing" | grep -v index 2> /dev/null`
        if [ ! -z "$stats" ]; then
                fail "Excluded event \"$event_name_expected_to_be_missing\" was found in trace!"
        else
@@ -83,10 +116,14 @@ function test_exclusion_fail
 
 plan_tests $NUM_TESTS
 
-print_test_banner $TEST_DESC
+print_test_banner "$TEST_DESC"
 
 start_lttng_sessiond
 
+diag "Enable event without exclusion"
+dry_run
+
+diag "Enable event with exclusion"
 test_exclusion 'tp:tptest2' 'tp:tptest2'
 test_exclusion 'tp:tptest3' 'tp:tptest3'
 test_exclusion 'tp:tptest*' 'tp:tptest1'
@@ -110,12 +147,12 @@ test_exclusion '*3' 'tp:tptest3'
 test_exclusion 'tp*test3,*2' 'tp:tptest2'
 test_exclusion '**tp*test3,*2' 'tp:tptest3'
 
-# Cannot use exclusions with non-globbing event name
+diag "Cannot use exclusions with non-globbing event name"
 test_exclusion_fail "allo" "lol"
 test_exclusion_fail "allo" "meow,lol"
 test_exclusion_fail "allo" "z*em"
 
-# Exclusion name excludes all possible event names
+diag "Exclusion name excludes all possible event names"
 test_exclusion_fail "allo*" "all*"
 test_exclusion_fail "allo*" "ze,all*,yes"
 
index e9324fa2e9e6d0978e434f5f83a5f1cacfc9a408..6d04c9da7e83af2fd1030333e092716f663bb6a8 100644 (file)
@@ -30,6 +30,7 @@ int check_env_var(const char *env)
 
 /* Session daemon */
 
+int __testpoint_sessiond_thread_manage_clients(void);
 int __testpoint_sessiond_thread_manage_clients(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CLIENTS_EXIT";
@@ -42,6 +43,7 @@ int __testpoint_sessiond_thread_manage_clients(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_registration_apps(void);
 int __testpoint_sessiond_thread_registration_apps(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_REG_APPS_EXIT";
@@ -53,6 +55,7 @@ int __testpoint_sessiond_thread_registration_apps(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_manage_apps(void);
 int __testpoint_sessiond_thread_manage_apps(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_APPS_EXIT";
@@ -65,6 +68,7 @@ int __testpoint_sessiond_thread_manage_apps(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_manage_kernel(void);
 int __testpoint_sessiond_thread_manage_kernel(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_KERNEL_EXIT";
@@ -76,6 +80,7 @@ int __testpoint_sessiond_thread_manage_kernel(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_manage_consumer(void);
 int __testpoint_sessiond_thread_manage_consumer(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CONSUMER_EXIT";
@@ -87,6 +92,7 @@ int __testpoint_sessiond_thread_manage_consumer(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_ht_cleanup(void);
 int __testpoint_sessiond_thread_ht_cleanup(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_HT_CLEANUP_EXIT";
@@ -99,6 +105,7 @@ int __testpoint_sessiond_thread_ht_cleanup(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_app_manage_notify(void);
 int __testpoint_sessiond_thread_app_manage_notify(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_APP_MANAGE_NOTIFY_EXIT";
@@ -111,6 +118,7 @@ int __testpoint_sessiond_thread_app_manage_notify(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_app_reg_dispatch(void);
 int __testpoint_sessiond_thread_app_reg_dispatch(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_APP_REG_DISPATCH_EXIT";
@@ -124,6 +132,7 @@ int __testpoint_sessiond_thread_app_reg_dispatch(void)
 
 /* Consumer daemon */
 
+int __testpoint_consumerd_thread_channel(void);
 int __testpoint_consumerd_thread_channel(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_CHANNEL_EXIT";
@@ -136,6 +145,7 @@ int __testpoint_consumerd_thread_channel(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_metadata(void);
 int __testpoint_consumerd_thread_metadata(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_EXIT";
@@ -148,6 +158,7 @@ int __testpoint_consumerd_thread_metadata(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_data(void);
 int __testpoint_consumerd_thread_data(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_DATA_EXIT";
@@ -160,6 +171,7 @@ int __testpoint_consumerd_thread_data(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_sessiond(void);
 int __testpoint_consumerd_thread_sessiond(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_SESSIOND_EXIT";
@@ -172,6 +184,7 @@ int __testpoint_consumerd_thread_sessiond(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_metadata_timer(void);
 int __testpoint_consumerd_thread_metadata_timer(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_TIMER_EXIT";
@@ -185,6 +198,7 @@ int __testpoint_consumerd_thread_metadata_timer(void)
 
 /* Relay daemon */
 
+int __testpoint_relayd_thread_dispatcher(void);
 int __testpoint_relayd_thread_dispatcher(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_DISPATCHER_EXIT";
@@ -196,6 +210,7 @@ int __testpoint_relayd_thread_dispatcher(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_worker(void);
 int __testpoint_relayd_thread_worker(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_WORKER_EXIT";
@@ -208,6 +223,7 @@ int __testpoint_relayd_thread_worker(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_listener(void);
 int __testpoint_relayd_thread_listener(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LISTENER_EXIT";
@@ -219,6 +235,7 @@ int __testpoint_relayd_thread_listener(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_live_dispatcher(void);
 int __testpoint_relayd_thread_live_dispatcher(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LIVE_DISPATCHER_EXIT";
@@ -230,6 +247,7 @@ int __testpoint_relayd_thread_live_dispatcher(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_live_worker(void);
 int __testpoint_relayd_thread_live_worker(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LIVE_WORKER_EXIT";
@@ -242,6 +260,7 @@ int __testpoint_relayd_thread_live_worker(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_live_listener(void);
 int __testpoint_relayd_thread_live_listener(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LIVE_LISTENER_EXIT";
index b3ebcedeaa222bc79e91dfd68f6d7325b1b3aec4..ca2bc34856743ce3c85ace22a4ce6c359b7571bf 100644 (file)
@@ -30,6 +30,7 @@ int check_env_var(const char *env)
 
 /* Session daemon */
 
+int __testpoint_sessiond_thread_manage_clients(void);
 int __testpoint_sessiond_thread_manage_clients(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CLIENTS_TP_FAIL";
@@ -41,6 +42,7 @@ int __testpoint_sessiond_thread_manage_clients(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_registration_apps(void);
 int __testpoint_sessiond_thread_registration_apps(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_REG_APPS_TP_FAIL";
@@ -52,6 +54,7 @@ int __testpoint_sessiond_thread_registration_apps(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_manage_apps(void);
 int __testpoint_sessiond_thread_manage_apps(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_APPS_TP_FAIL";
@@ -63,6 +66,7 @@ int __testpoint_sessiond_thread_manage_apps(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_manage_kernel(void);
 int __testpoint_sessiond_thread_manage_kernel(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_KERNEL_TP_FAIL";
@@ -74,6 +78,7 @@ int __testpoint_sessiond_thread_manage_kernel(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_manage_consumer(void);
 int __testpoint_sessiond_thread_manage_consumer(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CONSUMER_TP_FAIL";
@@ -85,6 +90,7 @@ int __testpoint_sessiond_thread_manage_consumer(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_ht_cleanup(void);
 int __testpoint_sessiond_thread_ht_cleanup(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_HT_CLEANUP_TP_FAIL";
@@ -96,6 +102,7 @@ int __testpoint_sessiond_thread_ht_cleanup(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_app_manage_notify(void);
 int __testpoint_sessiond_thread_app_manage_notify(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_APP_MANAGE_NOTIFY_TP_FAIL";
@@ -107,6 +114,7 @@ int __testpoint_sessiond_thread_app_manage_notify(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_app_reg_dispatch(void);
 int __testpoint_sessiond_thread_app_reg_dispatch(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_APP_REG_DISPATCH_TP_FAIL";
@@ -120,6 +128,7 @@ int __testpoint_sessiond_thread_app_reg_dispatch(void)
 
 /* Consumer daemon */
 
+int __testpoint_consumerd_thread_channel(void);
 int __testpoint_consumerd_thread_channel(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_CHANNEL_TP_FAIL";
@@ -131,6 +140,7 @@ int __testpoint_consumerd_thread_channel(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_metadata(void);
 int __testpoint_consumerd_thread_metadata(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_TP_FAIL";
@@ -142,6 +152,7 @@ int __testpoint_consumerd_thread_metadata(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_data(void);
 int __testpoint_consumerd_thread_data(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_DATA_TP_FAIL";
@@ -153,6 +164,7 @@ int __testpoint_consumerd_thread_data(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_sessiond(void);
 int __testpoint_consumerd_thread_sessiond(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_SESSIOND_TP_FAIL";
@@ -164,6 +176,7 @@ int __testpoint_consumerd_thread_sessiond(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_metadata_timer(void);
 int __testpoint_consumerd_thread_metadata_timer(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_TIMER_TP_FAIL";
@@ -177,6 +190,7 @@ int __testpoint_consumerd_thread_metadata_timer(void)
 
 /* Relay daemon */
 
+int __testpoint_relayd_thread_dispatcher(void);
 int __testpoint_relayd_thread_dispatcher(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_DISPATCHER_TP_FAIL";
@@ -188,6 +202,7 @@ int __testpoint_relayd_thread_dispatcher(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_worker(void);
 int __testpoint_relayd_thread_worker(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_WORKER_TP_FAIL";
@@ -199,6 +214,7 @@ int __testpoint_relayd_thread_worker(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_listener(void);
 int __testpoint_relayd_thread_listener(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LISTENER_TP_FAIL";
@@ -210,6 +226,7 @@ int __testpoint_relayd_thread_listener(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_live_dispatcher(void);
 int __testpoint_relayd_thread_live_dispatcher(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LIVE_DISPATCHER_TP_FAIL";
@@ -221,6 +238,7 @@ int __testpoint_relayd_thread_live_dispatcher(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_live_worker(void);
 int __testpoint_relayd_thread_live_worker(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LIVE_WORKER_TP_FAIL";
@@ -232,6 +250,7 @@ int __testpoint_relayd_thread_live_worker(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_live_listener(void);
 int __testpoint_relayd_thread_live_listener(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LIVE_LISTENER_TP_FAIL";
index 4439b736957cc9e46ea47e14ea8742903f3e5d0c..1c0d6dc77353cf7a0a8b80b970a5c66d1a5e906a 100644 (file)
@@ -43,6 +43,7 @@ void do_stall(void)
 
 /* Session daemon */
 
+int __testpoint_sessiond_thread_manage_clients(void);
 int __testpoint_sessiond_thread_manage_clients(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CLIENTS_STALL";
@@ -54,6 +55,7 @@ int __testpoint_sessiond_thread_manage_clients(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_registration_apps(void);
 int __testpoint_sessiond_thread_registration_apps(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_REG_APPS_STALL";
@@ -65,6 +67,7 @@ int __testpoint_sessiond_thread_registration_apps(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_manage_apps(void);
 int __testpoint_sessiond_thread_manage_apps(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_APPS_STALL";
@@ -76,6 +79,7 @@ int __testpoint_sessiond_thread_manage_apps(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_manage_kernel(void);
 int __testpoint_sessiond_thread_manage_kernel(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_KERNEL_STALL";
@@ -87,6 +91,7 @@ int __testpoint_sessiond_thread_manage_kernel(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_manage_consumer(void);
 int __testpoint_sessiond_thread_manage_consumer(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CONSUMER_STALL";
@@ -98,6 +103,7 @@ int __testpoint_sessiond_thread_manage_consumer(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_ht_cleanup(void);
 int __testpoint_sessiond_thread_ht_cleanup(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_HT_CLEANUP_STALL";
@@ -109,6 +115,7 @@ int __testpoint_sessiond_thread_ht_cleanup(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_app_manage_notify(void);
 int __testpoint_sessiond_thread_app_manage_notify(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_APP_MANAGE_NOTIFY_STALL";
@@ -120,6 +127,7 @@ int __testpoint_sessiond_thread_app_manage_notify(void)
        return 0;
 }
 
+int __testpoint_sessiond_thread_app_reg_dispatch(void);
 int __testpoint_sessiond_thread_app_reg_dispatch(void)
 {
        const char *var = "LTTNG_SESSIOND_THREAD_APP_REG_DISPATCH_STALL";
@@ -133,6 +141,7 @@ int __testpoint_sessiond_thread_app_reg_dispatch(void)
 
 /* Consumer daemon */
 
+int __testpoint_consumerd_thread_channel(void);
 int __testpoint_consumerd_thread_channel(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_CHANNEL_STALL";
@@ -144,6 +153,7 @@ int __testpoint_consumerd_thread_channel(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_metadata(void);
 int __testpoint_consumerd_thread_metadata(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_STALL";
@@ -155,6 +165,7 @@ int __testpoint_consumerd_thread_metadata(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_data(void);
 int __testpoint_consumerd_thread_data(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_DATA_STALL";
@@ -166,6 +177,7 @@ int __testpoint_consumerd_thread_data(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_sessiond(void);
 int __testpoint_consumerd_thread_sessiond(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_SESSIOND_STALL";
@@ -177,6 +189,7 @@ int __testpoint_consumerd_thread_sessiond(void)
        return 0;
 }
 
+int __testpoint_consumerd_thread_metadata_timer(void);
 int __testpoint_consumerd_thread_metadata_timer(void)
 {
        const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_TIMER_STALL";
@@ -190,6 +203,7 @@ int __testpoint_consumerd_thread_metadata_timer(void)
 
 /* Relay daemon */
 
+int __testpoint_relayd_thread_dispatcher(void);
 int __testpoint_relayd_thread_dispatcher(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_DISPATCHER_STALL";
@@ -201,6 +215,7 @@ int __testpoint_relayd_thread_dispatcher(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_worker(void);
 int __testpoint_relayd_thread_worker(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_WORKER_STALL";
@@ -212,6 +227,7 @@ int __testpoint_relayd_thread_worker(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_listener(void);
 int __testpoint_relayd_thread_listener(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LISTENER_STALL";
@@ -223,6 +239,7 @@ int __testpoint_relayd_thread_listener(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_live_dispatcher(void);
 int __testpoint_relayd_thread_live_dispatcher(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LIVE_DISPATCHER_STALL";
@@ -234,6 +251,7 @@ int __testpoint_relayd_thread_live_dispatcher(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_live_worker(void);
 int __testpoint_relayd_thread_live_worker(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LIVE_WORKER_STALL";
@@ -245,6 +263,7 @@ int __testpoint_relayd_thread_live_worker(void)
        return 0;
 }
 
+int __testpoint_relayd_thread_live_listener(void);
 int __testpoint_relayd_thread_live_listener(void)
 {
        const char *var = "LTTNG_RELAYD_THREAD_LIVE_LISTENER_STALL";
index ee75c04f7ade0e0044a9636d1360b21f0ecd6d4a..5d10d228b7d8f6f29614acbb42d966555b7a8115 100644 (file)
@@ -97,7 +97,7 @@ ssize_t lttng_live_send(int fd, const void *buf, size_t len)
 }
 
 static
-int connect_viewer(char *hostname)
+int connect_viewer(const char *hostname)
 {
        struct hostent *host;
        struct sockaddr_in server_addr;
@@ -138,7 +138,7 @@ end:
        return ret;
 }
 
-int establish_connection(void)
+static int establish_connection(void)
 {
        struct lttng_viewer_cmd cmd;
        struct lttng_viewer_connect connect;
@@ -182,7 +182,7 @@ error:
 /*
  * Returns the number of sessions, should be 1 during the unit test.
  */
-int list_sessions(uint64_t *session_id)
+static int list_sessions(uint64_t *session_id)
 {
        struct lttng_viewer_cmd cmd;
        struct lttng_viewer_list_sessions list;
@@ -229,7 +229,7 @@ error:
        return -1;
 }
 
-int create_viewer_session(void)
+static int create_viewer_session(void)
 {
        struct lttng_viewer_cmd cmd;
        struct lttng_viewer_create_session_response resp;
@@ -267,7 +267,7 @@ error:
        return -1;
 }
 
-int attach_session(uint64_t id)
+static int attach_session(uint64_t id)
 {
        struct lttng_viewer_cmd cmd;
        struct lttng_viewer_attach_session_request rq;
@@ -356,7 +356,7 @@ error:
        return -1;
 }
 
-int get_metadata(void)
+static int get_metadata(void)
 {
        struct lttng_viewer_cmd cmd;
        struct lttng_viewer_get_metadata rq;
@@ -451,7 +451,7 @@ error:
        return -1;
 }
 
-int get_next_index(void)
+static int get_next_index(void)
 {
        struct lttng_viewer_cmd cmd;
        struct lttng_viewer_get_next_index rq;
@@ -616,7 +616,7 @@ error:
        return -1;
 }
 
-int detach_viewer_session(uint64_t id)
+static int detach_viewer_session(uint64_t id)
 {
        struct lttng_viewer_cmd cmd;
        struct lttng_viewer_detach_session_response resp;
index 3430f450ad7de80a1b4c26368fbe0de58359785d..beb6cee6ad6182b3d2fa9bc0c6e297b58c689366 100644 (file)
@@ -1,8 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_srcdir)/tests -I$(top_srcdir)/tests/utils/ -I$(srcdir)
-AM_LDFLAGS =
-
+AM_CFLAGS += -I$(top_srcdir)/tests/utils
 
 LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la
 LIB_LTTNG_CTL = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la
@@ -12,7 +10,7 @@ noinst_PROGRAMS = base_client notification rotation
 if NO_SHARED
 
 CLEANFILES = libpause_consumer.so libpause_consumer.so.debug
-EXTRA_DIST = test_notification_ust test_notification_kernel test_notification_multi_app base_client.c notification.c consumer_testpoints.c
+EXTRA_DIST = test_notification_ust_error test_notification_ust_buffer_usage test_notification_ust_event_rule_condition_exclusion test_notification_kernel_error test_notification_kernel_buffer_usage test_notification_kernel_instrumentation test_notification_kernel_syscall test_notification_kernel_userspace_probe test_notification_multi_app base_client.c notification.c consumer_testpoints.c
 
 else
 
@@ -38,8 +36,8 @@ notification_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm
 rotation_SOURCES = rotation.c
 rotation_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm
 
-noinst_SCRIPTS = test_notification_ust test_notification_kernel test_notification_multi_app test_rotation
-EXTRA_DIST = test_notification_ust test_notification_kernel test_notification_multi_app test_rotation
+noinst_SCRIPTS = test_notification_ust_error test_notification_ust_buffer_usage test_notification_ust_event_rule_condition_exclusion test_notification_kernel_error test_notification_kernel_buffer_usage test_notification_kernel_instrumentation test_notification_kernel_syscall test_notification_kernel_userspace_probe test_notification_multi_app test_rotation
+EXTRA_DIST = test_notification_ust_error test_notification_ust_buffer_usage test_notification_ust_event_rule_condition_exclusion test_notification_kernel_error test_notification_kernel_buffer_usage test_notification_kernel_instrumentation test_notification_kernel_syscall test_notification_kernel_userspace_probe test_notification_multi_app test_rotation
 
 
 all-local:
index fcef66c21a3f062c1a8c1b48c119a25b27ea5c9a..18304b7292c0739801227943a41d14ecc9622bfc 100644 (file)
@@ -42,7 +42,8 @@ int handle_condition(
                const struct lttng_condition *condition,
                const struct lttng_evaluation *condition_evaluation);
 
-int parse_arguments(char **argv) {
+static 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;
index c437216068c137177c135f07ec61dc52ead067a1..0d1edc5f96a54ce0b12bbc483c8e8ac22d16e51e 100644 (file)
@@ -47,6 +47,7 @@ void __attribute__((destructor)) pause_pipe_fini(void)
  * 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 __testpoint_consumerd_thread_data(void)
 {
        int ret = 0;
@@ -106,6 +107,7 @@ end:
        return ret;
 }
 
+int __testpoint_consumerd_thread_data_poll(void);
 int __testpoint_consumerd_thread_data_poll(void)
 {
        int ret = 0;
index f076bbd0e5f5373740061b13f47ef4efdfcd2ad9..436519c696cbb7c430e995d0f957988875fc9f33 100644 (file)
 #include <lttng/condition/buffer-usage.h>
 #include <lttng/condition/condition.h>
 #include <lttng/condition/evaluation.h>
+#include <lttng/condition/event-rule.h>
 #include <lttng/domain.h>
 #include <lttng/endpoint.h>
+#include <lttng/event-rule/kprobe.h>
+#include <lttng/event-rule/syscall.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event-rule/uprobe.h>
 #include <lttng/lttng-error.h>
+#include <lttng/lttng.h>
 #include <lttng/notification/channel.h>
 #include <lttng/notification/notification.h>
 #include <lttng/trigger/trigger.h>
-#include <lttng/lttng.h>
+#include <lttng/userspace-probe.h>
 
 #include <tap/tap.h>
 
-#define NUM_TESTS 104
-
 int nb_args = 0;
 int named_pipe_args_start = 0;
-pid_t app_pid = -1;
+pid_t app_pid = 0;
 const char *app_state_file = NULL;
 
 static
@@ -87,7 +91,7 @@ void wait_on_file(const char *path, bool file_exist)
        }
 }
 
-int write_pipe(const char *path, uint8_t data)
+static int write_pipe(const char *path, uint8_t data)
 {
        int ret = 0;
        int fd = 0;
@@ -118,7 +122,7 @@ end:
        return ret;
 }
 
-int stop_consumer(const char **argv)
+static int stop_consumer(const char **argv)
 {
        int ret = 0, i;
 
@@ -128,7 +132,7 @@ int stop_consumer(const char **argv)
        return ret;
 }
 
-int resume_consumer(const char **argv)
+static int resume_consumer(const char **argv)
 {
        int ret = 0, i;
 
@@ -138,7 +142,7 @@ int resume_consumer(const char **argv)
        return ret;
 }
 
-int suspend_application()
+static int suspend_application(void)
 {
        int ret;
        struct stat buf;
@@ -152,6 +156,8 @@ int suspend_application()
        /*
         * Send SIGUSR1 to application instructing it to bypass tracepoint.
         */
+       assert(app_pid > 1);
+
        ret = kill(app_pid, SIGUSR1);
        if (ret) {
                fail("SIGUSR1 failed. errno %d", errno);
@@ -166,7 +172,7 @@ error:
 
 }
 
-int resume_application()
+static int resume_application()
 {
        int ret;
        struct stat buf;
@@ -181,6 +187,8 @@ int resume_application()
                goto error;
        }
 
+       assert(app_pid > 1);
+
        ret = kill(app_pid, SIGUSR1);
        if (ret) {
                fail("SIGUSR1 failed. errno %d", errno);
@@ -196,7 +204,7 @@ error:
 }
 
 
-void test_triggers_buffer_usage_condition(const char *session_name,
+static 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)
@@ -315,7 +323,7 @@ void test_triggers_buffer_usage_condition(const char *session_name,
                        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",
+               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",
@@ -389,46 +397,90 @@ void wait_data_pending(const char *session_name)
        } while (ret != 0);
 }
 
-void test_notification_channel(const char *session_name, const char *channel_name, const enum lttng_domain_type domain_type, const char **argv)
+static int setup_buffer_usage_condition(struct lttng_condition *condition,
+               const char *condition_name,
+               const char *session_name,
+               const char *channel_name,
+               const enum lttng_domain_type domain_type)
 {
-       int ret = 0;
        enum lttng_condition_status condition_status;
-       enum lttng_notification_channel_status nc_status;
+       int ret = 0;
 
-       struct lttng_action *action = NULL;
-       struct lttng_notification *notification = NULL;
-       struct lttng_notification_channel *notification_channel = NULL;
-       struct lttng_trigger *trigger = NULL;
+       condition_status = lttng_condition_buffer_usage_set_session_name(
+                       condition, session_name);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               fail("Error setting session name on %s creation", condition_name);
+               ret = -1;
+               goto end;
+       }
 
-       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;
+       condition_status = lttng_condition_buffer_usage_set_channel_name(
+                       condition, channel_name);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               fail("Error setting channel name on %s creation", condition_name);
+               ret = -1;
+               goto end;
+       }
 
-       double low_ratio = 0.0;
-       double high_ratio = 0.99;
+       condition_status = lttng_condition_buffer_usage_set_domain_type(
+                       condition, domain_type);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               fail("Error setting domain type on %s creation", condition_name);
+               ret = -1;
+               goto end;
+       }
 
-       /* Set-up */
-       action = lttng_action_notify_create();
-       if (!action) {
-               fail("Setup error on action creation");
+end:
+       return ret;
+}
+
+static void test_invalid_channel_subscription(
+               const enum lttng_domain_type domain_type)
+{
+       enum lttng_condition_status condition_status;
+       enum lttng_notification_channel_status nc_status;
+       struct lttng_condition *dummy_condition = NULL;
+       struct lttng_condition *dummy_invalid_condition = NULL;
+       struct lttng_notification_channel *notification_channel = NULL;
+       int ret = 0;
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
+       if (!notification_channel) {
                goto end;
        }
 
-       /* Create a dummy, empty condition for later test */
+       /*
+        * Create a dummy, empty (thus invalid) condition to test error paths.
+        */
        dummy_invalid_condition = lttng_condition_buffer_usage_low_create();
        if (!dummy_invalid_condition) {
                fail("Setup error on condition creation");
                goto end;
        }
 
+       /*
+        * Test subscription and unsubscription of an invalid condition to/from
+        * a channel.
+        */
+       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 from an invalid condition");
+
        /* 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) {
@@ -436,168 +488,247 @@ void test_notification_channel(const char *session_name, const char *channel_nam
                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");
+       ret = setup_buffer_usage_condition(dummy_condition, "dummy_condition",
+                       "dummy_session", "dummy_channel", domain_type);
+       if (ret) {
+               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;
-       }
+       /*
+        * Test subscription and unsubscription to/from a channel with invalid
+        * parameters.
+        */
+       nc_status = lttng_notification_channel_subscribe(NULL, NULL);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID,
+                       "Notification channel subscription is invalid: NULL, NULL");
 
-       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;
+       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, dummy_condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID,
+                       "Notification channel subscription is invalid: NULL, NON-NULL");
+
+       nc_status = lttng_notification_channel_unsubscribe(
+                       notification_channel, dummy_condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION,
+                       "Unsubscribing from a valid unknown condition");
+
+end:
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_condition_destroy(dummy_invalid_condition);
+       lttng_condition_destroy(dummy_condition);
+       return;
+}
+
+enum buffer_usage_type {
+       BUFFER_USAGE_TYPE_LOW,
+       BUFFER_USAGE_TYPE_HIGH,
+};
+
+static int register_buffer_usage_notify_trigger(const char *session_name,
+               const char *channel_name,
+               const enum lttng_domain_type domain_type,
+               enum buffer_usage_type buffer_usage_type,
+               double ratio,
+               struct lttng_condition **condition,
+               struct lttng_action **action,
+               struct lttng_trigger **trigger)
+{
+       enum lttng_condition_status condition_status;
+       struct lttng_action *tmp_action = NULL;
+       struct lttng_condition *tmp_condition = NULL;
+       struct lttng_trigger *tmp_trigger = NULL;
+       int ret = 0;
+
+       /* Set-up */
+       tmp_action = lttng_action_notify_create();
+       if (!action) {
+               fail("Setup error on action creation");
+               ret = -1;
+               goto error;
        }
-       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;
 
+       if (buffer_usage_type == BUFFER_USAGE_TYPE_LOW) {
+               tmp_condition = lttng_condition_buffer_usage_low_create();
+       } else {
+               tmp_condition = lttng_condition_buffer_usage_high_create();
        }
 
-       /* 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;
+       if (!tmp_condition) {
+               fail("Setup error on condition creation");
+               ret = -1;
+               goto error;
        }
 
+       /* Set the buffer usage threashold */
        condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
-                       high_condition, high_ratio);
+                       tmp_condition, ratio);
        if (condition_status != LTTNG_CONDITION_STATUS_OK) {
-               fail("Setup error on high_condition creation");
-               goto end;
+               fail("Setup error on condition creation");
+               ret = -1;
+               goto error;
        }
 
-       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;
+       ret = setup_buffer_usage_condition(tmp_condition, "condition_name",
+                       session_name, channel_name, domain_type);
+       if (ret) {
+               fail("Setup error on condition creation");
+               ret = -1;
+               goto error;
        }
-       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;
+
+       /* Register the triggers for condition */
+       tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action);
+       if (!tmp_trigger) {
+               fail("Setup error on trigger creation");
+               ret = -1;
+               goto error;
        }
-       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;
+
+       ret = lttng_register_trigger(tmp_trigger);
+       if (ret) {
+               fail("Setup error on trigger registration");
+               ret = -1;
+               goto error;
        }
 
-       /* Register the triggers for low and high condition */
-       trigger = lttng_trigger_create(low_condition, action);
-       if (!trigger) {
-               fail("Setup error on low trigger creation");
+       *condition = tmp_condition;
+       *trigger = tmp_trigger;
+       *action = tmp_action;
+       goto end;
+
+error:
+       lttng_action_destroy(tmp_action);
+       lttng_condition_destroy(tmp_condition);
+       lttng_trigger_destroy(tmp_trigger);
+
+end:
+       return ret;
+}
+
+static void test_subscription_twice(const char *session_name,
+               const char *channel_name,
+               const enum lttng_domain_type domain_type)
+{
+       int ret = 0;
+       enum lttng_notification_channel_status nc_status;
+
+       struct lttng_action *action = NULL;
+       struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_trigger *trigger = NULL;
+
+       struct lttng_condition *condition = NULL;
+
+       ret = register_buffer_usage_notify_trigger(session_name, channel_name,
+                       domain_type, BUFFER_USAGE_TYPE_LOW, 0.99, &condition,
+                       &action, &trigger);
+       if (ret) {
+               fail("Setup error on trigger registration");
                goto end;
        }
 
-       ret = lttng_register_trigger(trigger);
-       if (ret) {
-               fail("Setup error on low trigger registration");
+       /* 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;
        }
 
+       /* Subscribe a valid condition. */
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to condition");
+
+       /* Subscribing again should fail. */
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED,
+                       "Subscribe to a condition for which subscription was already done");
+
+end:
+       lttng_unregister_trigger(trigger);
        lttng_trigger_destroy(trigger);
-       trigger = NULL;
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_action_destroy(action);
+       lttng_condition_destroy(condition);
+}
 
-       trigger = lttng_trigger_create(high_condition, action);
-       if (!trigger) {
-               fail("Setup error on high trigger creation");
+static void test_buffer_usage_notification_channel(const char *session_name,
+               const char *channel_name,
+               const enum lttng_domain_type domain_type,
+               const char **argv)
+{
+       int ret = 0;
+       enum lttng_notification_channel_status nc_status;
+
+       struct lttng_action *low_action = NULL;
+       struct lttng_action *high_action = NULL;
+       struct lttng_notification *notification = NULL;
+       struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_trigger *low_trigger = NULL;
+       struct lttng_trigger *high_trigger = NULL;
+
+       struct lttng_condition *low_condition = NULL;
+       struct lttng_condition *high_condition = NULL;
+
+       double low_ratio = 0.0;
+       double high_ratio = 0.99;
+
+       ret = register_buffer_usage_notify_trigger(session_name, channel_name,
+                       domain_type, BUFFER_USAGE_TYPE_LOW, low_ratio,
+                       &low_condition, &low_action, &low_trigger);
+       if (ret) {
+               fail("Setup error on low trigger registration");
                goto end;
        }
 
-       ret = lttng_register_trigger(trigger);
+       ret = register_buffer_usage_notify_trigger(session_name, channel_name,
+                       domain_type, BUFFER_USAGE_TYPE_HIGH, high_ratio,
+                       &high_condition, &high_action, &high_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);
+       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 from an invalid condition");
-
-       nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_condition);
-       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, "Unsubscribing from a 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");
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, low_condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to low 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, high_condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to high 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");
+       resume_application();
 
        /* Wait for notification to happen */
        stop_consumer(argv);
        lttng_start_tracing(session_name);
 
        /* 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,
+       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;
@@ -612,16 +743,22 @@ void test_notification_channel(const char *session_name, const char *channel_nam
         * 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_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_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,
+       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;
@@ -631,9 +768,12 @@ void test_notification_channel(const char *session_name, const char *channel_nam
        resume_application();
        lttng_start_tracing(session_name);
 
-       nc_status = lttng_notification_channel_get_next_notification(notification_channel, &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,
+                                       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;
@@ -643,9 +783,12 @@ void test_notification_channel(const char *session_name, const char *channel_nam
        resume_consumer(argv);
        wait_data_pending(session_name);
 
-       nc_status = lttng_notification_channel_get_next_notification(notification_channel, &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,
+                                       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;
@@ -655,77 +798,892 @@ void test_notification_channel(const char *session_name, const char *channel_nam
        /* Stop consumer to force a high notification */
        lttng_start_tracing(session_name);
 
-       nc_status = lttng_notification_channel_get_next_notification(notification_channel, &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,
+                                       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 */
        suspend_application();
+
+       /* Resume consumer to allow event consumption */
        lttng_stop_tracing_no_wait(session_name);
        resume_consumer(argv);
        wait_data_pending(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");
+       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_trigger_destroy(low_trigger);
+       lttng_trigger_destroy(high_trigger);
+       lttng_action_destroy(low_action);
+       lttng_action_destroy(high_action);
        lttng_condition_destroy(low_condition);
        lttng_condition_destroy(high_condition);
-       lttng_condition_destroy(dummy_invalid_condition);
-       lttng_condition_destroy(dummy_condition);
 }
 
-int main(int argc, const char *argv[])
+static void create_tracepoint_event_rule_trigger(const char *event_pattern,
+               const char *trigger_name,
+               const char *filter,
+               unsigned int exclusion_count,
+               const char **exclusions,
+               enum lttng_domain_type domain_type,
+               struct lttng_condition **condition,
+               struct lttng_trigger **trigger)
 {
-       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;
+       enum lttng_event_rule_status event_rule_status;
+       enum lttng_trigger_status trigger_status;
 
-       plan_tests(NUM_TESTS);
+       struct lttng_action *tmp_action = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+       struct lttng_condition *tmp_condition = NULL;
+       struct lttng_trigger *tmp_trigger = NULL;
+       int ret;
+
+       assert(event_pattern);
+       assert(trigger_name);
+       assert(condition);
+       assert(trigger);
+
+       event_rule = lttng_event_rule_tracepoint_create(domain_type);
+       ok(event_rule, "Tracepoint event rule object creation");
+
+       event_rule_status = lttng_event_rule_tracepoint_set_pattern(
+                       event_rule, event_pattern);
+       ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                       "Setting tracepoint event rule pattern: %s",
+                       event_pattern);
+
+       if (filter) {
+               event_rule_status = lttng_event_rule_tracepoint_set_filter(
+                               event_rule, filter);
+               ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                               "Setting tracepoint event rule filter: %s",
+                               filter);
+       }
 
-       /* Argument 6 and upward are named pipe location for consumerd control */
-       named_pipe_args_start = 6;
+       if (exclusions) {
+               assert(domain_type == LTTNG_DOMAIN_UST);
+               assert(exclusion_count > 0);
 
-       if (argc < 7) {
-               fail("Missing parameter for tests to run %d", argc);
-               goto error;
+               event_rule_status = lttng_event_rule_tracepoint_set_exclusions(
+                               event_rule, exclusion_count, exclusions);
+               ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                               "Setting tracepoint event rule exclusions");
        }
 
-       nb_args = argc;
+       tmp_condition = lttng_condition_event_rule_create(event_rule);
+       ok(tmp_condition, "Condition event rule object creation");
+       /* Ownership was passed to condition */
+       event_rule = NULL;
 
-       domain_type_string = argv[1];
-       session_name = argv[2];
-       channel_name = argv[3];
-       app_pid = (pid_t) atoi(argv[4]);
-       app_state_file = argv[5];
+       tmp_action = lttng_action_notify_create();
+       ok(tmp_action, "Action event rule object creation");
 
-       if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) {
-               domain_type = LTTNG_DOMAIN_UST;
+       tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action);
+       ok(tmp_trigger, "Trigger object creation %s", trigger_name);
+
+       trigger_status = lttng_trigger_set_name(tmp_trigger, trigger_name);
+       ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+                       "Setting name to trigger %s", trigger_name);
+
+       ret = lttng_register_trigger(tmp_trigger);
+       ok(ret == 0, "Trigger registration %s", trigger_name);
+
+       *condition = tmp_condition;
+       *trigger = tmp_trigger;
+
+       return;
+}
+
+static char *get_next_notification_trigger_name(
+               struct lttng_notification_channel *notification_channel)
+{
+       struct lttng_notification *notification;
+       enum lttng_notification_channel_status status;
+       const struct lttng_evaluation *notification_evaluation;
+       char *trigger_name = NULL;
+       const char *name;
+
+       /* Receive the next notification. */
+       status = lttng_notification_channel_get_next_notification(
+                       notification_channel, &notification);
+
+       switch (status) {
+       case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK:
+               break;
+       default:
+               /* Unhandled conditions / errors. */
+               fail("error: Unknown notification channel status\n");
+               goto end;
        }
-       if (!strcmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) {
-               domain_type = LTTNG_DOMAIN_KERNEL;
+
+       notification_evaluation =
+                       lttng_notification_get_evaluation(notification);
+
+       switch (lttng_evaluation_get_type(notification_evaluation)) {
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               lttng_evaluation_event_rule_get_trigger_name(
+                               notification_evaluation, &name);
+
+               trigger_name = strdup(name);
+               break;
+       default:
+               fail("error: Wrong notification evaluation type \n");
+               break;
        }
-       if (domain_type == LTTNG_DOMAIN_NONE) {
-               fail("Unknown domain type");
-               goto error;
+
+       lttng_notification_destroy(notification);
+
+end:
+       return trigger_name;
+}
+
+static void test_tracepoint_event_rule_notification(
+               enum lttng_domain_type domain_type)
+{
+       int i;
+       enum lttng_notification_channel_status nc_status;
+
+       struct lttng_action *action = NULL;
+       struct lttng_condition *condition = NULL;
+       struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_trigger *trigger = NULL;
+       const char *trigger_name = "my_precious";
+       const char *pattern;
+
+       if (domain_type == LTTNG_DOMAIN_UST) {
+               pattern = "tp:tptest";
+       } else {
+               pattern = "lttng_test_filter_event";
+       }
+
+       create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0,
+                       NULL, domain_type, &condition, &trigger);
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
+
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
+
+       resume_application();
+
+       /* Get 3 notifications */
+       for (i = 0; i < 3; i++) {
+               char *name = get_next_notification_trigger_name(
+                               notification_channel);
+               ok(strcmp(trigger_name, name) == 0,
+                               "Received notification for the expected trigger name: %s",
+                               trigger_name);
+               free(name);
        }
 
-       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);
+       suspend_application();
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_unregister_trigger(trigger);
+       lttng_trigger_destroy(trigger);
+       lttng_action_destroy(action);
+       lttng_condition_destroy(condition);
+       return;
+}
+
+static void test_tracepoint_event_rule_notification_filter(
+               enum lttng_domain_type domain_type)
+{
+       int i;
+       enum lttng_notification_channel_status nc_status;
+
+       struct lttng_condition *ctrl_condition = NULL, *condition = NULL;
+       struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL;
+       const char *ctrl_trigger_name = "control_trigger";
+       const char *trigger_name = "trigger";
+       const char *pattern;
+       int ctrl_count = 0, count = 0;
+
+       if (domain_type == LTTNG_DOMAIN_UST) {
+               pattern = "tp:tptest";
+       } else {
+               pattern = "lttng_test_filter_event";
+       }
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
+
+       create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL,
+                       0, NULL, domain_type, &ctrl_condition, &ctrl_trigger);
+
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, ctrl_condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
+
+       /*
+        * Attach a filter expression to get notification only if the
+        * `intfield` is even.
+        */
+       create_tracepoint_event_rule_trigger(pattern, trigger_name,
+                       "(intfield & 1) == 0", 0, NULL, domain_type, &condition,
+                       &trigger);
+
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
+
+       /*
+        * We registered 2 notifications triggers, one with a filter and one
+        * without (control). The one with a filter will only fired when the
+        * `intfield` is a multiple of 2. We should get two times as many
+        * control notifications as filter notifications.
+        */
+       resume_application();
+
+       /*
+        * Get 3 notifications. We should get 1 for the regular trigger (with
+        * the filter) and 2 from the control trigger. This works whatever
+        * the order we receive the notifications.
+        */
+       for (i = 0; i < 3; i++) {
+               char *name = get_next_notification_trigger_name(
+                               notification_channel);
+               if (strcmp(ctrl_trigger_name, name) == 0) {
+                       ctrl_count++;
+               } else if (strcmp(trigger_name, name) == 0) {
+                       count++;
+               }
+               free(name);
+       }
+       ok(ctrl_count / 2 == count,
+                       "Get twice as many control notif as of regular notif");
+
+       suspend_application();
+       lttng_unregister_trigger(trigger);
+       lttng_unregister_trigger(ctrl_trigger);
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_trigger_destroy(trigger);
+       lttng_trigger_destroy(ctrl_trigger);
+       lttng_condition_destroy(condition);
+       lttng_condition_destroy(ctrl_condition);
+       return;
+}
+
+static void test_tracepoint_event_rule_notification_exclusion(
+               enum lttng_domain_type domain_type)
+{
+       enum lttng_notification_channel_status nc_status;
+       struct lttng_condition *ctrl_condition = NULL, *condition = NULL;
+       struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL;
+       const char *ctrl_trigger_name = "control_exclusion_trigger";
+       const char *trigger_name = "exclusion_trigger";
+       const char *pattern = "tp:tptest*";
+       const char *exclusions[4] = {
+                       "tp:tptest2", "tp:tptest3", "tp:tptest4", "tp:tptest5"};
+       int ctrl_count = 0, count = 0;
+       int i;
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
+
+       create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL,
+                       0, NULL, domain_type, &ctrl_condition, &ctrl_trigger);
+
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, ctrl_condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
+
+       create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 4,
+                       exclusions, domain_type, &condition, &trigger);
+
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
+
+       /*
+        * We registered 2 notifications triggers, one with an exclusion and
+        * one without (control).
+        * - The trigger with an exclusion will fire once every iteration.
+        * - The trigger without an exclusion will fire 5 times every
+        *   iteration.
+        *
+        *   We should get 5 times as many notifications from the control
+        *   trigger.
+        */
+       resume_application();
+
+       /*
+        * Get 6 notifications. We should get 1 for the regular trigger (with
+        * the exclusion) and 5 from the control trigger. This works whatever
+        * the order we receive the notifications.
+        */
+       for (i = 0; i < 6; i++) {
+               char *name = get_next_notification_trigger_name(
+                               notification_channel);
+               if (strcmp(ctrl_trigger_name, name) == 0) {
+                       ctrl_count++;
+               } else if (strcmp(trigger_name, name) == 0) {
+                       count++;
+               }
+               free(name);
+       }
+       ok(ctrl_count / 5 == count,
+                       "Got 5 times as many control notif as of regular notif");
+
+       suspend_application();
+       lttng_unregister_trigger(trigger);
+       lttng_unregister_trigger(ctrl_trigger);
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_trigger_destroy(trigger);
+       lttng_trigger_destroy(ctrl_trigger);
+       lttng_condition_destroy(condition);
+       lttng_condition_destroy(ctrl_condition);
+       return;
+}
+
+static void test_kprobe_event_rule_notification(
+               enum lttng_domain_type domain_type)
+{
+       enum lttng_notification_channel_status nc_status;
+       enum lttng_event_rule_status event_rule_status;
+       enum lttng_trigger_status trigger_status;
+
+       struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+       struct lttng_action *action = NULL;
+       struct lttng_trigger *trigger = NULL;
+       const char *trigger_name = "kprobe_trigger";
+       const char *symbol_name = "_do_fork";
+       int i, ret;
+
+       action = lttng_action_notify_create();
+       if (!action) {
+               fail("Setup error on action creation");
+               goto end;
+       }
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
+
+       event_rule = lttng_event_rule_kprobe_create();
+       ok(event_rule, "kprobe event rule object creation");
+
+       event_rule_status = lttng_event_rule_kprobe_set_source(
+                       event_rule, symbol_name);
+       ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                       "Setting kprobe event rule source: %s", symbol_name);
+
+       event_rule_status = lttng_event_rule_kprobe_set_name(
+                       event_rule, trigger_name);
+       ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                       "Setting kprobe event rule name: %s", trigger_name);
+
+       condition = lttng_condition_event_rule_create(event_rule);
+       ok(condition, "Condition event rule object creation");
+
+       /* Register the triggers for condition */
+       trigger = lttng_trigger_create(condition, action);
+       if (!trigger) {
+               fail("Setup error on trigger creation");
+               goto end;
+       }
+
+       trigger_status = lttng_trigger_set_name(trigger, trigger_name);
+       ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+                       "Setting name to trigger %s", trigger_name);
+
+       ret = lttng_register_trigger(trigger);
+       if (ret) {
+               fail("Setup error on trigger registration");
+               goto end;
+       }
+
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
+
+       resume_application();
+
+       for (i = 0; i < 3; i++) {
+               char *name = get_next_notification_trigger_name(
+                               notification_channel);
+               ok(strcmp(trigger_name, name) == 0,
+                               "Received notification for the expected trigger name: %s",
+                               trigger_name);
+               free(name);
+       }
+
+end:
+       suspend_application();
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_unregister_trigger(trigger);
+       lttng_trigger_destroy(trigger);
+       lttng_action_destroy(action);
+       lttng_condition_destroy(condition);
+       return;
+}
+
+static void test_uprobe_event_rule_notification(
+               enum lttng_domain_type domain_type,
+               const char *testapp_path,
+               const char *test_symbol_name)
+{
+       enum lttng_notification_channel_status nc_status;
+       enum lttng_event_rule_status event_rule_status;
+       enum lttng_trigger_status trigger_status;
+
+       struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_userspace_probe_location *probe_location = NULL;
+       struct lttng_userspace_probe_location_lookup_method *lookup_method =
+                       NULL;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+       struct lttng_action *action = NULL;
+       struct lttng_trigger *trigger = NULL;
+       const char *trigger_name = "uprobe_trigger";
+       int i, ret;
+
+       action = lttng_action_notify_create();
+       if (!action) {
+               fail("Setup error on action creation");
+               goto end;
+       }
+
+       lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create();
+       if (!lookup_method) {
+               fail("Setup error on userspace probe lookup method creation");
+               goto end;
+       }
+
+       probe_location = lttng_userspace_probe_location_function_create(
+                       testapp_path, test_symbol_name, lookup_method);
+       if (!probe_location) {
+               fail("Setup error on userspace probe location creation");
+               goto end;
+       }
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
+
+       event_rule = lttng_event_rule_uprobe_create();
+       ok(event_rule, "kprobe event rule object creation");
+
+       event_rule_status = lttng_event_rule_uprobe_set_location(
+                       event_rule, probe_location);
+       ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                       "Setting uprobe event rule location");
+
+       event_rule_status = lttng_event_rule_uprobe_set_name(
+                       event_rule, trigger_name);
+       ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                       "Setting uprobe event rule name: %s", trigger_name);
+
+       condition = lttng_condition_event_rule_create(event_rule);
+       ok(condition, "Condition event rule object creation");
+
+       /* Register the triggers for condition */
+       trigger = lttng_trigger_create(condition, action);
+       if (!trigger) {
+               fail("Setup error on trigger creation");
+               goto end;
+       }
+
+       trigger_status = lttng_trigger_set_name(trigger, trigger_name);
+       ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+                       "Setting name to trigger %s", trigger_name);
+
+       ret = lttng_register_trigger(trigger);
+       if (ret) {
+               fail("Setup error on trigger registration");
+               goto end;
+       }
+
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
+
+       resume_application();
+
+       for (i = 0; i < 3; i++) {
+               char *name = get_next_notification_trigger_name(
+                               notification_channel);
+               ok(strcmp(trigger_name, name) == 0,
+                               "Received notification for the expected trigger name: %s",
+                               trigger_name);
+               free(name);
+       }
+end:
+       suspend_application();
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_unregister_trigger(trigger);
+       lttng_trigger_destroy(trigger);
+       lttng_action_destroy(action);
+       lttng_condition_destroy(condition);
+       return;
+}
+
+static void test_syscall_event_rule_notification(
+               enum lttng_domain_type domain_type)
+{
+       enum lttng_notification_channel_status nc_status;
+       enum lttng_event_rule_status event_rule_status;
+       enum lttng_trigger_status trigger_status;
+
+       struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+       struct lttng_action *action = NULL;
+       struct lttng_trigger *trigger = NULL;
+       const char *trigger_name = "syscall_trigger";
+       const char *syscall_name = "openat";
+       int i, ret;
+
+       action = lttng_action_notify_create();
+       if (!action) {
+               fail("Setup error on action creation");
+               goto end;
+       }
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
+
+       event_rule = lttng_event_rule_syscall_create();
+       ok(event_rule, "syscall event rule object creation");
+
+       event_rule_status = lttng_event_rule_syscall_set_pattern(
+                       event_rule, syscall_name);
+       ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                       "Setting syscall event rule pattern: %s", syscall_name);
+
+       condition = lttng_condition_event_rule_create(event_rule);
+       ok(condition, "Condition event rule object creation");
+
+       /* Register the triggers for condition */
+       trigger = lttng_trigger_create(condition, action);
+       if (!trigger) {
+               fail("Setup error on trigger creation");
+               goto end;
+       }
+
+       trigger_status = lttng_trigger_set_name(trigger, trigger_name);
+       ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+                       "Setting name to trigger %s", trigger_name);
+
+       ret = lttng_register_trigger(trigger);
+       if (ret) {
+               fail("Setup error on trigger registration");
+               goto end;
+       }
+
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
+
+       resume_application();
+
+       for (i = 0; i < 3; i++) {
+               char *name = get_next_notification_trigger_name(
+                               notification_channel);
+               ok(strcmp(trigger_name, name) == 0,
+                               "Received notification for the expected trigger name: %s",
+                               trigger_name);
+               free(name);
+       }
+end:
+       suspend_application();
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_unregister_trigger(trigger);
+       lttng_trigger_destroy(trigger);
+       lttng_action_destroy(action);
+       lttng_condition_destroy(condition);
+       return;
+}
+
+static void test_syscall_event_rule_notification_filter(
+               enum lttng_domain_type domain_type)
+{
+       enum lttng_notification_channel_status nc_status;
+       enum lttng_event_rule_status event_rule_status;
+       enum lttng_trigger_status trigger_status;
+
+       struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+       struct lttng_action *action = NULL;
+       struct lttng_trigger *trigger = NULL;
+       const char *trigger_name = "syscall_trigger";
+       const char *syscall_name = "openat";
+       int i, ret;
+
+       action = lttng_action_notify_create();
+       if (!action) {
+               fail("Setup error on action creation");
+               goto end;
+       }
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
+
+       event_rule = lttng_event_rule_syscall_create();
+       ok(event_rule, "syscall event rule object creation");
+
+       event_rule_status = lttng_event_rule_syscall_set_pattern(
+                       event_rule, syscall_name);
+       ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                       "Setting syscall event rule pattern: %s", syscall_name);
+
+       event_rule_status = lttng_event_rule_syscall_set_filter(
+                       event_rule, "filename==\"/proc/cpuinfo\"");
+       ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                       "Setting syscall event rule pattern: %s", syscall_name);
+
+       condition = lttng_condition_event_rule_create(event_rule);
+       ok(condition, "Condition event rule object creation");
+
+       /* Register the triggers for condition */
+       trigger = lttng_trigger_create(condition, action);
+       if (!trigger) {
+               fail("Setup error on trigger creation");
+               goto end;
+       }
+
+       trigger_status = lttng_trigger_set_name(trigger, trigger_name);
+       ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+                       "Setting name to trigger %s", trigger_name);
+
+       ret = lttng_register_trigger(trigger);
+       if (ret) {
+               fail("Setup error on trigger registration");
+               goto end;
+       }
+
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
+
+       resume_application();
+
+       for (i = 0; i < 3; i++) {
+               char *name = get_next_notification_trigger_name(
+                               notification_channel);
+               ok(strcmp(trigger_name, name) == 0,
+                               "Received notification for the expected trigger name: %s",
+                               trigger_name);
+               free(name);
+       }
+
+end:
+       suspend_application();
+       lttng_unregister_trigger(trigger);
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_trigger_destroy(trigger);
+       lttng_condition_destroy(condition);
+       return;
+}
+
+int main(int argc, const char *argv[])
+{
+       int test_scenario;
+       const char *domain_type_string = NULL;
+       enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
+
+       if (argc < 5) {
+               fail("Missing test scenario, domain type, pid, or application state file argument(s)");
+               goto error;
+       }
+
+       test_scenario = atoi(argv[1]);
+       domain_type_string = argv[2];
+       app_pid = (pid_t) atoi(argv[3]);
+       app_state_file = argv[4];
+
+       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;
+       }
+
+       /*
+        * Test cases are responsible for resuming the app when needed
+        * and making sure it's suspended when returning.
+        */
+       suspend_application();
+
+       switch (test_scenario) {
+       case 1:
+       {
+               plan_tests(38);
+
+               /* Test cases that need gen-ust-event testapp. */
+               diag("Test basic notification error paths for domain %s",
+                               domain_type_string);
+               test_invalid_channel_subscription(domain_type);
+
+               diag("Test tracepoint event rule notifications for domain %s",
+                               domain_type_string);
+               test_tracepoint_event_rule_notification(domain_type);
+
+               diag("Test tracepoint event rule notifications with filter for domain %s",
+                               domain_type_string);
+               test_tracepoint_event_rule_notification_filter(domain_type);
+               break;
+       }
+       case 2:
+       {
+               const char *session_name, *channel_name;
+               /* Test cases that need a tracing session enabled. */
+               plan_tests(99);
+
+               /*
+                * Argument 7 and upward are named pipe location for consumerd
+                * control.
+                */
+               named_pipe_args_start = 7;
+
+               if (argc < 8) {
+                       fail("Missing parameter for tests to run %d", argc);
+                       goto error;
+               }
+
+               nb_args = argc;
+
+               session_name = argv[5];
+               channel_name = argv[6];
+
+               test_subscription_twice(session_name, channel_name,
+                               domain_type);
+
+               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 buffer usage notification channel api for domain %s",
+                               domain_type_string);
+               test_buffer_usage_notification_channel(session_name, channel_name,
+                               domain_type, argv);
+               break;
+       }
+       case 3:
+       {
+               /*
+                * Test cases that need a test app with more than one event
+                * type.
+                */
+               plan_tests(19);
+
+               /*
+                * At the moment, the only test case of this scenario is
+                * exclusion which is only supported by UST
+                */
+               assert(domain_type == LTTNG_DOMAIN_UST);
+               diag("Test tracepoint event rule notifications with exclusion for domain %s",
+                               domain_type_string);
+               test_tracepoint_event_rule_notification_exclusion(domain_type);
+
+               break;
+       }
+       case 4:
+       {
+               plan_tests(10);
+               /* Test cases that need the kernel tracer. */
+               assert(domain_type == LTTNG_DOMAIN_KERNEL);
+
+               diag("Test kprobe event rule notifications for domain %s",
+                               domain_type_string);
+
+               test_kprobe_event_rule_notification(domain_type);
+
+               break;
+       }
+       case 5:
+       {
+               plan_tests(19);
+               /* Test cases that need the kernel tracer. */
+               assert(domain_type == LTTNG_DOMAIN_KERNEL);
+
+               diag("Test syscall event rule notifications for domain %s",
+                               domain_type_string);
+
+               test_syscall_event_rule_notification(domain_type);
+
+               diag("Test syscall filtering event rule notifications for domain %s",
+                               domain_type_string);
+
+               test_syscall_event_rule_notification_filter(domain_type);
+
+               break;
+       }
+       case 6:
+       {
+               const char *testapp_path, *test_symbol_name;
+
+               plan_tests(10);
+
+               if (argc < 7) {
+                       fail("Missing parameter for tests to run %d", argc);
+                       goto error;
+               }
+
+               testapp_path = argv[5];
+               test_symbol_name = argv[6];
+               /* Test cases that need the kernel tracer. */
+               assert(domain_type == LTTNG_DOMAIN_KERNEL);
+
+               diag("Test userspace-probe event rule notifications for domain %s",
+                               domain_type_string);
+
+               test_uprobe_event_rule_notification(
+                               domain_type, testapp_path, test_symbol_name);
+
+               break;
+       }
+       default:
+               abort();
+       }
 
-       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_kernel b/tests/regression/tools/notification/test_notification_kernel
deleted file mode 100755 (executable)
index 81035b8..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
-#
-# SPDX-License-Identifier: LGPL-2.1-only
-
-CURDIR=$(dirname $0)/
-TESTDIR=$CURDIR/../../../
-
-TMPDIR=$(mktemp -d)
-
-#This is needed since the testpoint create a pipe with the consumerd type suffixed
-TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
-TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
-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"
-TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
-
-NR_ITER=-1
-NR_USEC_WAIT=5
-
-SESSION_NAME="my_session"
-CHANNEL_NAME="my_channel"
-
-TRACE_PATH=$(mktemp -d)
-PAGE_SIZE=$(getconf PAGE_SIZE)
-
-DIR=$(readlink -f $TESTDIR)
-NUM_TESTS=104
-
-source $TESTDIR/utils/utils.sh
-
-function kernel_event_generator_toggle_state
-{
-       kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
-
-}
-function kernel_event_generator
-{
-       state_file=$1
-       kernel_event_generator_suspended=0
-       trap kernel_event_generator_toggle_state SIGUSR1
-
-       while (true); do
-               if [[ $kernel_event_generator_suspended -eq "1" ]]; then
-                       touch $state_file
-                       sleep 0.5
-               else
-                       if [[ -f $state_file ]]; then
-                               rm $state_file 2> /dev/null
-                       fi
-                       echo -n "1000" > /proc/lttng-test-filter-event 2> /dev/null
-               fi
-       done
-}
-
-function kernel_test
-{
-       local consumerd_pipe=()
-       local event_name="lttng_test_filter_event"
-
-       modprobe lttng-test
-
-       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
-
-       lttng_enable_kernel_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size=$PAGE_SIZE
-       enable_kernel_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
-
-       kernel_event_generator $TESTAPP_STATE_PATH &
-       APP_PID=$!
-
-       $CURDIR/notification LTTNG_DOMAIN_KERNEL $SESSION_NAME $CHANNEL_NAME $APP_PID $TESTAPP_STATE_PATH ${consumerd_pipe[@]}
-
-       destroy_lttng_session_notap $SESSION_NAME
-       stop_lttng_sessiond_notap
-
-       kill -9 $APP_PID
-       wait $APP_PID 2> /dev/null
-
-
-       rmmod lttng-test
-
-       rm -rf ${consumerd_pipe[@]} 2> /dev/null
-}
-
-if [ "$(id -u)" == "0" ]; then
-       validate_lttng_modules_present
-       kernel_test
-else
-       # Kernel tests are skipped.
-       plan_tests $NUM_TESTS
-       skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
-fi
-
-# Just in case cleanup
-rm -rf $TRACE_PATH
-rm -rf $TMPDIR
diff --git a/tests/regression/tools/notification/test_notification_kernel_buffer_usage b/tests/regression/tools/notification/test_notification_kernel_buffer_usage
new file mode 100755 (executable)
index 0000000..36a238c
--- /dev/null
@@ -0,0 +1,126 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+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"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+SYSCALL_TESTAPP_NAME="gen-syscall-events"
+SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME"
+
+USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary"
+USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME"
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+NUM_TESTS=104
+
+source $TESTDIR/utils/utils.sh
+
+function kernel_event_generator_toggle_state
+{
+       kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
+}
+
+function generate_filter_events
+{
+       /bin/echo -n "10" > /proc/lttng-test-filter-event 2> /dev/null
+}
+
+function kernel_event_generator
+{
+       command_to_run=$1
+       state_file=$2
+       kernel_event_generator_suspended=0
+       trap kernel_event_generator_toggle_state SIGUSR1
+
+       while (true); do
+               if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm $state_file 2> /dev/null
+                       fi
+                       $command_to_run
+               fi
+       done
+}
+
+function test_buffer_usage_notification
+{
+       local consumerd_pipe=$1
+       local event_name="lttng_test_filter_event"
+
+       create_lttng_session_notap $SESSION_NAME $TRACE_PATH
+
+       lttng_enable_kernel_channel_notap $SESSION_NAME $CHANNEL_NAME \
+               --subbuf-size=$PAGE_SIZE
+       enable_kernel_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME
+
+       kernel_event_generator generate_filter_events $TESTAPP_STATE_PATH &
+       APP_PID=$!
+
+       $CURDIR/notification 2 LTTNG_DOMAIN_KERNEL $APP_PID $TESTAPP_STATE_PATH \
+               $SESSION_NAME $CHANNEL_NAME ${consumerd_pipe[@]}
+
+       destroy_lttng_session_notap $SESSION_NAME
+
+       kill -9 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+       consumerd_pipe=()
+
+       validate_lttng_modules_present
+
+       # 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
+
+       modprobe lttng-test
+
+       LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \
+               CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \
+               LD_PRELOAD=${TESTPOINT}"
+
+       start_lttng_sessiond_notap
+
+       test_buffer_usage_notification $consumerd_pipe
+
+       stop_lttng_sessiond_notap
+       rmmod lttng-test
+
+       rm -rf ${consumerd_pipe[@]} 2> /dev/null
+else
+       # Kernel tests are skipped.
+       plan_tests $NUM_TESTS
+       skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf $TRACE_PATH
+rm -rf $TMPDIR
diff --git a/tests/regression/tools/notification/test_notification_kernel_error b/tests/regression/tools/notification/test_notification_kernel_error
new file mode 100755 (executable)
index 0000000..fca7ef5
--- /dev/null
@@ -0,0 +1,115 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+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"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+SYSCALL_TESTAPP_NAME="gen-syscall-events"
+SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME"
+
+USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary"
+USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME"
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+NUM_TESTS=104
+
+source $TESTDIR/utils/utils.sh
+
+function kernel_event_generator_toggle_state
+{
+       kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
+}
+
+function generate_filter_events
+{
+       /bin/echo -n "10" > /proc/lttng-test-filter-event 2> /dev/null
+}
+
+function kernel_event_generator
+{
+       command_to_run=$1
+       state_file=$2
+       kernel_event_generator_suspended=0
+       trap kernel_event_generator_toggle_state SIGUSR1
+
+       while (true); do
+               if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm $state_file 2> /dev/null
+                       fi
+                       $command_to_run
+               fi
+       done
+}
+
+function test_basic_error_path
+{
+       kernel_event_generator generate_filter_events $TESTAPP_STATE_PATH &
+       APP_PID=$!
+
+       $CURDIR/notification 1 LTTNG_DOMAIN_KERNEL $APP_PID $TESTAPP_STATE_PATH
+
+       kill -9 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+
+if [ "$(id -u)" == "0" ]; then
+       consumerd_pipe=()
+
+       validate_lttng_modules_present
+
+       # 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
+
+       modprobe lttng-test
+
+       LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \
+               CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \
+               LD_PRELOAD=${TESTPOINT}"
+
+       start_lttng_sessiond_notap
+
+       test_basic_error_path
+
+       stop_lttng_sessiond_notap
+       rmmod lttng-test
+
+       rm -rf ${consumerd_pipe[@]} 2> /dev/null
+else
+       # Kernel tests are skipped.
+       plan_tests $NUM_TESTS
+       skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf $TRACE_PATH
+rm -rf $TMPDIR
diff --git a/tests/regression/tools/notification/test_notification_kernel_instrumentation b/tests/regression/tools/notification/test_notification_kernel_instrumentation
new file mode 100755 (executable)
index 0000000..236775a
--- /dev/null
@@ -0,0 +1,125 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+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"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+SYSCALL_TESTAPP_NAME="gen-syscall-events"
+SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME"
+
+USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary"
+USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME"
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+NUM_TESTS=104
+
+source $TESTDIR/utils/utils.sh
+
+function kernel_event_generator_toggle_state
+{
+       kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
+}
+
+function generate_filter_events
+{
+       /bin/echo -n "10" > /proc/lttng-test-filter-event 2> /dev/null
+}
+
+function generate_syscalls
+{
+       # Pass /dev/null so to generate the syscall right away.
+       $SYSCALL_TESTAPP_BIN /dev/null
+}
+
+function userspace_probe_testapp
+{
+        $USERSPACE_PROBE_ELF_TESTAPP_BIN
+}
+
+function kernel_event_generator
+{
+       command_to_run=$1
+       state_file=$2
+       kernel_event_generator_suspended=0
+       trap kernel_event_generator_toggle_state SIGUSR1
+
+       while (true); do
+               if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm $state_file 2> /dev/null
+                       fi
+                       $command_to_run
+               fi
+       done
+}
+
+function test_kernel_instrumentation_notification
+{
+       kernel_event_generator generate_filter_events $TESTAPP_STATE_PATH &
+       APP_PID=$!
+
+       $CURDIR/notification 4 LTTNG_DOMAIN_KERNEL $APP_PID $TESTAPP_STATE_PATH
+
+       kill -9 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+       consumerd_pipe=()
+
+       validate_lttng_modules_present
+
+       # 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
+
+       modprobe lttng-test
+
+       LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \
+               CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \
+               LD_PRELOAD=${TESTPOINT}"
+
+       start_lttng_sessiond_notap
+
+       test_kernel_instrumentation_notification
+
+       stop_lttng_sessiond_notap
+       rmmod lttng-test
+
+       rm -rf ${consumerd_pipe[@]} 2> /dev/null
+else
+       # Kernel tests are skipped.
+       plan_tests $NUM_TESTS
+       skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf $TRACE_PATH
+rm -rf $TMPDIR
diff --git a/tests/regression/tools/notification/test_notification_kernel_syscall b/tests/regression/tools/notification/test_notification_kernel_syscall
new file mode 100755 (executable)
index 0000000..05f9e27
--- /dev/null
@@ -0,0 +1,125 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+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"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+SYSCALL_TESTAPP_NAME="gen-syscall-events"
+SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME"
+
+USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary"
+USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME"
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+NUM_TESTS=104
+
+source $TESTDIR/utils/utils.sh
+
+function kernel_event_generator_toggle_state
+{
+       kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
+}
+
+function generate_filter_events
+{
+       /bin/echo -n "10" > /proc/lttng-test-filter-event 2> /dev/null
+}
+
+function generate_syscalls
+{
+       # Pass /dev/null so to generate the syscall right away.
+       $SYSCALL_TESTAPP_BIN /dev/null
+}
+
+function userspace_probe_testapp
+{
+        $USERSPACE_PROBE_ELF_TESTAPP_BIN
+}
+
+function kernel_event_generator
+{
+       command_to_run=$1
+       state_file=$2
+       kernel_event_generator_suspended=0
+       trap kernel_event_generator_toggle_state SIGUSR1
+
+       while (true); do
+               if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm $state_file 2> /dev/null
+                       fi
+                       $command_to_run
+               fi
+       done
+}
+
+function test_kernel_syscall_notification
+{
+       kernel_event_generator generate_syscalls $TESTAPP_STATE_PATH &
+       APP_PID=$!
+
+       $CURDIR/notification 5 LTTNG_DOMAIN_KERNEL $APP_PID $TESTAPP_STATE_PATH
+
+       kill -9 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+       consumerd_pipe=()
+
+       validate_lttng_modules_present
+
+       # 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
+
+       modprobe lttng-test
+
+       LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \
+               CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \
+               LD_PRELOAD=${TESTPOINT}"
+
+       start_lttng_sessiond_notap
+
+       test_kernel_syscall_notification
+
+       stop_lttng_sessiond_notap
+       rmmod lttng-test
+
+       rm -rf ${consumerd_pipe[@]} 2> /dev/null
+else
+       # Kernel tests are skipped.
+       plan_tests $NUM_TESTS
+       skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf $TRACE_PATH
+rm -rf $TMPDIR
diff --git a/tests/regression/tools/notification/test_notification_kernel_userspace_probe b/tests/regression/tools/notification/test_notification_kernel_userspace_probe
new file mode 100755 (executable)
index 0000000..2536310
--- /dev/null
@@ -0,0 +1,116 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+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"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+SYSCALL_TESTAPP_NAME="gen-syscall-events"
+SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME"
+
+USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary"
+USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME"
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+NUM_TESTS=104
+
+source $TESTDIR/utils/utils.sh
+
+function kernel_event_generator_toggle_state
+{
+       kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
+}
+
+function userspace_probe_testapp
+{
+        $USERSPACE_PROBE_ELF_TESTAPP_BIN
+}
+
+function kernel_event_generator
+{
+       command_to_run=$1
+       state_file=$2
+       kernel_event_generator_suspended=0
+       trap kernel_event_generator_toggle_state SIGUSR1
+
+       while (true); do
+               if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm $state_file 2> /dev/null
+                       fi
+                       $command_to_run
+               fi
+       done
+}
+
+function test_kernel_userspace_probe_notification
+{
+       kernel_event_generator userspace_probe_testapp $TESTAPP_STATE_PATH &
+       APP_PID=$!
+
+       $CURDIR/notification 6 LTTNG_DOMAIN_KERNEL \
+               $APP_PID $TESTAPP_STATE_PATH \
+               $USERSPACE_PROBE_ELF_TESTAPP_BIN "test_function"
+
+       kill -9 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+       consumerd_pipe=()
+
+       validate_lttng_modules_present
+
+       # 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
+
+       modprobe lttng-test
+
+       LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \
+               CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \
+               LD_PRELOAD=${TESTPOINT}"
+
+       start_lttng_sessiond_notap
+
+       test_kernel_userspace_probe_notification
+
+       stop_lttng_sessiond_notap
+       rmmod lttng-test
+
+       rm -rf ${consumerd_pipe[@]} 2> /dev/null
+else
+       # Kernel tests are skipped.
+       plan_tests $NUM_TESTS
+       skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf $TRACE_PATH
+rm -rf $TMPDIR
diff --git a/tests/regression/tools/notification/test_notification_ust b/tests/regression/tools/notification/test_notification_ust
deleted file mode 100755 (executable)
index ad8b0b0..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
-#
-# SPDX-License-Identifier: LGPL-2.1-only
-
-CURDIR=$(dirname $0)/
-TESTDIR=$CURDIR/../../../
-
-TMPDIR=$(mktemp -d)
-
-#This is needed since the testpoint create a pipe with the consumerd type suffixed
-TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
-TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
-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"
-TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
-
-NR_ITER=1000
-NR_USEC_WAIT=5
-
-SESSION_NAME="my_session"
-CHANNEL_NAME="my_channel"
-
-TRACE_PATH=$(mktemp -d)
-PAGE_SIZE=$(getconf PAGE_SIZE)
-
-DIR=$(readlink -f $TESTDIR)
-
-
-source $TESTDIR/utils/utils.sh
-
-function ust_event_generator_toggle_state
-{
-       ust_event_generator_suspended=$((ust_event_generator_suspended==0))
-
-}
-function ust_event_generator
-{
-       state_file=$1
-       ust_event_generator_suspended=0
-       trap ust_event_generator_toggle_state SIGUSR1
-
-       while (true); do
-               if [[ $ust_event_generator_suspended -eq "1" ]]; then
-                       touch $state_file
-                       sleep 0.5
-               else
-                       if [[ -f $state_file ]]; then
-                               rm -rf $state_file 2> /dev/null
-                       fi
-                       taskset  -c 0 $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
-               fi
-       done
-}
-
-consumerd_pipe=()
-file_sync_after_first_event=$(mktemp -u)
-event_name="tp:tptest"
-
-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=$PAGE_SIZE
-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
-
-
-ust_event_generator $TESTAPP_STATE_PATH &
-APP_PID=$!
-
-$CURDIR/notification LTTNG_DOMAIN_UST $SESSION_NAME $CHANNEL_NAME $APP_PID $TESTAPP_STATE_PATH ${consumerd_pipe[@]}
-
-destroy_lttng_session_notap $SESSION_NAME
-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
-
-rm -rf $TMPDIR
-
-# Just in case cleanup
-rm -rf $TRACE_PATH
diff --git a/tests/regression/tools/notification/test_notification_ust_buffer_usage b/tests/regression/tools/notification/test_notification_ust_buffer_usage
new file mode 100755 (executable)
index 0000000..8488dce
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so)
+
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+
+GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events"
+GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME"
+
+GEN_UST_NEVENTS_TESTAPP_NAME="gen-ust-nevents"
+GEN_UST_NEVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_NEVENTS_TESTAPP_NAME/$GEN_UST_NEVENTS_TESTAPP_NAME"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NR_ITER=5
+NR_USEC_WAIT=5
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+
+
+source $TESTDIR/utils/utils.sh
+
+function ust_event_generator_toggle_state
+{
+       ust_event_generator_suspended=$((ust_event_generator_suspended==0))
+
+}
+function ust_event_generator
+{
+       test_app=$1
+       state_file=$2
+       ust_event_generator_suspended=0
+       trap ust_event_generator_toggle_state SIGUSR1
+
+       while (true); do
+               if [[ $ust_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm -rf $state_file 2> /dev/null
+                       fi
+                       taskset  -c 0 $test_app -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
+               fi
+       done
+}
+
+function test_buffer_usage_notification
+{
+       consumerd_pipe=()
+       file_sync_after_first_event=$(mktemp -u)
+       event_name="tp:tptest"
+
+       create_lttng_session_notap $SESSION_NAME $TRACE_PATH
+
+       enable_ust_lttng_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size=$PAGE_SIZE
+       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
+
+       ust_event_generator $GEN_UST_EVENTS_TESTAPP_BIN $TESTAPP_STATE_PATH &
+       APP_PID=$!
+
+       $CURDIR/notification 2 LTTNG_DOMAIN_UST $APP_PID $TESTAPP_STATE_PATH $SESSION_NAME $CHANNEL_NAME ${consumerd_pipe[@]}
+
+       destroy_lttng_session_notap $SESSION_NAME
+
+       # 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
+}
+
+LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}"
+start_lttng_sessiond_notap
+
+test_buffer_usage_notification
+
+stop_lttng_sessiond_notap
+
+rm -rf $TMPDIR
diff --git a/tests/regression/tools/notification/test_notification_ust_error b/tests/regression/tools/notification/test_notification_ust_error
new file mode 100755 (executable)
index 0000000..9b90b7e
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so)
+
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+
+GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events"
+GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME"
+
+GEN_UST_NEVENTS_TESTAPP_NAME="gen-ust-nevents"
+GEN_UST_NEVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_NEVENTS_TESTAPP_NAME/$GEN_UST_NEVENTS_TESTAPP_NAME"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NR_ITER=5
+NR_USEC_WAIT=5
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+
+
+source $TESTDIR/utils/utils.sh
+
+function ust_event_generator_toggle_state
+{
+       ust_event_generator_suspended=$((ust_event_generator_suspended==0))
+
+}
+function ust_event_generator
+{
+       test_app=$1
+       state_file=$2
+       ust_event_generator_suspended=0
+       trap ust_event_generator_toggle_state SIGUSR1
+
+       while (true); do
+               if [[ $ust_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm -rf $state_file 2> /dev/null
+                       fi
+                       taskset  -c 0 $test_app -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
+               fi
+       done
+}
+
+function test_basic_error_path
+{
+       ust_event_generator $GEN_UST_EVENTS_TESTAPP_BIN $TESTAPP_STATE_PATH &
+       APP_PID=$!
+
+       $CURDIR/notification 1 LTTNG_DOMAIN_UST $APP_PID $TESTAPP_STATE_PATH
+
+       kill -9 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}"
+start_lttng_sessiond_notap
+
+test_basic_error_path
+
+stop_lttng_sessiond_notap
+
+rm -rf $TMPDIR
diff --git a/tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion b/tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion
new file mode 100755 (executable)
index 0000000..8f2ed73
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so)
+
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+
+GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events"
+GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME"
+
+GEN_UST_NEVENTS_TESTAPP_NAME="gen-ust-nevents"
+GEN_UST_NEVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_NEVENTS_TESTAPP_NAME/$GEN_UST_NEVENTS_TESTAPP_NAME"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NR_ITER=5
+NR_USEC_WAIT=5
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+
+
+source $TESTDIR/utils/utils.sh
+
+function ust_event_generator_toggle_state
+{
+       ust_event_generator_suspended=$((ust_event_generator_suspended==0))
+
+}
+function ust_event_generator
+{
+       test_app=$1
+       state_file=$2
+       ust_event_generator_suspended=0
+       trap ust_event_generator_toggle_state SIGUSR1
+
+       while (true); do
+               if [[ $ust_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm -rf $state_file 2> /dev/null
+                       fi
+                       taskset  -c 0 $test_app -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
+               fi
+       done
+}
+
+function test_event_rule_condition_exclusion_notification
+{
+       ust_event_generator $GEN_UST_NEVENTS_TESTAPP_BIN $TESTAPP_STATE_PATH &
+       APP_PID=$!
+
+       $CURDIR/.libs/notification 3 LTTNG_DOMAIN_UST $APP_PID $TESTAPP_STATE_PATH
+
+       kill -9 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}"
+start_lttng_sessiond_notap
+
+test_event_rule_condition_exclusion_notification
+
+stop_lttng_sessiond_notap
+
+rm -rf $TMPDIR
index 03c446fcb498c7f50309ecc21e35268d178e98f5..1e19f5be34580f53bbc6e17ec875f05ceb6bcfb2 100644 (file)
@@ -22,7 +22,7 @@
 
 const char *session_name;
 
-bool schedules_equal(const struct lttng_rotation_schedule *a,
+static bool schedules_equal(const struct lttng_rotation_schedule *a,
                const struct lttng_rotation_schedule *b)
 {
        bool equal = false;
@@ -84,7 +84,7 @@ end:
        return equal;
 }
 
-void test_add_null_session(void)
+static void test_add_null_session(void)
 {
        enum lttng_rotation_status status;
        struct lttng_rotation_schedule *size_schedule = NULL;
@@ -97,7 +97,7 @@ void test_add_null_session(void)
        lttng_rotation_schedule_destroy(size_schedule);
 }
 
-void test_add_null_schedule(void)
+static void test_add_null_schedule(void)
 {
        enum lttng_rotation_status status;
 
@@ -106,7 +106,7 @@ void test_add_null_schedule(void)
                        "NULL schedule rejected by lttng_session_add_rotation_schedule()");
 }
 
-void test_add_uninitialized_schedule(void)
+static void test_add_uninitialized_schedule(void)
 {
        enum lttng_rotation_status status;
        struct lttng_rotation_schedule *size_schedule = NULL,
@@ -132,7 +132,7 @@ void test_add_uninitialized_schedule(void)
        lttng_rotation_schedule_destroy(periodic_schedule);
 }
 
-void test_remove_null_session(void)
+static void test_remove_null_session(void)
 {
        enum lttng_rotation_status status;
        struct lttng_rotation_schedule *size_schedule = NULL;
@@ -145,7 +145,7 @@ void test_remove_null_session(void)
        lttng_rotation_schedule_destroy(size_schedule);
 }
 
-void test_remove_null_schedule(void)
+static void test_remove_null_schedule(void)
 {
        enum lttng_rotation_status status;
 
@@ -154,7 +154,7 @@ void test_remove_null_schedule(void)
                        "NULL schedule rejected by lttng_session_remove_rotation_schedule()");
 }
 
-void test_remove_uninitialized_schedule(void)
+static void test_remove_uninitialized_schedule(void)
 {
        enum lttng_rotation_status status;
        struct lttng_rotation_schedule *size_schedule = NULL,
@@ -176,7 +176,7 @@ void test_remove_uninitialized_schedule(void)
        lttng_rotation_schedule_destroy(periodic_schedule);
 }
 
-void test_uninitialized_schedule_get(void)
+static void test_uninitialized_schedule_get(void)
 {
        uint64_t value;
        enum lttng_rotation_status status;
@@ -200,7 +200,7 @@ void test_uninitialized_schedule_get(void)
 
 }
 
-void test_add_list_remove_schedule(
+static void test_add_list_remove_schedule(
                const struct lttng_rotation_schedule *original_schedule)
 {
        int ret;
@@ -249,7 +249,7 @@ void test_add_list_remove_schedule(
 
 }
 
-void test_add_list_remove_size_schedule(void)
+static void test_add_list_remove_size_schedule(void)
 {
        struct lttng_rotation_schedule *size_schedule;
 
@@ -261,7 +261,7 @@ void test_add_list_remove_size_schedule(void)
        lttng_rotation_schedule_destroy(size_schedule);
 }
 
-void test_add_list_remove_periodic_schedule(void)
+static void test_add_list_remove_periodic_schedule(void)
 {
        struct lttng_rotation_schedule *periodic_schedule;
 
index 3f7ee2555d87a4ceb2640f7bc2ac5bab51a4bd45..c2563afd867b64a42902ccc5e6b51444c4c183e5 100755 (executable)
@@ -358,7 +358,7 @@ function test_ust_local_snapshot_large_metadata ()
        enable_ust_lttng_event_ok $SESSION_NAME $LM_EVENT $CHANNEL_NAME
        start_lttng_tracing_ok $SESSION_NAME
        lttng_snapshot_add_output_ok $SESSION_NAME file://$TRACE_PATH
-       $LM_BIN 1 1
+       $LM_BIN --iter 1 --wait 1
        ok $? "Start application to trace"
        lttng_snapshot_record $SESSION_NAME
        stop_lttng_tracing_ok $SESSION_NAME
diff --git a/tests/regression/tools/trigger/Makefile.am b/tests/regression/tools/trigger/Makefile.am
new file mode 100644 (file)
index 0000000..ba55d31
--- /dev/null
@@ -0,0 +1,55 @@
+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 trigger
+
+if NO_SHARED
+
+CLEANFILES = libpause_consumer.so libpause_consumer.so.debug
+EXTRA_DIST = test_trigger_ust test_trigger_kernel base_client.c trigger.c consumer_testpoints.c
+
+else
+
+# In order to use testpoint, 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 \
+       $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
+       $(DL_LIBS)
+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)
+
+trigger_SOURCES = trigger.c
+trigger_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm
+
+noinst_SCRIPTS = test_trigger_ust test_trigger_kernel
+EXTRA_DIST = test_trigger_ust test_trigger_kernel \
+       test_add_trigger_cli \
+       test_list_triggers_cli \
+       test_remove_trigger_cli
+
+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/trigger/base_client.c b/tests/regression/tools/trigger/base_client.c
new file mode 100644 (file)
index 0000000..f5bd7f1
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * base_client.c
+ *
+ * Base client application for testing of LTTng trigger API
+ *
+ * Copyright 2019 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/start-session.h>
+#include <lttng/action/notify.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/domain.h>
+#include <lttng/trigger/trigger.h>
+#include <lttng/lttng-error.h>
+#include <lttng/endpoint.h>
+#include <lttng/notification/channel.h>
+#include <lttng/notification/notification.h>
+
+const char *session_name = NULL;
+enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
+const char *pattern = NULL;
+
+int parse_arguments(char **argv) {
+       const char *domain_type_string = NULL;
+
+       session_name = argv[1];
+       domain_type_string = argv[2];
+       pattern = argv[3];
+
+       /* 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 (!strcasecmp("LTTNG_DOMAIN_JUL", domain_type_string)) {
+               domain_type = LTTNG_DOMAIN_JUL;
+       }
+       if (!strcasecmp("LTTNG_DOMAIN_PYTHON", domain_type_string)) {
+               domain_type = LTTNG_DOMAIN_PYTHON;
+       }
+       if (!strcasecmp("LTTNG_DOMAIN_LOG4J", domain_type_string)) {
+               domain_type = LTTNG_DOMAIN_LOG4J;
+       }
+       if (domain_type == LTTNG_DOMAIN_NONE) {
+               printf("error: Unknown domain type\n");
+               goto error;
+       }
+
+       return 0;
+error:
+       return 1;
+}
+
+int main(int argc, char **argv)
+{
+       int ret = 0;
+       struct lttng_condition *condition = NULL;
+
+       enum lttng_event_rule_status event_rule_status;
+       struct lttng_event_rule *event_rule = NULL;
+
+       enum lttng_action_status action_status;
+       struct lttng_action *action = NULL;
+
+       enum lttng_notification_channel_status nc_status;
+       struct lttng_notification_channel *notification_channel = NULL;
+
+       const char* exclusions[] = { "sample_component:message2"};
+
+       struct lttng_trigger *trigger = NULL;
+
+       if (argc < 4) {
+               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;
+       }
+
+       event_rule = lttng_event_rule_tracepoint_create(domain_type);
+       if (!event_rule) {
+               printf("error: Could not create condition object\n");
+               ret = 1;
+               goto end;
+       }
+
+       event_rule_status = lttng_event_rule_tracepoint_set_pattern(event_rule, pattern);
+       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+               printf("error: Could not set pattern\n");
+               ret = 1;
+               goto end;
+       }
+
+       event_rule_status = lttng_event_rule_tracepoint_set_filter(event_rule, "message=='Hello World'");
+       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+               printf("error: Could not set pattern\n");
+               ret = 1;
+               goto end;
+       }
+
+       event_rule_status = lttng_event_rule_tracepoint_set_exclusions(event_rule, 1, exclusions);
+       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+               printf("error: Could not set exclusions\n");
+               ret = 1;
+               goto end;
+       }
+
+       condition = lttng_condition_event_rule_create(event_rule);
+       if (!condition) {
+               printf("error: Could not create condition\n");
+               ret = 1;
+               goto end;
+       }
+       /* Ownership was passed to condition */
+       event_rule = NULL;
+
+       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);
+
+       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;
+       }
+       /*
+        * 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;
+       }
+
+       for (;;) {
+               struct lttng_notification *notification;
+               enum lttng_notification_channel_status status;
+               const struct lttng_evaluation *notification_evaluation;
+               const struct lttng_condition *notification_condition;
+               const char *name;
+
+               /* 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);
+               switch (lttng_evaluation_get_type(notification_evaluation)) {
+               case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+                       lttng_evaluation_event_rule_get_trigger_name(notification_evaluation, &name);
+                       printf("Received nootification from trigger \"%s\"\n", name);
+                       break;
+               default:
+                       printf("error: Wrong notification evaluation type \n");
+                       break;
+
+               }
+
+               lttng_notification_destroy(notification);
+       }
+end:
+       if (trigger) {
+               lttng_unregister_trigger(trigger);
+       }
+       lttng_event_rule_destroy(event_rule);
+       lttng_trigger_destroy(trigger);
+       lttng_condition_destroy(condition);
+       lttng_action_destroy(action);
+       printf("exit: %d\n", ret);
+       return ret;
+}
diff --git a/tests/regression/tools/trigger/consumer_testpoints.c b/tests/regression/tools/trigger/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/trigger/test_add_trigger_cli b/tests/regression/tools/trigger/test_add_trigger_cli
new file mode 100755 (executable)
index 0000000..ff9bdbb
--- /dev/null
@@ -0,0 +1,330 @@
+#!/bin/bash
+#
+# Copyright (C) - 2020 EfficiOS, inc
+#
+# 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 the `lttng add-trigger` command line interface.
+
+CURDIR="$(dirname "$0")"
+TESTDIR="$CURDIR/../../.."
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+
+plan_tests 171
+
+FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}"
+
+# shellcheck disable=SC2119
+start_lttng_sessiond_notap
+
+tmp_stdout=$(mktemp -t test_parse_cli_trigger_stdout.XXXXXX)
+tmp_stderr=$(mktemp -t test_parse_cli_trigger_stderr.XXXXXX)
+uprobe_elf_binary="${TESTDIR}/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary"
+
+if [ "$(id -u)" == "0" ]; then
+       ist_root=1
+else
+       ist_root=0
+fi
+
+function test_success ()
+{
+       local test_name="$1"
+       shift
+
+       diag "${FULL_LTTNG_BIN} add-trigger $*"
+       "${FULL_LTTNG_BIN}" add-trigger "$@" > "${tmp_stdout}" 2> "${tmp_stderr}"
+       ok $? "${test_name}: exit code is 0"
+
+       diff -u "${tmp_stdout}" <(echo "Trigger registered successfully.")
+       ok $? "${test_name}: expected stdout"
+
+       diff -u "${tmp_stderr}" /dev/null
+       ok $? "${test_name}: expected stderr"
+}
+
+function test_failure ()
+{
+       local test_name="$1"
+       local error_msg="$2"
+
+       shift 2
+
+       diag "${FULL_LTTNG_BIN} add-trigger $*"
+       "${FULL_LTTNG_BIN}" add-trigger "$@" > "${tmp_stdout}" 2> "${tmp_stderr}"
+       isnt $? 0 "${test_name}: exit code is not 0"
+
+       diff -u "${tmp_stdout}" /dev/null
+       ok $? "${test_name}: expected stdout"
+
+       diff -u "${tmp_stderr}" <(echo "${error_msg}")
+       ok $? "${test_name}: expected stderr"
+}
+
+# top-level options
+test_success "explicit id" \
+       --id hohoho \
+       --condition on-event some-event-id -u \
+       --action notify
+
+# `--condition on-event` successes
+test_success "--condition on-event some-event -u" \
+       --condition on-event some-event -u \
+       --action notify
+
+test_success "--condition on-event -a -u" \
+       --condition on-event -a -u \
+       --action notify
+
+test_success "--fire-once-after" \
+       --condition on-event -u test-fire-once-after \
+       --action notify \
+       --fire-once-after=55
+
+test_success "--fire-every" \
+       --condition on-event -u test-fire-every \
+       --action notify \
+       --fire-every=55
+
+skip $ist_root "non-root user: skipping kprobe tests" 12 || {
+       test_success "--condition on-event probe by symbol" \
+               --condition on-event -k --probe=do_sys_open my_sys_open \
+               --action notify
+
+       test_success "--condition on-event probe by symbol with offset" \
+               --condition on-event -k --probe=do_sys_open+10 my_sys_open \
+               --action notify
+
+       sys_open_addr=$(grep ' T do_sys_open$' /proc/kallsyms | cut -f 1 -d ' ')
+
+       test_success "--condition on-event probe by address" \
+               --condition on-event -k "--probe=${sys_open_addr}" my_sys_open \
+               --action notify
+
+       test_success "--condition on-event probe by address with offset" \
+               --condition on-event -k "--probe=${sys_open_addr}+10" my_sys_open \
+               --action notify
+}
+
+skip $ist_root "non-root user: skipping uprobe tests" 6 || {
+       test_success "--condition on-event uprobe" \
+               --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe \
+               --action notify
+
+       test_success "--condition on-event uprobe with elf prefix" \
+               --condition on-event -k --userspace-probe=elf:${uprobe_elf_binary}:test_function ma-probe-2 \
+               --action notify
+}
+
+skip $ist_root "non-root user: skipping syscall tests" 9 || {
+       test_success "--condition on-event syscall" \
+               --condition on-event -k --syscall open \
+               --action notify
+
+       test_success "--condition on-event syscall -a" \
+               --condition on-event -k --syscall -a \
+               --action notify
+
+       test_success "--condition on-event syscall with filter" \
+               --condition on-event -k --syscall --filter 'a > 2' open \
+               --action notify
+}
+
+# `--action notify` successes
+test_success "--action notify" \
+       --condition on-event some-event-notify -u \
+       --action notify
+
+# `--action start-session` successes
+test_success "--action start-session" \
+       --condition on-event some-event-start-session -u \
+       --action start-session ze-session
+
+# `--action stop-session` successes
+test_success "--action stop-session foo" \
+       --condition on-event some-event-stop-session -u \
+       --action stop-session ze-session
+
+# `--action rotate-session` successes
+test_success "--action rotate-session foo" \
+       --condition on-event some-event-rotate-session -u \
+       --action rotate-session ze-session
+
+# `--action snapshot-session` successes
+test_success "--action snapshot-session foo" \
+       --condition on-event some-event-snapshot-session -u \
+       --action snapshot-session ze-session
+
+test_success "--action snapshot-session with file URI" \
+       --condition on-event some-event-snapshot-session2 -u \
+       --action snapshot-session ze-session /hello
+
+test_success "--action snapshot-session with net URI" \
+       --condition on-event some-event-snapshot-session3 -u \
+       --action snapshot-session ze-session net://1.2.3.4
+
+test_success "--action snapshot-session with ctrl/data URIs" \
+       --condition on-event some-event-snapshot-session4 -u \
+       --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1234 --data-url=tcp://1.2.3.4:1235
+
+# top-level failures
+test_failure "no args" "Missing --condition."
+
+test_failure "unknown option" \
+       "Unknown option \`--hello\`" \
+       --hello
+
+test_failure "missing --action" \
+       "Need at least one --action." \
+       --condition on-event hello -u
+
+test_failure "two --condition" \
+       "A --condition was already given." \
+       --condition on-event aaa -u \
+       --condition on-event bbb -u \
+       --action notify
+
+test_failure "missing argument to --id" \
+       "Error: While parsing argument #1 (\`--id\`): Missing required argument for option \`--id\`" \
+       --id
+
+for cmd in fire-once-after fire-every; do
+       test_failure "missing argument to --${cmd}" \
+               "Error: While parsing argument #1 (\`--${cmd}\`): Missing required argument for option \`--${cmd}\`" \
+               --condition on-event -u -a --action notify \
+               --${cmd}
+
+       test_failure "invalid argument to --${cmd}: non-digit character" \
+               "Failed to parse \`123bob\` as an integer." \
+               --condition on-event -u -a --action notify \
+               --${cmd} 123bob
+
+       test_failure "invalid argument to --${cmd}: empty string" \
+               "Failed to parse \`\` as an integer." \
+               --condition on-event -u -a --action notify \
+               --${cmd} ""
+done
+
+# `--condition` failures
+test_failure "missing args after --condition" \
+       "Missing condition name." \
+       --condition
+test_failure "unknown --condition" \
+       "Unknown condition name: zoofest" \
+       --condition zoofest
+
+# `--condition on-event` failures
+test_failure "missing args after --condition on-event" \
+       "Error: Need to provide either a tracepoint name or -a/--all." \
+       --condition on-event
+test_failure "missing domain in --condition on-event" \
+       "Error: Please specify a domain (-k/-u/-j)." \
+       --condition on-event -a
+test_failure "extra args after --condition on-event" \
+       "Error: Unexpected argument: bozo" \
+       --condition on-event foo -u bozo
+test_failure "--condition on-event: --all with --probe" \
+       "Error: Can't use -a/--all with event rule of type probe." \
+       --condition on-event --probe=do_sys_open --all
+test_failure "--condition on-event: missing tracepoint name with --probe" \
+       "Error: Need to provide either a tracepoint name or -a/--all." \
+       --condition on-event -k --probe do_sys_open
+
+test_failure "--condition on-event: missing tracepoint name with --userspace-probe" \
+       "Error: Need to provide either a tracepoint name or -a/--all." \
+       --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function
+
+test_failure "--condition on-event: extra argument with --userspace-probe" \
+       "Error: Unexpected argument: world" \
+       --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_failure hello world
+
+test_failure "--condition on-event: missing tracepoint name with --syscall" \
+       "Error: Need to provide either a tracepoint name or -a/--all." \
+       --condition on-event -k --syscall
+
+test_failure "--condition on-event: extra argument with --syscall" \
+       "Error: Unexpected argument: open" \
+       --condition on-event -k --syscall open open
+
+test_failure "--condition on-event: both -a and a tracepoint name with --syscall" \
+       "Error: Can't provide a tracepoint name with -a/--all." \
+       --condition on-event -k --syscall -a open
+
+# `--action` failures
+test_failure "missing args after --action" \
+       "Missing action name." \
+       --condition on-event -u -a \
+       --action
+
+# `--action notify` failures
+test_failure "extra arg after --action notify" \
+       "Unexpected argument \`bob\`." \
+       --condition on-event -u -a \
+       --action notify bob
+
+# `--action start-session` failures
+test_failure "missing arg after --action start-session" \
+       "Missing session name." \
+       --condition on-event some-event-start-session -u \
+       --action start-session
+test_failure "extra arg after --action start-session" \
+       "Unexpected argument \`bob\`." \
+       --condition on-event some-event-start-session -u \
+       --action start-session ze-session bob
+
+# `--action stop-session` failures
+test_failure "missing arg after --action stop-session" \
+       "Missing session name." \
+       --condition on-event some-event-stop-session -u \
+       --action stop-session
+test_failure "extra arg after --action stop-session" \
+       "Unexpected argument \`bob\`." \
+       --condition on-event some-event-stop-session -u \
+       --action stop-session ze-session bob
+
+# `--action rotate-session` failures
+test_failure "missing arg after --action rotate-session" \
+       "Missing session name." \
+       --condition on-event some-event-rotate-session -u \
+       --action rotate-session
+test_failure "extra arg after --action rotate-session" \
+       "Unexpected argument \`bob\`." \
+       --condition on-event some-event-rotate-session -u \
+       --action rotate-session ze-session bob
+
+# `--action snapshot-session` failures
+test_failure "missing arg after --action snapshot-session" \
+       "Missing session name." \
+       --condition on-event some-event-snapshot-session -u \
+       --action snapshot-session
+test_failure "extra arg after --action snapshot-session" \
+       "Unexpected argument \`bob\`." \
+       --condition on-event some-event-snapshot-session -u \
+       --action snapshot-session ze-session /dest bob
+test_failure "snapshot-session action, --max-size without destination" \
+       "Can't provide a snapshot output max size without a snapshot output destination." \
+       --condition on-event some-event-snapshot-session -u \
+       --action snapshot-session ze-session --max-size 10M
+test_failure "snapshot-session action, --name without destination" \
+       "Can't provide a snapshot output name without a snapshot output destination." \
+       --condition on-event some-event-snapshot-session -u \
+       --action snapshot-session ze-session --name hallo
+
+
+# Cleanup
+stop_lttng_sessiond_notap
+rm -f "${tmp_stdout}"
+rm -f "${tmp_stderr}"
diff --git a/tests/regression/tools/trigger/test_list_triggers_cli b/tests/regression/tools/trigger/test_list_triggers_cli
new file mode 100755 (executable)
index 0000000..202a7b7
--- /dev/null
@@ -0,0 +1,301 @@
+#!/bin/bash
+#
+# Copyright (C) - 2020 EfficiOS, inc
+#
+# 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 the `lttng list-trigger` command line interface.
+
+CURDIR="$(dirname "$0")"
+TESTDIR="$CURDIR/../../.."
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+
+plan_tests 41
+
+FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}"
+
+tmp_stdout=$(mktemp -t test_list_triggers_cli_stdout.XXXXXX)
+tmp_stderr=$(mktemp -t test_list_triggers_cli_stderr.XXXXXX)
+tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX)
+uprobe_elf_binary=$(realpath "${TESTDIR}/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary")
+
+if [ "$(id -u)" == "0" ]; then
+       ist_root=1
+else
+       ist_root=0
+fi
+
+function add_trigger ()
+{
+       "${FULL_LTTNG_BIN}" add-trigger "$@"
+       ok $? "add trigger \`$*\`: exit code is 0"
+}
+
+function list_triggers ()
+{
+       local test_name="$1"
+       local expected_stdout_file="$2"
+
+       "${FULL_LTTNG_BIN}" list-triggers > "${tmp_stdout}" 2> "${tmp_stderr}"
+       ok $? "${test_name}: exit code is 0"
+
+       diff -u "${expected_stdout_file}" "${tmp_stdout}"
+       ok $? "${test_name}: expected stdout"
+
+       diff -u /dev/null "${tmp_stderr}"
+       ok $? "${test_name}: expected stderr"
+}
+
+test_top_level_options ()
+{
+       # shellcheck disable=SC2119
+       start_lttng_sessiond_notap
+
+
+       add_trigger --id hello --condition on-event -u test-id --action notify
+       add_trigger --fire-once-after 123 --condition on-event -u test-fire-once-after --action notify
+       add_trigger --fire-every 124 --condition on-event -u test-fire-every --action notify
+
+       cat > "${tmp_expected_stdout}" <<- EOF
+       - id: T2
+         firing policy: once after 123 occurences
+         condition: event rule hit
+           rule: test-fire-once-after (type: tracepoint, domain: ust)
+         actions:
+           notify
+       - id: T3
+         firing policy: after every 124 occurences
+         condition: event rule hit
+           rule: test-fire-every (type: tracepoint, domain: ust)
+         actions:
+           notify
+       - id: hello
+         condition: event rule hit
+           rule: test-id (type: tracepoint, domain: ust)
+         actions:
+           notify
+       EOF
+
+       list_triggers "top level options" "${tmp_expected_stdout}"
+
+       stop_lttng_sessiond_notap
+}
+
+test_on_event_tracepoint ()
+{
+       # shellcheck disable=SC2119
+       start_lttng_sessiond_notap
+
+       add_trigger --condition on-event -u -a --action notify
+       add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify
+       add_trigger --condition on-event 'hello*' -u -x 'hello2,hello3,hello4' --action notify
+       add_trigger --id BCD --condition on-event -u gerboise --loglevel INFO --action notify
+       add_trigger --condition on-event -u lemming --loglevel-only WARNING --action notify
+
+       cat > "${tmp_expected_stdout}" <<- EOF
+       - id: ABC
+         condition: event rule hit
+           rule: aaa (type: tracepoint, domain: ust, filter: p == 2)
+         actions:
+           notify
+       - id: BCD
+         condition: event rule hit
+           rule: gerboise (type: tracepoint, domain: ust, log level <= TRACE_INFO)
+         actions:
+           notify
+       - id: T1
+         condition: event rule hit
+           rule: * (type: tracepoint, domain: ust)
+         actions:
+           notify
+       - id: T3
+         condition: event rule hit
+           rule: hello* (type: tracepoint, domain: ust, exclusions: hello2,hello3,hello4)
+         actions:
+           notify
+       - id: T5
+         condition: event rule hit
+           rule: lemming (type: tracepoint, domain: ust, log level == TRACE_WARNING)
+         actions:
+           notify
+       EOF
+
+       list_triggers "on-event, tracepoint event rule" "${tmp_expected_stdout}"
+
+       stop_lttng_sessiond_notap
+}
+
+test_on_event_probe ()
+{
+       local sys_open_addr
+
+       # shellcheck disable=SC2119
+       start_lttng_sessiond_notap
+
+       sys_open_addr=$(grep ' T do_sys_open$' /proc/kallsyms | cut -f 1 -d ' ')
+
+       add_trigger --condition on-event -k --probe=do_sys_open my_sys_open --action notify
+       add_trigger --condition on-event -k --probe=do_sys_open+10 my_sys_open --action notify
+       add_trigger --condition on-event -k --probe="${sys_open_addr}" my_sys_open --action notify
+       add_trigger --condition on-event -k --probe="${sys_open_addr}+10" my_sys_open --action notify
+
+       cat > "${tmp_expected_stdout}" <<- EOF
+       - id: T1
+         condition: event rule hit
+           rule: my_sys_open (type: probe, location: do_sys_open)
+         actions:
+           notify
+       - id: T2
+         condition: event rule hit
+           rule: my_sys_open (type: probe, location: do_sys_open+10)
+         actions:
+           notify
+       - id: T3
+         condition: event rule hit
+           rule: my_sys_open (type: probe, location: ${sys_open_addr})
+         actions:
+           notify
+       - id: T4
+         condition: event rule hit
+           rule: my_sys_open (type: probe, location: ${sys_open_addr}+10)
+         actions:
+           notify
+       EOF
+
+       list_triggers "on-event, probe event rule" "${tmp_expected_stdout}"
+
+       stop_lttng_sessiond_notap
+}
+
+test_on_event_userspace_probe ()
+{
+       # shellcheck disable=SC2119
+       start_lttng_sessiond_notap
+
+       add_trigger --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe --action notify
+
+       cat > "${tmp_expected_stdout}" <<- EOF
+       - id: T1
+         condition: event rule hit
+           rule: ma-probe (type: userspace probe, location: ${uprobe_elf_binary}:test_function)
+         actions:
+           notify
+       EOF
+
+       list_triggers "on-event, userspace-probe event rule" "${tmp_expected_stdout}"
+
+       stop_lttng_sessiond_notap
+}
+
+test_on_event_syscall ()
+{
+       # shellcheck disable=SC2119
+       start_lttng_sessiond_notap
+
+       add_trigger --condition on-event -k --syscall open --action notify
+       add_trigger --condition on-event -k --syscall ptrace --filter 'a > 2' --action notify
+
+       cat > "${tmp_expected_stdout}" <<- EOF
+       - id: T1
+         condition: event rule hit
+         - rule: open (type: syscall)
+         actions:
+           notify
+       - id: T2
+         condition: event rule hit
+         - rule: ptrace (type: syscall, filter: a > 2)
+         actions:
+           notify
+       EOF
+
+       list_triggers "on-event, syscall event rule" "${tmp_expected_stdout}"
+
+       stop_lttng_sessiond_notap
+}
+
+test_snapshot_action ()
+{
+       start_lttng_sessiond_notap
+
+       add_trigger --condition on-event -u some-event --action snapshot-session ze-session
+       add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path
+       add_trigger --condition on-event -u some-event --action snapshot-session ze-session file:///some/other/path
+       add_trigger --condition on-event -u some-event --action snapshot-session ze-session net://1.2.3.4
+       add_trigger --condition on-event -u some-event --action snapshot-session ze-session net://1.2.3.4:1234:1235
+       add_trigger --condition on-event -u some-event --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1111 --data-url=tcp://1.2.3.4:1112
+       add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path --max-size=1234
+       add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path --name=meh
+
+
+       cat > "${tmp_expected_stdout}" <<- EOF
+       - id: T1
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`
+       - id: T2
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, path: /some/path
+       - id: T3
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, path: /some/other/path
+       - id: T4
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, url: net://1.2.3.4
+       - id: T5
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, url: net://1.2.3.4:1234:1235
+       - id: T6
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, control url: tcp://1.2.3.4:1111, data url: tcp://1.2.3.4:1112
+       - id: T7
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, path: /some/path, max size: 1234
+       - id: T8
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, path: /some/path, name: meh
+       EOF
+
+       list_triggers "snapshot action" "${tmp_expected_stdout}"
+
+       stop_lttng_sessiond_notap
+}
+
+test_top_level_options
+test_on_event_tracepoint
+skip $ist_root "non-root user: skipping kprobe tests" 7 || test_on_event_probe
+skip $ist_root "non-root user: skipping uprobe tests" 4 || test_on_event_userspace_probe
+skip $ist_root "non-root user: skipping syscall tests" 5 || test_on_event_syscall
+test_snapshot_action
+
+# Cleanup
+rm -f "${tmp_stdout}"
+rm -f "${tmp_stderr}"
+rm -f "${tmp_expected_stdout}"
diff --git a/tests/regression/tools/trigger/test_remove_trigger_cli b/tests/regression/tools/trigger/test_remove_trigger_cli
new file mode 100755 (executable)
index 0000000..4b3cce5
--- /dev/null
@@ -0,0 +1,110 @@
+#!/bin/bash
+#
+# Copyright (C) - 2020 EfficiOS, inc
+#
+# 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 the `lttng remove-trigger` command line interface.
+
+CURDIR="$(dirname "$0")"
+TESTDIR="$CURDIR/../../.."
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+
+plan_tests 17
+
+FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}"
+
+tmp_stdout=$(mktemp -t test_list_triggers_cli_stdout.XXXXXX)
+tmp_stderr=$(mktemp -t test_list_triggers_cli_stderr.XXXXXX)
+tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX)
+
+function add_trigger ()
+{
+       "${FULL_LTTNG_BIN}" add-trigger "$@"
+       ok $? "add trigger \`$*\`: exit code is 0"
+}
+
+function list_triggers ()
+{
+       local test_name="$1"
+       local expected_stdout_file="$2"
+
+       "${FULL_LTTNG_BIN}" list-triggers > "${tmp_stdout}" 2> "${tmp_stderr}"
+       ok $? "${test_name}: exit code is 0"
+
+       diff -u "${expected_stdout_file}" "${tmp_stdout}"
+       ok $? "${test_name}: expected stdout"
+
+       diff -u /dev/null "${tmp_stderr}"
+       ok $? "${test_name}: expected stderr"
+}
+
+function remove_trigger ()
+{
+       local id="$1"
+       local test_name="remove trigger ${id}"
+
+       "${FULL_LTTNG_BIN}" remove-trigger "${id}" > "${tmp_stdout}" 2> "${tmp_stderr}"
+       ok $? "${test_name}: exit code is 0"
+
+       diff -u <(echo "Removed trigger \`${id}\`.") "${tmp_stdout}"
+       ok $? "${test_name}: expected stdout"
+
+       diff -u /dev/null "${tmp_stderr}"
+       ok $? "${test_name}: expected stderr"
+}
+
+# shellcheck disable=SC2119
+start_lttng_sessiond_notap
+
+# Add a few triggers
+add_trigger --condition on-event -u -a --action notify
+add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify
+
+cat > "${tmp_expected_stdout}" <<- EOF
+- id: ABC
+  condition: event rule hit
+    rule: aaa (type: tracepoint, domain: ust, filter: p == 2)
+  actions:
+    notify
+- id: T1
+  condition: event rule hit
+    rule: * (type: tracepoint, domain: ust)
+  actions:
+    notify
+EOF
+list_triggers "two triggers left" "${tmp_expected_stdout}"
+
+remove_trigger "ABC"
+
+cat > "${tmp_expected_stdout}" <<- EOF
+- id: T1
+  condition: event rule hit
+    rule: * (type: tracepoint, domain: ust)
+  actions:
+    notify
+EOF
+list_triggers "one trigger left" "${tmp_expected_stdout}"
+
+remove_trigger "T1"
+
+list_triggers "no triggers left" "/dev/null"
+
+# Cleanup
+stop_lttng_sessiond_notap
+rm -f "${tmp_stdout}"
+rm -f "${tmp_stderr}"
+rm -f "${tmp_expected_stdout}"
diff --git a/tests/regression/tools/trigger/test_trigger_kernel b/tests/regression/tools/trigger/test_trigger_kernel
new file mode 100755 (executable)
index 0000000..cc6fc58
--- /dev/null
@@ -0,0 +1,121 @@
+#!/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/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/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"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NR_ITER=-1
+NR_USEC_WAIT=5
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+NUM_TESTS=104
+
+source $TESTDIR/utils/utils.sh
+
+function kernel_event_generator_toogle_state
+{
+       kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
+
+}
+function kernel_event_generator
+{
+       state_file=$1
+       kernel_event_generator_suspended=0
+       trap kernel_event_generator_toogle_state SIGUSR1
+
+       while (true); do
+               if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm $state_file 2> /dev/null
+                       fi
+                       echo -n "1000" > /proc/lttng-test-filter-event 2> /dev/null
+               fi
+       done
+}
+
+function kernel_test
+{
+       local consumerd_pipe=()
+       local event_name="lttng_test_filter_event"
+
+       modprobe lttng-test
+
+       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
+
+       lttng_enable_kernel_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size=$PAGE_SIZE
+       enable_kernel_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
+
+       kernel_event_generator $TESTAPP_STATE_PATH &
+       APP_PID=$!
+
+       $CURDIR/notification LTTNG_DOMAIN_KERNEL $SESSION_NAME $CHANNEL_NAME $APP_PID $TESTAPP_STATE_PATH ${consumerd_pipe[@]}
+
+       destroy_lttng_session_notap $SESSION_NAME
+       stop_lttng_sessiond_notap
+
+       kill -9 $APP_PID
+       wait $APP_PID 2> /dev/null
+
+
+       rmmod lttng-test
+
+       rm -rf ${consumerd_pipe[@]} 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+       validate_lttng_modules_present
+       kernel_test
+else
+       # Kernel tests are skipped.
+       plan_tests $NUM_TESTS
+       skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf $TRACE_PATH
+rm -rf $TMPDIR
diff --git a/tests/regression/tools/trigger/test_trigger_ust b/tests/regression/tools/trigger/test_trigger_ust
new file mode 100755 (executable)
index 0000000..82f79a8
--- /dev/null
@@ -0,0 +1,107 @@
+#!/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/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/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"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NR_ITER=1000
+NR_USEC_WAIT=5
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+
+
+source $TESTDIR/utils/utils.sh
+
+function ust_event_generator_toogle_state
+{
+       ust_event_generator_suspended=$((ust_event_generator_suspended==0))
+
+}
+function ust_event_generator
+{
+       state_file=$1
+       ust_event_generator_suspended=0
+       trap ust_event_generator_toogle_state SIGUSR1
+
+       while (true); do
+               if [[ $ust_event_generator_suspended -eq "1" ]]; then
+                       touch $state_file
+                       sleep 0.5
+               else
+                       if [[ -f $state_file ]]; then
+                               rm -rf $state_file 2> /dev/null
+                       fi
+                       taskset  -c 0 $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT > /dev/null 2>&1
+               fi
+       done
+}
+
+consumerd_pipe=()
+file_sync_after_first_event=$(mktemp -u)
+event_name="tp:tptest"
+
+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=$PAGE_SIZE
+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
+
+
+ust_event_generator $TESTAPP_STATE_PATH &
+APP_PID=$!
+
+$CURDIR/notification LTTNG_DOMAIN_UST $SESSION_NAME $CHANNEL_NAME $APP_PID $TESTAPP_STATE_PATH ${consumerd_pipe[@]}
+
+destroy_lttng_session_notap $SESSION_NAME
+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
+
+rm -rf $TMPDIR
+
+# Just in case cleanup
+rm -rf $TRACE_PATH
diff --git a/tests/regression/tools/trigger/trigger.c b/tests/regression/tools/trigger/trigger.c
new file mode 100644 (file)
index 0000000..88ef99d
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+ * 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 <signal.h>
+#include <errno.h>
+#include <poll.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 <lttng/lttng.h>
+
+#include <tap/tap.h>
+
+#define NUM_TESTS 104
+
+int nb_args = 0;
+int named_pipe_args_start = 0;
+pid_t app_pid = -1;
+const char *app_state_file = NULL;
+
+static
+void wait_on_file(const char *path, bool file_exist)
+{
+       if (!path) {
+               return;
+       }
+       for (;;) {
+               int ret;
+               struct stat buf;
+
+               ret = stat(path, &buf);
+               if (ret == -1 && errno == ENOENT) {
+                       if (file_exist) {
+                               (void) poll(NULL, 0, 10);       /* 10 ms delay */
+                               continue;                       /* retry */
+                       }
+                       break; /* File does not exist */
+               }
+               if (ret) {
+                       perror("stat");
+                       exit(EXIT_FAILURE);
+               }
+               break;  /* found */
+       }
+}
+
+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");
+               if (close(fd)) {
+                       perror("Named pipe close 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;
+}
+
+int suspend_application()
+{
+       int ret;
+       struct stat buf;
+
+       if (!stat(app_state_file, &buf)) {
+               fail("App is already in a suspended state.");
+               ret = -1;
+               goto error;
+       }
+
+       /*
+        * Send SIGUSR1 to application instructing it to bypass tracepoint.
+        */
+       ret = kill(app_pid, SIGUSR1);
+       if (ret) {
+               fail("SIGUSR1 failed. errno %d", errno);
+               ret = -1;
+               goto error;
+       }
+
+       wait_on_file(app_state_file, true);
+
+error:
+       return ret;
+
+}
+
+int resume_application()
+{
+       int ret;
+       struct stat buf;
+
+       ret = stat(app_state_file, &buf);
+       if (ret == -1 && errno == ENOENT) {
+               fail("State file does not exist");
+               goto error;
+       }
+       if (ret) {
+               perror("stat");
+               goto error;
+       }
+
+       ret = kill(app_pid, SIGUSR1);
+       if (ret) {
+               fail("SIGUSR1 failed. errno %d", errno);
+               ret = -1;
+               goto error;
+       }
+
+       wait_on_file(app_state_file, false);
+
+error:
+       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);
+}
+
+static
+void wait_data_pending(const char *session_name)
+{
+       int ret;
+
+       do {
+               ret = lttng_data_pending(session_name);
+               assert(ret >= 0);
+       } while (ret != 0);
+}
+
+void test_notification_channel(const char *session_name, const char *channel_name, const 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 from an invalid condition");
+
+       nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, "Unsubscribing from a 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 */
+       stop_consumer(argv);
+       lttng_start_tracing(session_name);
+
+       /* 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;
+
+       suspend_application();
+       lttng_stop_tracing_no_wait(session_name);
+       resume_consumer(argv);
+       wait_data_pending(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 */
+       stop_consumer(argv);
+       resume_application();
+       lttng_start_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_HIGH,
+                       "High notification received after intermediary communication");
+       lttng_notification_destroy(notification);
+       notification = NULL;
+
+       suspend_application();
+       lttng_stop_tracing_no_wait(session_name);
+       resume_consumer(argv);
+       wait_data_pending(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(argv);
+       resume_application();
+       /* Stop consumer to force a high notification */
+       lttng_start_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_HIGH,
+                       "High notification");
+       lttng_notification_destroy(notification);
+       notification = NULL;
+
+       /* Resume consumer to allow event consumption */
+       suspend_application();
+       lttng_stop_tracing_no_wait(session_name);
+       resume_consumer(argv);
+       wait_data_pending(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(high_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 6 and upward are named pipe location for consumerd control */
+       named_pipe_args_start = 6;
+
+       if (argc < 7) {
+               fail("Missing parameter for tests to run %d", argc);
+               goto error;
+       }
+
+       nb_args = argc;
+
+       domain_type_string = argv[1];
+       session_name = argv[2];
+       channel_name = argv[3];
+       app_pid = (pid_t) atoi(argv[4]);
+       app_state_file = argv[5];
+
+       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();
+}
+
index b2c20bee3819cddde3ec29af1f82997b34efd4a4..dace1d99cba4fef0d7ccf86bd4a6b8a99782202f 100644 (file)
@@ -49,6 +49,7 @@ const char *plugin_description(void)
        return "Freeze time with 1KHz for regression test";
 }
 
+void lttng_ust_clock_plugin_init(void);
 void lttng_ust_clock_plugin_init(void)
 {
        int ret;
index 55c9d49bc47792cf0101e7d31dcc9829e71a5faa..7030a293d532db5d81cca4fa96c1948a938b7f55 100644 (file)
@@ -34,7 +34,7 @@ int main(int argc, char **argv, char *env[])
                return 1;
        }
        if (result == 0) {
-               char *args[] = { "fork2", NULL };
+               char *args[] = { (char *) "fork2", NULL };
 
                tracepoint(ust_tests_fork, after_fork_child, getpid());
 
index f1b70742591322cc8c50e021c52779bd89952a72..c387063110e9f586be5b9d452dbecd24a3daf84c 100644 (file)
@@ -18,7 +18,7 @@
 
 static long nprocessors;
 
-int plugin_getcpu(void)
+static int plugin_getcpu(void)
 {
        /* Generate a sequence based on the number of configurated processor
         * by using sequence[i] % nb_configured_processors. Where sequence
@@ -57,6 +57,7 @@ int plugin_getcpu(void)
        return ret;
 }
 
+void lttng_ust_getcpu_plugin_init(void);
 void lttng_ust_getcpu_plugin_init(void)
 {
        int ret;
index b59c9241f3bdc3e9b6dacda2eb364223ef97d169..fab14879097756df4e3a67940c334eb38969a212 100644 (file)
 #define TRACEPOINT_DEFINE
 #include "tp.h"
 
-void inthandler(int sig)
+static void inthandler(int sig)
 {
 }
 
-int init_int_handler(void)
+static int init_int_handler(void)
 {
        int result;
        struct sigaction act;
index 3fa8c9a2dd9e5f08c52cc918e61a3888e0b211a1..3ec3c40c199b0918607a68e8d64a5652e698b0d3 100644 (file)
@@ -14,7 +14,7 @@
 #include <lttng/tracepoint.h>
 
 TRACEPOINT_EVENT(tp, slow,
-       TP_ARGS(unsigned int, c, char *, thread_name),
+       TP_ARGS(unsigned int, c, const char *, thread_name),
        TP_FIELDS(
                ctf_integer(unsigned int, counter, c)
                ctf_string(th_name, thread_name)
index eaa6137085cbe133b22411bb09bfb99f9196e3ba..a67f102b498674b2b3eb92b6b0701e0c2cea6e23 100644 (file)
@@ -18,7 +18,9 @@
 #define VALUE (-1)
 #endif
 
-void call_tracepoint(void) {
+void call_tracepoint(void);
+void call_tracepoint(void)
+{
        tracepoint(multi, tp, VALUE);
 }
 
index 6a91089df27dc0c1281060d0e11a897354237efd..6ebb13d7a4c9eaa6fefa304d784af1e000dc14dc 100644 (file)
@@ -15,6 +15,7 @@
 #include "callsites.h"
 #endif
 
+void exec_callsite();
 void exec_callsite()
 {
 #if HAS_CALLSITES
@@ -22,7 +23,7 @@ void exec_callsite()
 #endif
 }
 
-void print_list(void)
+static void print_list(void)
 {
        fprintf(stderr, "Test list (-t X):\n");
        fprintf(stderr, "\t0: dlopen() all libraries pass in arguments and execute "
@@ -31,7 +32,8 @@ void print_list(void)
        fprintf(stderr, "\t2: simulate the upgrade of a library containing the callsites using dlopen() and dlclose(). \n");
 }
 
-int dl_open_all(int nb_libraries, char **libraries)
+#if HAS_CALLSITES
+static int dl_open_all(int nb_libraries, char **libraries)
 {
        int i, ret = 0;
        void **handles;
@@ -56,12 +58,14 @@ error:
        free(handles);
        return ret;
 }
+#endif
 
+#if HAS_CALLSITES
 /*
  * Takes 2 paths to libraries, dlopen() the first, trace, dlopen() the second,
  * and dlclose the first to simulate the upgrade of a library.
  */
-int upgrade_lib(int nb_libraries, char **libraries)
+static int upgrade_lib(int nb_libraries, char **libraries)
 {
        int i, ret = 0;
        void *handles[2];
@@ -92,12 +96,14 @@ int upgrade_lib(int nb_libraries, char **libraries)
 error:
        return ret;
 }
+#endif
 
+#if !HAS_CALLSITES
 /*
  * Simulate the upgrade of a library containing a callsite.
  * Receives two libraries containing callsites for the same tracepoint.
  */
-int upgrade_callsite(int nb_libraries, char **libraries)
+static int upgrade_callsite(int nb_libraries, char **libraries)
 {
        int ret = 0;
        void *handles[2];
@@ -159,6 +165,7 @@ int upgrade_callsite(int nb_libraries, char **libraries)
 error:
        return ret;
 }
+#endif
 
 int main(int argc, const char **argv)
 {
index 09de39e8cc99d3a22ee4378cc340484ad12e35a9..1b69b1f6ff323eb83ad69b155df6e1bf15503caf 100644 (file)
@@ -17,11 +17,13 @@ TESTS = test_kernel_data \
        test_utils_compat_poll \
        test_string_utils \
        test_notification \
+       test_event_rule \
        test_directory_handle \
        test_relayd_backward_compat_group_by_session \
        ini_config/test_ini_config \
        test_fd_tracker \
-       test_uuid
+       test_uuid \
+       test_buffer_view
 
 LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la
 
@@ -39,7 +41,8 @@ noinst_PROGRAMS = test_uri test_session test_kernel_data \
                   test_utils_expand_path test_utils_compat_poll \
                   test_string_utils test_notification test_directory_handle \
                   test_relayd_backward_compat_group_by_session \
-                  test_fd_tracker test_uuid
+                  test_fd_tracker test_uuid \
+                  test_buffer_view test_fd_tracker test_event_rule test_condition
 
 if HAVE_LIBLTTNG_UST_CTL
 noinst_PROGRAMS += test_ust_data
@@ -59,6 +62,7 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT)
         $(top_builddir)/src/bin/lttng-sessiond/kernel.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/ht-cleanup.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/notification-thread.$(OBJEXT) \
+        $(top_builddir)/src/bin/lttng-sessiond/action-executor.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/lttng-syscall.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/channel.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/agent.$(OBJEXT) \
@@ -182,6 +186,14 @@ test_string_utils_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSTRINGUTILS) $(DL_LIBS)
 # Notification api
 test_notification_SOURCES = test_notification.c
 test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS)
+#
+# Event rule api
+test_event_rule_SOURCES = test_event_rule.c
+test_event_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS)
+
+# Condition api
+test_condition_SOURCES = test_condition.c
+test_condition_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS)
 
 # relayd backward compat for groou-by-session utilities
 test_relayd_backward_compat_group_by_session_SOURCES = test_relayd_backward_compat_group_by_session.c
@@ -195,3 +207,7 @@ test_fd_tracker_LDADD = $(LIBTAP) $(LIBFDTRACKER) $(DL_LIBS) -lurcu $(LIBCOMMON)
 # uuid unit test
 test_uuid_SOURCES = test_uuid.c
 test_uuid_LDADD = $(LIBTAP) $(LIBCOMMON)
+
+# buffer view unit test
+test_buffer_view_SOURCES = test_buffer_view.c
+test_buffer_view_LDADD = $(LIBTAP) $(LIBCOMMON)
diff --git a/tests/unit/test_buffer_view.c b/tests/unit/test_buffer_view.c
new file mode 100644 (file)
index 0000000..b6c772d
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) - EfficiOS, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by as
+ * published by the Free Software Foundation; only version 2 of the License.
+ *
+ * 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/buffer-view.h>
+#include <tap/tap.h>
+
+static const int TEST_COUNT = 5;
+
+/* For error.h */
+int lttng_opt_quiet = 1;
+int lttng_opt_verbose;
+int lttng_opt_mi;
+
+static void test_validate_string(void)
+{
+       const char buf[] = {'A', 'l', 'l', 'o', '\0'};
+       struct lttng_buffer_view view = lttng_buffer_view_init(buf, 0, 5);
+       struct lttng_buffer_view view_minus_one =
+                       lttng_buffer_view_init(buf, 0, 4);
+
+       ok1(!lttng_buffer_view_validate_string(&view, buf, 4));
+       ok1(lttng_buffer_view_validate_string(&view, buf, 5));
+       ok1(!lttng_buffer_view_validate_string(&view, buf, 6));
+
+       ok1(!lttng_buffer_view_validate_string(&view_minus_one, buf, 4));
+       ok1(!lttng_buffer_view_validate_string(&view_minus_one, buf, 5));
+}
+
+int main(void)
+{
+       plan_tests(TEST_COUNT);
+
+       test_validate_string();
+
+       return exit_status();
+}
diff --git a/tests/unit/test_condition.c b/tests/unit/test_condition.c
new file mode 100644 (file)
index 0000000..9ae793f
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * test_condition.c
+ *
+ * Unit tests for the condition API.
+ *
+ * Copyright (C) 2019 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 <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <tap/tap.h>
+
+#include <lttng/event.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/domain.h>
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+
+/* For error.h */
+int lttng_opt_quiet = 1;
+int lttng_opt_verbose;
+int lttng_opt_mi;
+
+#define NUM_TESTS 3
+
+void test_condition_event_rule(void)
+{
+       int ret;
+       struct lttng_event_rule *tracepoint = NULL;
+       const struct lttng_event_rule *tracepoint_tmp = NULL;
+       enum lttng_event_rule_status status;
+       struct lttng_condition *condition = NULL;
+       struct lttng_condition *condition_from_buffer = NULL;
+       enum lttng_condition_status condition_status;
+       const char *pattern="my_event_*";
+       const char *filter="msg_id == 23 && size >= 2048";
+       const char *exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"};
+       struct lttng_dynamic_buffer buffer;
+       struct lttng_buffer_view view;
+
+       lttng_dynamic_buffer_init(&buffer);
+
+       tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_UST);
+       ok(tracepoint, "tracepoint UST_DOMAIN");
+
+       status = lttng_event_rule_tracepoint_set_pattern(tracepoint, pattern);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern");
+
+       status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter");
+
+       status = lttng_event_rule_tracepoint_set_loglevel_range(tracepoint, LTTNG_LOGLEVEL_WARNING);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel");
+
+       status = lttng_event_rule_tracepoint_set_exclusions(tracepoint, 3, exclusions);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting exclusions");
+
+       condition = lttng_condition_event_rule_create(tracepoint);
+       ok(condition, "created condition");
+
+       condition_status = lttng_condition_event_rule_get_rule(condition, &tracepoint_tmp);
+       ok(condition_status == LTTNG_CONDITION_STATUS_OK, "getting event rule");
+       ok(tracepoint == tracepoint_tmp, "Ownership transfer is good");
+
+       ret = lttng_condition_serialize(condition, &buffer, NULL);
+       ok(ret == 0, "Condition serialized");
+
+       view = lttng_buffer_view_from_dynamic_buffer(&buffer, 0, -1);
+       (void) lttng_condition_create_from_buffer(&view, &condition_from_buffer);
+       ok(condition_from_buffer, "condition from buffer is non null");
+
+       ok(lttng_condition_is_equal(condition, condition_from_buffer), "serialized and from buffer are equal");
+
+       lttng_dynamic_buffer_reset(&buffer);
+       lttng_condition_destroy(condition);
+       lttng_condition_destroy(condition_from_buffer);
+}
+
+int main(int argc, const char *argv[])
+{
+       plan_tests(NUM_TESTS);
+       test_condition_event_rule();
+       return exit_status();
+}
index f3213cb74adbb16e0ac5c2a7d5c46780d66f91a5..7bbb648d11956991478e502ea788b1e89477c117 100644 (file)
@@ -126,7 +126,8 @@ end:
 }
 
 /* Remove "file1" from the test folder hierarchy. */
-int remove_file_from_hierarchy(struct lttng_directory_handle *test_dir_handle,
+static int remove_file_from_hierarchy(
+               struct lttng_directory_handle *test_dir_handle,
                const char *test_root_name)
 {
        int ret;
diff --git a/tests/unit/test_event_rule.c b/tests/unit/test_event_rule.c
new file mode 100644 (file)
index 0000000..5903645
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * test_event_rule.c
+ *
+ * Unit tests for the notification API.
+ *
+ * Copyright (C) 2019 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 <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <tap/tap.h>
+
+#include <lttng/event.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event-rule/tracepoint-internal.h>
+#include <lttng/domain.h>
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+
+/* For error.h */
+int lttng_opt_quiet = 1;
+int lttng_opt_verbose;
+int lttng_opt_mi;
+
+#define NUM_TESTS 31
+
+void test_event_rule_tracepoint_ust(void)
+{
+       int ret;
+       unsigned int count;
+       struct lttng_event_rule *tracepoint = NULL;
+       struct lttng_event_rule *tracepoint_from_buffer = NULL;
+       enum lttng_event_rule_status status;
+       enum lttng_domain_type domain_type;
+       enum lttng_loglevel_type loglevel_type;
+       const char *pattern="my_event_*";
+       const char *filter="msg_id == 23 && size >= 2048";
+       const char *tmp;
+       const char *exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"};
+       struct lttng_dynamic_buffer buffer;
+       struct lttng_buffer_view view;
+
+       lttng_dynamic_buffer_init(&buffer);
+
+       tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_UST);
+       ok(tracepoint, "tracepoint UST_DOMAIN");
+
+       status = lttng_event_rule_tracepoint_get_domain_type(tracepoint, &domain_type);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get tracepoint domain");
+       ok(domain_type == LTTNG_DOMAIN_UST, "domain is UST");
+
+       status = lttng_event_rule_tracepoint_set_pattern(tracepoint, pattern);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern");
+       status = lttng_event_rule_tracepoint_get_pattern(tracepoint, &tmp);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern");
+       ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal");
+
+       status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter");
+       status = lttng_event_rule_tracepoint_get_filter(tracepoint, &tmp);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter");
+       ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal");
+
+       status = lttng_event_rule_tracepoint_set_loglevel_all(tracepoint);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting all loglevel");
+       status = lttng_event_rule_tracepoint_get_loglevel_type(tracepoint, &loglevel_type);
+       ok(loglevel_type == LTTNG_EVENT_LOGLEVEL_ALL, "getting loglevel type all");
+       status = lttng_event_rule_tracepoint_get_loglevel(tracepoint, &ret);
+       ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset loglevel value");
+
+       status = lttng_event_rule_tracepoint_set_loglevel(tracepoint, LTTNG_LOGLEVEL_INFO);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting single loglevel");
+       status = lttng_event_rule_tracepoint_get_loglevel_type(tracepoint, &loglevel_type);
+       ok(loglevel_type == LTTNG_EVENT_LOGLEVEL_SINGLE, "getting loglevel type single");
+       status = lttng_event_rule_tracepoint_get_loglevel(tracepoint, &ret);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get loglevel value");
+       ok(ret == LTTNG_LOGLEVEL_INFO, "loglevel value is equal");
+
+       status = lttng_event_rule_tracepoint_set_loglevel_range(tracepoint, LTTNG_LOGLEVEL_WARNING);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel");
+       status = lttng_event_rule_tracepoint_get_loglevel_type(tracepoint, &loglevel_type);
+       ok(loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE, "getting loglevel type range");
+       status = lttng_event_rule_tracepoint_get_loglevel(tracepoint, &ret);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get loglevel value");
+       ok(ret == LTTNG_LOGLEVEL_WARNING, "loglevel valuei is equal");
+
+       status = lttng_event_rule_tracepoint_set_exclusions(tracepoint, 3, exclusions);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting exclusions");
+
+       status = lttng_event_rule_tracepoint_get_exclusions_count(tracepoint, &count);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting exclusion count");
+       ok(count == 3, "count is %d/3", count);
+
+       for (int i = 0; i < count; i++) {
+               status = lttng_event_rule_tracepoint_get_exclusion_at_index(tracepoint, i, &tmp);
+               ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting exclusion at index %d", i);
+               ok(!strncmp(exclusions[i], tmp, strlen(exclusions[i])), "%s == %s", tmp, exclusions[i]);
+       }
+
+       lttng_event_rule_serialize(tracepoint, &buffer, NULL);
+       view = lttng_buffer_view_from_dynamic_buffer(&buffer, 0, -1);
+       lttng_event_rule_create_from_buffer(&view, &tracepoint_from_buffer);
+
+       ok(lttng_event_rule_is_equal(tracepoint, tracepoint_from_buffer), "serialized and from buffer are equal");
+
+
+
+
+       lttng_dynamic_buffer_reset(&buffer);
+       lttng_event_rule_destroy(tracepoint);
+       lttng_event_rule_destroy(tracepoint_from_buffer);
+}
+
+void test_event_rule_tracepoint(void)
+{
+       struct lttng_event_rule *tracepoint = NULL;
+
+       diag("Testing lttng_event_rule_tracepoint");
+       tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_NONE);
+       ok(!tracepoint, "Domain type restriction on create");
+
+       test_event_rule_tracepoint_ust();
+}
+
+int main(int argc, const char *argv[])
+{
+       plan_tests(NUM_TESTS);
+       test_event_rule_tracepoint();
+       return exit_status();
+}
index cb192ff80019d95b1932502c501f4882e9fbb533..928f3755a6241243291613f3b2744c8beca79c30 100644 (file)
@@ -76,7 +76,7 @@ void get_temporary_directories(char **_test_directory, char **_unlink_directory)
        }
 }
 
-int fd_count(void)
+static int fd_count(void)
 {
        DIR *dir;
        struct dirent *entry;
index 769f8809efc2183cf03176c06ef9e12f36bdba02..d2c0573aec158b816f4682472cea00b4d1c63bbb 100644 (file)
 #define NUM_TESTS_PER_TEST 1
 
 struct test {
-       char *stream_path;
-       char *session_name;
-       char *hostname;
-       char *creation_time;
-       char *extra_path;
-       char *leftover;
+       const char *stream_path;
+       const char *session_name;
+       const char *hostname;
+       const char *creation_time;
+       const char *extra_path;
+       const char *leftover;
        bool is_valid;
 };
 
index 94e93cd808f6e1ace74133371da576a52d5ae05c..2103a8f46634b2a46844625e93a89133f9f04366 100644 (file)
@@ -66,7 +66,7 @@ static char *get_random_string(void)
 /*
  * Return 0 if session name is found, else -1
  */
-static int find_session_name(char *name)
+static int find_session_name(const char *name)
 {
        struct ltt_session *iter;
 
@@ -110,7 +110,7 @@ static void empty_session_list(void)
 /*
  * Test creation of 1 session
  */
-static int create_one_session(char *name)
+static int create_one_session(const char *name)
 {
        int ret;
        enum lttng_error_code ret_code;
index e6f5e0c8b1597f538b0aba3ee737648978912aee..d7e7f2b054fb067570ca77543b94ed1405194b11 100644 (file)
@@ -25,19 +25,19 @@ int lttng_opt_verbose = 3;
 int lttng_opt_mi;
 
 struct valid_test_input {
-       char *input;
-       char *relative_part;
-       char *absolute_part;
+       const char *input;
+       const char *relative_part;
+       const char *absolute_part;
 };
 
 struct tree_symlink {
-       char *orig;
-       char *dest;
+       const char *orig;
+       const char *dest;
 };
 
 struct symlink_test_input {
-       char *input;
-       char *expected_result;
+       const char *input;
+       const char *expected_result;
 };
 
 /* Valid test cases */
index 27dbf2c9ad6b020e54600327f04c46088cddc297..4340a69ec639266d96d0bdf1655a9db077d903c5 100644 (file)
@@ -19,7 +19,7 @@ int lttng_opt_verbose = 3;
 int lttng_opt_mi;
 
 struct valid_test_input {
-       char *input;
+       const char *input;
        uint64_t expected_result;
 };
 
@@ -69,7 +69,7 @@ static struct valid_test_input valid_tests_inputs[] = {
 static const int num_valid_tests = sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]);
 
 /* Invalid test cases */
-static char *invalid_tests_inputs[] = {
+static const char *invalid_tests_inputs[] = {
                "",
                " ",
                "-1",
index 09708edd03e913ec13d57c5492649c9669a5e281..6a1c3280bc4f48fa2ff9bd357db4396b34bfd222 100644 (file)
@@ -20,7 +20,7 @@ int lttng_opt_verbose = 3;
 int lttng_opt_mi;
 
 struct valid_test_input {
-       char *input;
+       const char *input;
        uint64_t expected_result;
 };
 
@@ -56,7 +56,7 @@ static struct valid_test_input valid_tests_inputs[] = {
 static const int num_valid_tests = sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]);
 
 /* Invalid test cases */
-static char *invalid_tests_inputs[] = {
+static const char *invalid_tests_inputs[] = {
                "",
                " ",
                "-1",
index 9c967f82d4e29b5b3a0aa8b7a4570131ddebd838..7395f6fc59b80c14ce5a710ff8018e45d025f861 100644 (file)
@@ -40,7 +40,7 @@ static unsigned int test_count = 0; /* Number of tests that have been run */
 static unsigned int e_tests = 0; /* Expected number of tests to run */
 static unsigned int failures = 0; /* Number of tests that failed */
 static char *todo_msg = NULL;
-static char *todo_msg_fixed = "libtap malloc issue";
+static const char *todo_msg_fixed = "libtap malloc issue";
 static int todo = 0;
 static int test_died = 0;
 
@@ -68,8 +68,8 @@ static void _cleanup(void);
  * test_comment -- a comment to print afterwards, may be NULL
  */
 unsigned int
-_gen_result(int ok, const char *func, char *file, unsigned int line,
-           char *test_name, ...)
+_gen_result(int ok, const char *func, const char *file, unsigned int line,
+           const char *test_name, ...)
 {
        va_list ap;
        char *local_test_name = NULL;
@@ -270,7 +270,7 @@ plan_tests(unsigned int tests)
 }
 
 unsigned int
-diag(char *fmt, ...)
+diag(const char *fmt, ...)
 {
        va_list ap;
 
@@ -294,7 +294,7 @@ _expected_tests(unsigned int tests)
 }
 
 int
-skip(unsigned int n, char *fmt, ...)
+skip(unsigned int n, const char *fmt, ...)
 {
        va_list ap;
        char *skip_msg = NULL;
index 045499a98cdfbde096958164774396d1a0a22319..ab9aad1aa6f9f080c2829f48552ee852582a1a0a 100644 (file)
 
 #define skip_end() } while(0);
 
-unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
+unsigned int _gen_result(int, const char *, const char *, unsigned int, const char *, ...);
 
 int plan_no_plan(void);
 int plan_skip_all(char *);
 int plan_tests(unsigned int);
 
-unsigned int diag(char *, ...);
+unsigned int diag(const char *, ...);
 
-int skip(unsigned int, char *, ...);
+int skip(unsigned int, const char *, ...);
 
 void todo_start(char *, ...);
 void todo_end(void);
index 81800a50a01b3539afa4747d99f068d8b7a3654e..3b10c274d2fc6493b580aea940d0659cc207b34c 100644 (file)
@@ -10,7 +10,7 @@ AM_CFLAGS += -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer
 #  available, we assume that the PIE is not enabled by default so we do not
 #  need to disable it.
 if LINKER_HAVE_NO_PIE_OPTION
-AM_CFLAGS += -no-pie
+AM_LDFLAGS += -no-pie
 endif
 
 noinst_PROGRAMS = gen-syscall-events-callstack
index ead6fe3870be9b31595d9f4c55d0deed08b0f39c..29472572f50678fd528d649a0b9e9b1dad07dcb3 100644 (file)
 #include "utils.h"
 
 #define MAX_LEN 16
+
+static
+int open_read_close(const char *path)
+{
+       int fd, ret;
+       char buf[MAX_LEN];
+       /*
+        * Start generating syscalls. We use syscall(2) to prevent libc to change
+        * the underlying syscall. e.g. calling openat(2) instead of open(2).
+        */
+       fd = syscall(SYS_openat, AT_FDCWD, path, O_RDONLY);
+       if (fd < 0) {
+               perror("open");
+               ret = -1;
+               goto error;
+       }
+
+       ret = syscall(SYS_read, fd, buf, MAX_LEN);
+       if (ret < 0) {
+               perror("read");
+               ret = -1;
+               goto error;
+       }
+
+       ret = syscall(SYS_close, fd);
+       if (ret == -1) {
+               perror("close");
+               ret = -1;
+               goto error;
+       }
+
+error:
+       return ret;
+}
+
 /*
  * The process waits for the creation of a file passed as argument from an
  * external processes to execute a syscall and exiting. This is useful for tests
@@ -21,8 +56,7 @@
  */
 int main(int argc, char **argv)
 {
-       int fd, ret;
-       char buf[MAX_LEN];
+       int ret;
        char *start_file;
 
        if (argc != 2) {
@@ -47,23 +81,14 @@ int main(int argc, char **argv)
         * Start generating syscalls. We use syscall(2) to prevent libc to change
         * the underlying syscall. e.g. calling openat(2) instead of open(2).
         */
-       fd = syscall(SYS_openat, AT_FDCWD, "/proc/cpuinfo", O_RDONLY);
-       if (fd < 0) {
-               perror("open");
-               ret = -1;
-               goto error;
-       }
-
-       ret = syscall(SYS_read, fd, buf, MAX_LEN);
-       if (ret < 0) {
-               perror("read");
+       ret = open_read_close("/proc/cpuinfo");
+       if (ret == -1) {
                ret = -1;
                goto error;
        }
 
-       ret = syscall(SYS_close, fd);
+       ret = open_read_close("/proc/cmdline");
        if (ret == -1) {
-               perror("close");
                ret = -1;
                goto error;
        }
index 52e5f3cb952fb0e69a596b7d9d0e36a974125306..3293687314aea3e213f470361d5a137bd0b5eace 100644 (file)
@@ -7,6 +7,7 @@
 
 #define _LGPL_SOURCE
 #include <arpa/inet.h>
+#include <getopt.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #define TRACEPOINT_DEFINE
 #include "tp.h"
 
+static struct option long_options[] =
+{
+       /* These options set a flag. */
+       {"iter", required_argument, 0, 'i'},
+       {"wait", required_argument, 0, 'w'},
+       {0, 0, 0, 0}
+};
+
 int main(int argc, char **argv)
 {
-       int i, netint, ret = 0;
+       int i, netint, ret = 0, option_index, option;
        long values[] = { 1, 2, 3 };
        char text[10] = "test";
        double dbl = 2.0;
@@ -31,19 +40,29 @@ int main(int argc, char **argv)
        unsigned int nr_iter = 100;
        useconds_t nr_usec = 0;
 
+       while ((option = getopt_long(argc, argv, "i:w:",
+                       long_options, &option_index)) != -1) {
+               switch (option) {
+               case 'i':
+                       nr_iter = atoi(optarg);
+                       break;
+               case 'w':
+                       nr_usec = atoi(optarg);
+                       break;
+               case '?':
+                       /* getopt_long already printed an error message. */
+                       break;
+               default:
+                       ret = -1;
+                       goto end;
+               }
+       }
+
        if (set_signal_handler()) {
                ret = -1;
                goto end;
        }
 
-       if (argc >= 2) {
-               nr_iter = atoi(argv[1]);
-       }
-
-       if (argc == 3) {
-               /* By default, don't wait unless user specifies. */
-               nr_usec = atoi(argv[2]);
-       }
 
        for (i = 0; i < nr_iter; i++) {
                netint = htonl(i);
index 09a2c327dc8724ebdfb7c25c0406d8671c4ccf11..c5e721136eb4d1af5e77b22f7a7107dc00310af3 100644 (file)
@@ -23,7 +23,7 @@
 
 const char *str = "test string";
 
-void create_file(const char *path)
+static void create_file(const char *path)
 {
        int ret;
 
index aa27a82ed262b08ef4eb5e8718b81abd9569a8a0..af76ae7fc909c60d8cb682dc30ec18dca521ed57 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
 # no optimization
-AM_CFLAGS = -O0
+AM_CFLAGS += -O0
 noinst_LTLIBRARIES = libfoo.la
 
 libfoo_la_SOURCES = foo.c foo.h
index d0f76bc6f678adff8375397f673b1e89d95162be..7da125654cd21a6f7be191a63ee2680ae1e6ac68 100644 (file)
@@ -392,7 +392,7 @@ function start_lttng_relayd_opt()
 
        DIR=$(readlink -f "$TESTDIR")
 
-       if [ -z $(pgrep $RELAYD_MATCH) ]; then
+       if [ -z $(pgrep -f $RELAYD_MATCH) ]; then
                # shellcheck disable=SC2086
                $DIR/../src/bin/lttng-relayd/$RELAYD_BIN $process_mode $opt 1> $OUTPUT_DEST 2> $ERROR_OUTPUT_DEST
                #$DIR/../src/bin/lttng-relayd/$RELAYD_BIN $opt -vvv >>/tmp/relayd.log 2>&1 &
@@ -441,7 +441,7 @@ function stop_lttng_relayd_opt()
        local retval=0
        local pids=
 
-       pids=$(pgrep "$RELAYD_MATCH")
+       pids=$(pgrep -f "$RELAYD_MATCH")
        if [ -z "$pids" ]; then
                if [ "$withtap" -eq "1" ]; then
                        pass "No relay daemon to kill"
@@ -460,7 +460,7 @@ function stop_lttng_relayd_opt()
        else
                out=1
                while [ -n "$out" ]; do
-                       out=$(pgrep "$RELAYD_MATCH")
+                       out=$(pgrep -f "$RELAYD_MATCH")
                        if [ -n "$dtimeleft_s" ]; then
                                if [ $dtimeleft_s -lt 0 ]; then
                                        out=
@@ -538,7 +538,7 @@ function start_lttng_sessiond_opt()
        : "${LTTNG_SESSION_CONFIG_XSD_PATH="${DIR}/../src/common/config/"}"
        export LTTNG_SESSION_CONFIG_XSD_PATH
 
-       if [ -z "$(pgrep "${SESSIOND_MATCH}")" ]; then
+       if [ -z "$(pgrep -f "${SESSIOND_MATCH}")" ]; then
                # Have a load path ?
                if [ -n "$load_path" ]; then
                        # shellcheck disable=SC2086
@@ -590,10 +590,10 @@ function stop_lttng_sessiond_opt()
        local retval=0
 
        local runas_pids=
-       runas_pids=$(pgrep "$RUNAS_MATCH")
+       runas_pids=$(pgrep -f "$RUNAS_MATCH")
 
        local pids=
-       pids=$(pgrep "$SESSIOND_MATCH")
+       pids=$(pgrep -f "$SESSIOND_MATCH")
 
        if [ -n "$runas_pids" ]; then
                pids="$pids $runas_pids"
@@ -617,7 +617,7 @@ function stop_lttng_sessiond_opt()
        else
                out=1
                while [ -n "$out" ]; do
-                       out=$(pgrep "${SESSIOND_MATCH}")
+                       out=$(pgrep -f "${SESSIOND_MATCH}")
                        if [ -n "$dtimeleft_s" ]; then
                                if [ $dtimeleft_s -lt 0 ]; then
                                        out=
@@ -629,7 +629,7 @@ function stop_lttng_sessiond_opt()
                done
                out=1
                while [ -n "$out" ]; do
-                       out=$(pgrep "$CONSUMERD_MATCH")
+                       out=$(pgrep -f "$CONSUMERD_MATCH")
                        if [ -n "$dtimeleft_s" ]; then
                                if [ $dtimeleft_s -lt 0 ]; then
                                        out=
@@ -683,7 +683,7 @@ function sigstop_lttng_sessiond_opt()
                return
        fi
 
-       PID_SESSIOND="$(pgrep "${SESSIOND_MATCH}") $(pgrep "$RUNAS_MATCH")"
+       PID_SESSIOND="$(pgrep -f "${SESSIOND_MATCH}") $(pgrep -f "$RUNAS_MATCH")"
 
        if [ "$withtap" -eq "1" ]; then
                diag "Sending SIGSTOP to lt-$SESSIOND_BIN and $SESSIOND_BIN pids: $(echo "$PID_SESSIOND" | tr '\n' ' ')"
@@ -697,7 +697,7 @@ function sigstop_lttng_sessiond_opt()
        else
                out=1
                while [ $out -ne 0 ]; do
-                       pid="$(pgrep "$SESSIOND_MATCH")"
+                       pid="$(pgrep -f "$SESSIOND_MATCH")"
 
                        # Wait until state becomes stopped for session
                        # daemon(s).
@@ -745,7 +745,7 @@ function stop_lttng_consumerd_opt()
 
        local retval=0
 
-       PID_CONSUMERD="$(pgrep "$CONSUMERD_MATCH")"
+       PID_CONSUMERD="$(pgrep -f "$CONSUMERD_MATCH")"
 
        if [ -z "$PID_CONSUMERD" ]; then
                if [ "$withtap" -eq "1" ]; then
@@ -765,7 +765,7 @@ function stop_lttng_consumerd_opt()
        else
                out=1
                while [ $out -ne 0 ]; do
-                       pid="$(pgrep "$CONSUMERD_MATCH")"
+                       pid="$(pgrep -f "$CONSUMERD_MATCH")"
 
                        # If consumerds are still present check their status.
                        # A zombie status qualifies the consumerd as *killed*
@@ -812,7 +812,7 @@ function sigstop_lttng_consumerd_opt()
        local withtap=$1
        local signal=SIGSTOP
 
-       PID_CONSUMERD="$(pgrep "$CONSUMERD_MATCH")"
+       PID_CONSUMERD="$(pgrep -f "$CONSUMERD_MATCH")"
 
        diag "Sending SIGSTOP to $CONSUMERD_BIN pids: $(echo "$PID_CONSUMERD" | tr '\n' ' ')"
 
@@ -828,7 +828,7 @@ function sigstop_lttng_consumerd_opt()
        else
                out=1
                while [ $out -ne 0 ]; do
-                       pid="$(pgrep "$CONSUMERD_MATCH")"
+                       pid="$(pgrep -f "$CONSUMERD_MATCH")"
 
                        # Wait until state becomes stopped for all
                        # consumers.
This page took 0.546141 seconds and 5 git commands to generate.