SoW-2020-0002: Trace Hit Counters: trigger error reporting integration sow-2020-0002-rev1
authorJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Tue, 29 Sep 2020 15:46:24 +0000 (11:46 -0400)
committerJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Fri, 16 Oct 2020 00:09:25 +0000 (20:09 -0400)
Revision 1

Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Change-Id: I841cc0a4a8c9cc41c6f14fa982c5967992511287

243 files changed:
.gitignore
DO_NO_MERGE.txt [new file with mode: 0644]
LICENSES/BSL-1.0 [new file with mode: 0644]
Makefile.am
configure.ac
doc/examples/trigger-on-event/Makefile [new file with mode: 0644]
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/performance/Makefile [new file with mode: 0644]
doc/examples/trigger-on-event/performance/README.md [new file with mode: 0644]
doc/examples/trigger-on-event/performance/bt_plugin_plot.py [new file with mode: 0644]
doc/examples/trigger-on-event/performance/consumer.c [new file with mode: 0644]
doc/examples/trigger-on-event/performance/generate-data.sh [new file with mode: 0755]
doc/examples/trigger-on-event/performance/generate-graph.sh [new file with mode: 0755]
doc/examples/trigger-on-event/performance/perform-experience.sh [new file with mode: 0755]
doc/examples/trigger-on-event/performance/performance.c [new file with mode: 0644]
doc/examples/trigger-on-event/performance/performance.h [new file with mode: 0644]
doc/examples/trigger-on-event/performance/producer.c [new file with mode: 0644]
doc/examples/trigger-on-event/tracepoint-trigger-example.c [new file with mode: 0644]
doc/examples/trigger-on-event/tracepoint-trigger-example.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]
include/Makefile.am
include/lttng/action/action-internal.h
include/lttng/action/action.h
include/lttng/condition/condition-internal.h
include/lttng/condition/condition.h
include/lttng/condition/evaluation-internal.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-expr-internal.h [new file with mode: 0644]
include/lttng/event-expr.h [new file with mode: 0644]
include/lttng/event-field-value-internal.h [new file with mode: 0644]
include/lttng/event-field-value.h [new file with mode: 0644]
include/lttng/event-internal.h
include/lttng/event-rule/event-rule-internal.h
include/lttng/event-rule/syscall-internal.h
include/lttng/event-rule/tracepoint-internal.h
include/lttng/lttng-error.h
include/lttng/lttng.h
include/lttng/snapshot.h
include/lttng/trigger/trigger-internal.h
include/lttng/trigger/trigger.h
src/Makefile.am
src/bin/lttng-sessiond/Makefile.am
src/bin/lttng-sessiond/action-executor.c
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/condition-internal.c [new file with mode: 0644]
src/bin/lttng-sessiond/condition-internal.h [new file with mode: 0644]
src/bin/lttng-sessiond/consumer.c
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/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/register.c
src/bin/lttng-sessiond/rotate.c
src/bin/lttng-sessiond/session.c
src/bin/lttng-sessiond/sessiond-config.c
src/bin/lttng-sessiond/sessiond-config.h
src/bin/lttng-sessiond/shm.c [deleted file]
src/bin/lttng-sessiond/shm.h [deleted file]
src/bin/lttng-sessiond/testpoint.h
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/trace-ust.h
src/bin/lttng-sessiond/trigger-error-accounting.c [new file with mode: 0644]
src/bin/lttng-sessiond/trigger-error-accounting.h [new file with mode: 0644]
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-consumer.c
src/bin/lttng-sessiond/ust-ctl-internal.h
src/bin/lttng/Makefile.am
src/bin/lttng/command.h
src/bin/lttng/commands/add_trigger.c [new file with mode: 0644]
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/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/actions/action.c
src/common/actions/group.c
src/common/actions/rotate-session.c
src/common/actions/snapshot-session.c
src/common/actions/start-session.c
src/common/actions/stop-session.c
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/bytecode/Makefile.am [new file with mode: 0644]
src/common/bytecode/bytecode.c [new file with mode: 0644]
src/common/bytecode/bytecode.h [new file with mode: 0644]
src/common/compat/directory-handle.c
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
src/common/credentials.h
src/common/domain.c [new file with mode: 0644]
src/common/dynamic-array.h
src/common/error.c
src/common/evaluation.c
src/common/event-expr-to-bytecode.c [new file with mode: 0644]
src/common/event-expr-to-bytecode.h [new file with mode: 0644]
src/common/event-expr.c [new file with mode: 0644]
src/common/event-field-value.c [new file with mode: 0644]
src/common/event-rule/event-rule.c
src/common/event-rule/kprobe.c
src/common/event-rule/syscall.c
src/common/event-rule/tracepoint.c
src/common/event-rule/uprobe.c
src/common/filter.c
src/common/filter.h
src/common/filter/Makefile.am
src/common/filter/filter-ast.h
src/common/filter/filter-bytecode.h [deleted file]
src/common/filter/filter-grammar-test.c
src/common/filter/filter-ir.h
src/common/filter/filter-parser.y
src/common/filter/filter-visitor-generate-bytecode.c
src/common/index-allocator.c [new file with mode: 0644]
src/common/index-allocator.h [new file with mode: 0644]
src/common/kernel-consumer/kernel-consumer.c
src/common/kernel-ctl/kernel-ctl.c
src/common/kernel-ctl/kernel-ctl.h
src/common/kernel-ctl/kernel-ioctl.h
src/common/lttng-kernel.h
src/common/notification.c
src/common/optional.h
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/shm.c [new file with mode: 0644]
src/common/shm.h [new file with mode: 0644]
src/common/trace-chunk.c
src/common/trigger.c
src/common/ust-consumer/ust-consumer.c
src/common/utils.c
src/common/utils.h
src/lib/lttng-ctl/lttng-ctl.c
src/lib/lttng-ctl/snapshot.c
src/vendor/Makefile.am [new file with mode: 0644]
src/vendor/msgpack/Makefile.am [new file with mode: 0644]
src/vendor/msgpack/fbuffer.h [new file with mode: 0644]
src/vendor/msgpack/gcc_atomic.h [new file with mode: 0644]
src/vendor/msgpack/lttng-config.h [new file with mode: 0644]
src/vendor/msgpack/msgpack.h [new file with mode: 0644]
src/vendor/msgpack/object.h [new file with mode: 0644]
src/vendor/msgpack/objectc.c [new file with mode: 0644]
src/vendor/msgpack/pack.h [new file with mode: 0644]
src/vendor/msgpack/pack_define.h [new file with mode: 0644]
src/vendor/msgpack/pack_template.h [new file with mode: 0644]
src/vendor/msgpack/predef.h [new file with mode: 0644]
src/vendor/msgpack/sbuffer.h [new file with mode: 0644]
src/vendor/msgpack/sysdep.h [new file with mode: 0644]
src/vendor/msgpack/timestamp.h [new file with mode: 0644]
src/vendor/msgpack/unpack.c [new file with mode: 0644]
src/vendor/msgpack/unpack.h [new file with mode: 0644]
src/vendor/msgpack/unpack_define.h [new file with mode: 0644]
src/vendor/msgpack/unpack_template.h [new file with mode: 0644]
src/vendor/msgpack/util.h [new file with mode: 0644]
src/vendor/msgpack/version.c [new file with mode: 0644]
src/vendor/msgpack/version.h [new file with mode: 0644]
src/vendor/msgpack/version_master.h [new file with mode: 0644]
src/vendor/msgpack/vrefbuffer.c [new file with mode: 0644]
src/vendor/msgpack/vrefbuffer.h [new file with mode: 0644]
src/vendor/msgpack/zbuffer.h [new file with mode: 0644]
src/vendor/msgpack/zone.c [new file with mode: 0644]
src/vendor/msgpack/zone.h [new file with mode: 0644]
tests/regression/Makefile.am
tests/regression/kernel/test_callstack
tests/regression/kernel/test_syscall
tests/regression/tools/Makefile.am
tests/regression/tools/notification/Makefile.am
tests/regression/tools/notification/notification.c
tests/regression/tools/notification/sessiond_testpoints.c [new file with mode: 0644]
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_capture [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_multi_app
tests/regression/tools/notification/test_notification_trigger_discarded_count [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_capture [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/notification/util_event_generator.sh [new file with mode: 0644]
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/start-stop/Makefile.am [new file with mode: 0644]
tests/regression/tools/trigger/start-stop/test_start_stop [new file with mode: 0755]
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/tools/trigger/utils/Makefile.am [new file with mode: 0644]
tests/regression/tools/trigger/utils/notification-client.c [new file with mode: 0644]
tests/unit/Makefile.am
tests/unit/test_condition.c [new file with mode: 0644]
tests/unit/test_event_expr_to_bytecode.c [new file with mode: 0644]
tests/utils/testapp/gen-syscall-events/gen-syscall-events.c
tests/utils/testapp/gen-ust-events/gen-ust-events.c
tests/utils/testapp/gen-ust-events/tp.h

index 9b2b60d041f643092e3117287bc8ed9ebc0c27f3..f1b53075a118de1d28349533a42681551147e364 100644 (file)
@@ -90,6 +90,7 @@ compile_commands.json
 /tests/unit/test_fd_tracker
 /tests/unit/test_payload
 /tests/unit/test_event_rule
 /tests/unit/test_fd_tracker
 /tests/unit/test_payload
 /tests/unit/test_event_rule
+/tests/unit/test_condition
 /tests/unit/test_unix_socket
 kernel_all_events_basic
 kernel_event_basic
 /tests/unit/test_unix_socket
 kernel_all_events_basic
 kernel_event_basic
@@ -108,6 +109,8 @@ health_check
 /tests/regression/tools/notification/notification
 /tests/regression/tools/rotation/schedule_api
 /tests/regression/tools/notification/rotation
 /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
 /tests/regression/ust/overlap/demo/demo
 /tests/regression/ust/linking/demo_builtin
 /tests/regression/ust/linking/demo_static
@@ -163,6 +166,9 @@ health_check
 
 # examples
 /doc/examples/rotation/rotate-client
 
 # examples
 /doc/examples/rotation/rotate-client
+/doc/examples/trigger-on-event/performance/consumer
+/doc/examples/trigger-on-event/performance/producer
+/doc/examples/trigger-on-event/performance/performance.a
 
 /benchmark/
 
 
 /benchmark/
 
diff --git a/DO_NO_MERGE.txt b/DO_NO_MERGE.txt
new file mode 100644 (file)
index 0000000..0c57f8d
--- /dev/null
@@ -0,0 +1,2 @@
+captures
+trigger error counter
diff --git a/LICENSES/BSL-1.0 b/LICENSES/BSL-1.0
new file mode 100644 (file)
index 0000000..f1a0122
--- /dev/null
@@ -0,0 +1,30 @@
+Valid-License-Identifier: BSL-1.0
+SPDX-URL: https://spdx.org/licenses/BSL-1.0.html
+Usage-Guide:
+  To use the Boost Software License put the following SPDX tag/value pair into a
+  comment according to the placement guidelines in the licensing rules
+  documentation:
+    SPDX-License-Identifier: BSL-1.0
+License-Text:
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by this
+license (the "Software") to use, reproduce, display, distribute, execute, and
+transmit the Software, and to prepare derivative works of the Software, and to
+permit third-parties to whom the Software is furnished to do so, all subject to
+the following:
+
+The copyright notices in the Software and this entire statement, including the
+above license grant, this restriction and the following disclaimer, must be
+included in all copies of the Software, in whole or in part, and all derivative
+works of the Software, unless such copies or derivative works are solely in the
+form of machine-executable object code generated by a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES
+OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
index 0240558c8d44444239e98aa9ca59c5c209d2a9d7..807d85b1782ad11ce249f2a30f9c48c7b1ed1d5a 100644 (file)
@@ -22,4 +22,5 @@ EXTRA_DIST = extras/lttng-bash_completion \
             LICENSES/GPL-2.0 \
             LICENSES/LGPL-2.1 \
             LICENSES/MIT \
             LICENSES/GPL-2.0 \
             LICENSES/LGPL-2.1 \
             LICENSES/MIT \
+            LICENSES/BSL-1.0 \
             version
             version
index 1b6f3fcb0d429c0c478caa96943173620f33a76d..0c458e5d01bded4e33d76374e88b99e88a18784a 100644 (file)
@@ -1121,6 +1121,8 @@ AC_CONFIG_FILES([
        extras/core-handler/Makefile
        src/Makefile
        src/common/Makefile
        extras/core-handler/Makefile
        src/Makefile
        src/common/Makefile
+       src/common/argpar/Makefile
+       src/common/bytecode/Makefile
        src/common/kernel-ctl/Makefile
        src/common/kernel-consumer/Makefile
        src/common/consumer/Makefile
        src/common/kernel-ctl/Makefile
        src/common/kernel-consumer/Makefile
        src/common/consumer/Makefile
@@ -1145,6 +1147,8 @@ AC_CONFIG_FILES([
        src/bin/lttng-relayd/Makefile
        src/bin/lttng/Makefile
        src/bin/lttng-crash/Makefile
        src/bin/lttng-relayd/Makefile
        src/bin/lttng/Makefile
        src/bin/lttng-crash/Makefile
+       src/vendor/Makefile
+       src/vendor/msgpack/Makefile
        tests/Makefile
        tests/destructive/Makefile
        tests/regression/Makefile
        tests/Makefile
        tests/destructive/Makefile
        tests/regression/Makefile
@@ -1172,6 +1176,9 @@ AC_CONFIG_FILES([
        tests/regression/tools/working-directory/Makefile
        tests/regression/tools/relayd-grouping/Makefile
        tests/regression/tools/clear/Makefile
        tests/regression/tools/working-directory/Makefile
        tests/regression/tools/relayd-grouping/Makefile
        tests/regression/tools/clear/Makefile
+       tests/regression/tools/trigger/Makefile
+       tests/regression/tools/trigger/start-stop/Makefile
+       tests/regression/tools/trigger/utils/Makefile
        tests/regression/ust/Makefile
        tests/regression/ust/nprocesses/Makefile
        tests/regression/ust/high-throughput/Makefile
        tests/regression/ust/Makefile
        tests/regression/ust/nprocesses/Makefile
        tests/regression/ust/high-throughput/Makefile
diff --git a/doc/examples/trigger-on-event/Makefile b/doc/examples/trigger-on-event/Makefile
new file mode 100644 (file)
index 0000000..f758a05
--- /dev/null
@@ -0,0 +1,56 @@
+# Copyright (C) 2020  Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+# OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
+#
+# Permission is hereby granted to use or copy this program for any
+# purpose, provided the above notices are retained on all copies.
+# Permission to modify the code and to distribute modified code is
+# granted, provided the above notices are retained, and a notice that
+# the code was modified is included with the above copyright notice.
+#
+# This Makefile is not using automake so that users may see how to build
+# a program with tracepoint provider probes compiled as static libraries.
+#
+# This makefile is purposefully kept simple to support GNU and BSD make.
+
+LOCAL_CPPFLAGS += -I.
+LIBS_INSTRUMENTED_APP = -ldl -llttng-ust
+LIBS_NOTIFICATION_CLIENT = -ldl -llttng-ctl
+AM_V_P := :
+AR ?= ar
+
+all: instrumented-app notification-client
+
+tracepoint-trigger-example.o: tracepoint-trigger-example.c tracepoint-trigger-example.h
+       @if $(AM_V_P); then set -x; else echo "  CC       $@"; fi; \
+               $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+               $(CFLAGS) -c -o $@ $<
+
+tracepoint-trigger-example.a: tracepoint-trigger-example.o
+       @if $(AM_V_P); then set -x; else echo "  AR       $@"; fi; \
+               $(AR) -rc $@ tracepoint-trigger-example.o
+
+instrumented-app.o: instrumented-app.c
+       @if $(AM_V_P); then set -x; else echo "  CC       $@"; fi; \
+               $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+               $(CFLAGS) -c -o $@ $<
+
+instrumented-app: instrumented-app.o tracepoint-trigger-example.a
+       @if $(AM_V_P); then set -x; else echo "  CCLD     $@"; fi; \
+               $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \
+               $(CFLAGS) instrumented-app.o tracepoint-trigger-example.a $(LIBS_INSTRUMENTED_APP)
+
+notification-client.o: notification-client.c
+       @if $(AM_V_P); then set -x; else echo "  CC       $@"; fi; \
+               $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+               $(CFLAGS) -c -o $@ $<
+
+notification-client: notification-client.o
+       @if $(AM_V_P); then set -x; else echo "  CCLD     $@"; fi; \
+               $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \
+               $(CFLAGS) notification-client.o tracepoint-trigger-example.a $(LIBS_NOTIFICATION_CLIENT)
+
+.PHONY: clean
+clean:
+       rm -f *.o *.a instrumented-app notification-client
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..da2c7b9
--- /dev/null
@@ -0,0 +1,95 @@
+# Trigger notification example
+
+## Description
+This example is made-up of three executables.
+
+### `notification-client`
+
+```
+Usage: notification-client TRIGGER_NAME TRIGGER_NAME2 ...
+```
+
+A simple client that subscribes to the notifications emitted by the `TRIGGER_NAME` trigger.
+
+Multiple trigger names can be passed and subscribed to.
+
+
+### `instrumented-app`
+
+An application that emits the `trigger_example: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_example:my_event` event occurs.
+
+This script also adds a trigger named `demo_trigger_capture` which emits a
+notification when the user-space `trigger_example:my_event` event occurs and
+provides captured fields if present.
+
+Once the triggers have been setup, the notification-client is launched to print
+all notifications emitted by the `demo_trigger` and `demo_trigger_capture`
+trigger.
+
+## Building
+
+Simply run the included Makefile.
+
+## Running the example
+
+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_example:my_event user-space event
+Trigger registered successfully.
+Trigger registered successfully.
+Subscribed to notifications of trigger "demo_trigger_capture"
+Subscribed to notifications of trigger "demo_trigger"
+[08-24-2020] 17:20:33.598221 - Received notification of event rule trigger "demo_trigger"
+[08-24-2020] 17:20:33.598855 - Received notification of event rule trigger "demo_trigger_capture"
+Captured field values:
+  Field: iteration Value: [Unsigned int] 0,
+  Field: does_not_exist Value: Capture unavailable,
+  Field: $ctx.vtid Value: [Unsigned int] 2302494,
+  Field: $ctx.procname Value: [String] instrumented-ap.
+[08-24-2020] 17:20:35.598556 - Received notification of event rule trigger "demo_trigger"
+[08-24-2020] 17:20:35.599293 - Received notification of event rule trigger "demo_trigger_capture"
+Captured field values:
+  Field: iteration Value: [Unsigned int] 1,
+  Field: does_not_exist Value: Capture unavailable,
+  Field: $ctx.vtid Value: [Unsigned int] 2302494,
+  Field: $ctx.procname Value: [String] instrumented-ap.
+[08-24-2020] 17:20:37.598977 - Received notification of event rule trigger "demo_trigger"
+[08-24-2020] 17:20:37.599676 - Received notification of event rule trigger "demo_trigger_capture"
+Captured field values:
+  Field: iteration Value: [Unsigned int] 2,
+  Field: does_not_exist Value: Capture unavailable,
+  Field: $ctx.vtid Value: [Unsigned int] 2302494,
+  Field: $ctx.procname Value: [String] instrumented-ap.
+[08-24-2020] 17:20:39.599430 - Received notification of event rule trigger "demo_trigger"
+[08-24-2020] 17:20:39.600178 - Received notification of event rule trigger "demo_trigger_capture"
+Captured field values:
+  Field: iteration Value: [Unsigned int] 3,
+  Field: does_not_exist Value: Capture unavailable,
+  Field: $ctx.vtid Value: [Unsigned int] 2302494,
+  Field: $ctx.procname Value: [String] instrumented-ap.
+...
+```
+
+```
+$ ./instrumented-app
+[08-24-2020] 17:20:33.597441 - Tracing event "trigger_example:my_event"
+[08-24-2020] 17:20:35.597703 - Tracing event "trigger_example:my_event"
+[08-24-2020] 17:20:37.597997 - Tracing event "trigger_example: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..9bf8a00
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# SPDX-License-Identifier: MIT
+
+EVENT_NAME=trigger_example:my_event
+TRIGGER_NAME=demo_trigger
+TRIGGER_NAME_CAPTURE=demo_trigger_capture
+
+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
+lttng add-trigger --id $TRIGGER_NAME_CAPTURE --condition on-event --userspace $EVENT_NAME --capture 'iteration' --capture 'does_not_exist' --capture '$ctx.vtid' --capture '$ctx.procname' --action notify
+
+./notification-client $TRIGGER_NAME $TRIGGER_NAME_CAPTURE
+
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..f6fd67d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#include "tracepoint-trigger-example.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_example:my_event\"\n", time_str, tv.tv_usec);
+
+               tracepoint(trigger_example, 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..dc50446
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * 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 <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+static int print_capture(const struct lttng_condition *condition,
+               const struct lttng_event_field_value *capture,
+               unsigned int indent_level);
+static int print_array(const struct lttng_condition *condition,
+               const struct lttng_event_field_value *array,
+               unsigned int indent_level);
+
+static void indent(unsigned int indentation_level)
+{
+       unsigned int i;
+       for (i = 0; i < indentation_level; i++) {
+               printf(" ");
+       }
+}
+
+static
+void print_one_event_expr(const struct lttng_event_expr *event_expr)
+{
+       enum lttng_event_expr_type type;
+
+       type = lttng_event_expr_get_type(event_expr);
+
+       switch (type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: {
+               const char *name;
+
+               name = lttng_event_expr_event_payload_field_get_name(event_expr);
+               printf("%s", name);
+
+               break;
+       }
+
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: {
+               const char *name;
+
+               name = lttng_event_expr_channel_context_field_get_name(event_expr);
+               printf("$ctx.%s", name);
+
+               break;
+       }
+
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: {
+               const char *provider_name;
+               const char *type_name;
+
+               provider_name =
+                       lttng_event_expr_app_specific_context_field_get_provider_name(
+                               event_expr);
+               type_name =
+                       lttng_event_expr_app_specific_context_field_get_type_name(
+                               event_expr);
+
+               printf("$app.%s:%s", provider_name, type_name);
+
+               break;
+       }
+
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: {
+               unsigned int index;
+               const struct lttng_event_expr *parent_expr;
+               enum lttng_event_expr_status status;
+
+               parent_expr = lttng_event_expr_array_field_element_get_parent_expr(
+                       event_expr);
+               assert(parent_expr != NULL);
+
+               print_one_event_expr(parent_expr);
+
+               status = lttng_event_expr_array_field_element_get_index(
+                       event_expr, &index);
+               assert(status == LTTNG_EVENT_EXPR_STATUS_OK);
+
+               printf("[%u]", index);
+
+               break;
+       }
+
+       default:
+               abort();
+       }
+}
+
+static bool action_group_contains_notify(
+               const struct lttng_action *action_group)
+{
+       unsigned int i, count;
+       enum lttng_action_status status =
+                       lttng_action_group_get_count(action_group, &count);
+
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               printf("Failed to get action count from action group\n");
+               exit(1);
+       }
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_action *action =
+                               lttng_action_group_get_at_index(
+                                               action_group, i);
+               const enum lttng_action_type action_type =
+                               lttng_action_get_type(action);
+
+               if (action_type == LTTNG_ACTION_TYPE_NOTIFY) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+static int print_capture(const struct lttng_condition *condition,
+               const struct lttng_event_field_value *capture,
+               unsigned int indent_level)
+{
+       int ret = 0;
+       enum lttng_event_field_value_status event_field_status;
+       enum lttng_event_field_value_type type;
+       uint64_t u_val;
+       int64_t s_val;
+       double d_val;
+       const char *string_val = NULL;
+
+       switch (lttng_event_field_value_get_type(capture)) {
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT:
+       {
+               event_field_status =
+                               lttng_event_field_value_unsigned_int_get_value(
+                                               capture, &u_val);
+               if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+                       ret = 1;
+                       goto end;
+               }
+
+               printf("[Unsigned int] %" PRIu64, u_val);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT:
+       {
+               event_field_status =
+                               lttng_event_field_value_signed_int_get_value(
+                                               capture, &s_val);
+               if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+                       ret = 1;
+                       goto end;
+               }
+
+               printf("[Signed int]  %" PRId64, s_val);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+       {
+               event_field_status =
+                               lttng_event_field_value_unsigned_int_get_value(
+                                               capture, &u_val);
+               if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+                       ret = 1;
+                       goto end;
+               }
+
+               printf("[Unsigned enum] %" PRIu64, u_val);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+       {
+               event_field_status =
+                               lttng_event_field_value_signed_int_get_value(
+                                               capture, &s_val);
+               if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+                       ret = 1;
+                       goto end;
+               }
+
+               printf("[Signed enum] %" PRId64, s_val);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL:
+       {
+               event_field_status = lttng_event_field_value_real_get_value(
+                               capture, &d_val);
+               if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+                       ret = 1;
+                       goto end;
+               }
+
+               printf("[Real] %lf", d_val);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING:
+       {
+               string_val = lttng_event_field_value_string_get_value(capture);
+               if (string_val == NULL) {
+                       ret = 1;
+                       goto end;
+               }
+
+               printf("[String] %s", string_val);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY:
+               printf("[Array] [\n");
+               print_array(condition, capture, indent_level);
+               indent(indent_level);
+               printf("]\n");
+               break;
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN:
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID:
+       default:
+               ret = 1;
+               break;
+       }
+
+end:
+       return ret;
+}
+
+static void print_unavailabe(void)
+{
+       printf("Capture unavailable");
+}
+
+static int print_array(const struct lttng_condition *condition,
+               const struct lttng_event_field_value *array,
+               unsigned int indent_level)
+{
+       int ret = 0;
+       enum lttng_event_field_value_status event_field_status;
+       unsigned int captured_field_count;
+
+       event_field_status = lttng_event_field_value_array_get_length(
+                       array, &captured_field_count);
+       if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               ret = 1;
+               goto end;
+       }
+
+       for (unsigned int i = 0; i < captured_field_count; i++) {
+               const struct lttng_event_field_value *captured_field = NULL;
+               const struct lttng_event_expr *expr =
+                               lttng_condition_event_rule_get_capture_descriptor_at_index(
+                                               condition, i);
+               assert(expr);
+
+               indent(indent_level + 1);
+
+               printf("Field: ");
+               print_one_event_expr(expr);
+               printf(" Value: ");
+
+               event_field_status =
+                               lttng_event_field_value_array_get_element_at_index(
+                                               array, i, &captured_field);
+               if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+                       if (event_field_status ==
+                                       LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) {
+                               print_unavailabe();
+                       } else {
+                               ret = 1;
+                               goto end;
+                       }
+               } else {
+                       print_capture(condition, captured_field, indent_level + 1);
+               }
+
+               if (i + 1 < captured_field_count) {
+                       printf(",");
+               } else {
+                       printf(".");
+               }
+               printf("\n");
+       }
+
+end:
+       return ret;
+}
+
+static int print_captures(struct lttng_notification *notification)
+{
+       int ret = 0;
+       const struct lttng_evaluation *evaluation =
+                       lttng_notification_get_evaluation(notification);
+       const struct lttng_condition *condition =
+                       lttng_notification_get_condition(notification);
+
+       /* Status */
+       enum lttng_condition_status condition_status;
+       enum lttng_evaluation_status evaluation_status;
+       enum lttng_event_field_value_status event_field_status;
+
+       const struct lttng_event_field_value *captured_field_array = NULL;
+       unsigned int expected_capture_field_count;
+       unsigned int captured_field_count;
+
+       assert(lttng_evaluation_get_type(evaluation) ==
+                       LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+       condition_status =
+                       lttng_condition_event_rule_get_capture_descriptor_count(
+                                       condition,
+                                       &expected_capture_field_count);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               ret = 1;
+               goto end;
+       }
+
+       if (expected_capture_field_count == 0) {
+               ret = 0;
+               goto end;
+       }
+
+       evaluation_status = lttng_evaluation_get_captured_values(
+                       evaluation, &captured_field_array);
+       if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) {
+               ret = 1;
+               goto end;
+       }
+
+       printf("Captured field values:\n");
+       print_array(condition, captured_field_array, 1);
+end:
+       return ret;
+}
+
+static int print_notification(struct lttng_notification *notification)
+{
+       int ret = 0;
+       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);
+               ret = print_captures(notification);
+               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, j, subcription_count = 0, trigger_count;
+       enum lttng_trigger_status trigger_status;
+       struct lttng_notification_channel *notification_channel = NULL;
+
+       if (argc < 2) {
+               fprintf(stderr, "Missing trigger name(s)\n");
+               fprintf(stderr, "Usage: notification-client TRIGGER_NAME ...");
+               ret = -1;
+               goto end;
+       }
+
+       trigger_count = argc - 1;
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       if (!notification_channel) {
+               fprintf(stderr, "Failed to create notification channel\n");
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_list_triggers(&triggers);
+       if (ret != LTTNG_OK) {
+               fprintf(stderr, "Failed to list triggers\n");
+               goto end;
+       }
+
+       trigger_status = lttng_triggers_get_count(triggers, &count);
+       if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+               fprintf(stderr, "Failed to get trigger count\n");
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_trigger *trigger =
+                               lttng_triggers_get_at_index(triggers, i);
+               const struct lttng_condition *condition =
+                               lttng_trigger_get_const_condition(trigger);
+               const struct lttng_action *action =
+                               lttng_trigger_get_const_action(trigger);
+               const enum lttng_action_type action_type =
+                               lttng_action_get_type(action);
+               enum lttng_notification_channel_status channel_status;
+               const char *trigger_name = NULL;
+               bool subscribe = false;
+
+               lttng_trigger_get_name(trigger, &trigger_name);
+               for (j = 0; j < trigger_count; j++) {
+                       if (!strcmp(trigger_name, argv[j + 1])) {
+                               subscribe = true;
+                               break;
+                       }
+               }
+
+               if (!subscribe) {
+                       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 ==
+                               LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED) {
+                       continue;
+               }
+               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/performance/Makefile b/doc/examples/trigger-on-event/performance/Makefile
new file mode 100644 (file)
index 0000000..751548b
--- /dev/null
@@ -0,0 +1,57 @@
+# Copyright (C) 2020  Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+# OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
+#
+# Permission is hereby granted to use or copy this program for any
+# purpose, provided the above notices are retained on all copies.
+# Permission to modify the code and to distribute modified code is
+# granted, provided the above notices are retained, and a notice that
+# the code was modified is included with the above copyright notice.
+#
+# This Makefile is not using automake so that users may see how to build
+# a program with tracepoint provider probes compiled as static libraries.
+#
+# This makefile is purposefully kept simple to support GNU and BSD make.
+
+LOCAL_CPPFLAGS += -I.
+LIBS_INSTRUMENTED_APP = -ldl -llttng-ust
+LIBS_NOTIFICATION_CLIENT = -ldl -llttng-ctl
+LIBS_PERFORMANCE_CLIENT = -ldl -llttng-ust -llttng-ctl
+AM_V_P := :
+AR ?= ar
+
+all: producer consumer
+
+performance.o: performance.c performance.h
+       @if $(AM_V_P); then set -x; else echo "  CC       $@"; fi; \
+               $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+               $(CFLAGS) -c -o $@ $<
+
+performance.a: performance.o
+       @if $(AM_V_P); then set -x; else echo "  AR       $@"; fi; \
+               $(AR) -rc $@ performance.o
+
+producer.o: producer.c
+       @if $(AM_V_P); then set -x; else echo "  CC       $@"; fi; \
+               $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+               $(CFLAGS) -c -o $@ $<
+
+consumer.o: consumer.c
+       @if $(AM_V_P); then set -x; else echo "  CC       $@"; fi; \
+               $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+               $(CFLAGS) -c -o $@ $<
+
+producer: producer.o performance.a
+       @if $(AM_V_P); then set -x; else echo "  CCLD     $@"; fi; \
+               $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \
+               $(CFLAGS) producer.o performance.a $(LIBS_PERFORMANCE_CLIENT)
+
+consumer: consumer.o performance.a
+       @if $(AM_V_P); then set -x; else echo "  CCLD     $@"; fi; \
+               $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \
+               $(CFLAGS) consumer.o performance.a $(LIBS_PERFORMANCE_CLIENT)
+
+.PHONY: clean
+clean:
+       rm -f *.o *.a producer consumer
diff --git a/doc/examples/trigger-on-event/performance/README.md b/doc/examples/trigger-on-event/performance/README.md
new file mode 100644 (file)
index 0000000..8592f41
--- /dev/null
@@ -0,0 +1,83 @@
+# Trigger notification end-to-end latency analysis
+
+## Description
+This analysis is made-up of five executables.
+
+### `producer`
+
+```
+Usage: producer UNIQUE_ID NB_EVENT DELAY_MS
+```
+
+An application that emits `NB_EVENT` times the `performance:hit` event every
+`DELAY_MS` milliseconds.
+
+
+### `consumer`
+```
+Usage: consumer  UNIQUE_ID NB_EVENT TRIGGER_NAME
+```
+
+A simple notification client that subscribes to the notifications emitted by the
+`TRIGGER_NAME` trigger. The consumer expects `NB_EVENT` notification and on each
+valid reception emits a `performance:receive` event.
+
+
+### `perform-experience.sh`
+
+```
+Usage: perform-experience.sh SOURCE_ID TRACE_DIRECTORY_NAME DELAY_MS NB_EVENT`
+```
+
+This script performs a complete end-to-end trigger latency experience with
+`DELAY_MS` between each trigger hit and `NB_EVENT` times.
+
+The resulting lttng-ust trace is stored inside `$(pwd)/trace/TRACE_DIRECTORY_NAME`
+
+### `generate-data.sh`
+
+```
+Usage: generate-data.sh
+```
+
+This script performs all configured experiences and apply the customs workload
+as necessary.
+
+The resulting traces are stored inside  `$(pwd)/trace/`
+
+This script in its current form will run for about 25 hours.
+
+This script depends on `perform-experience.sh`.
+
+### `generate-graph.sh`
+
+```
+Usage: generate-graph.sh
+```
+
+This script generate all histograms and saved them individually as pdf files. It
+also generate a `summary.pdf` files that contains all pdfs in order of trigger frequency.
+
+This script does not have to run on the machine that produced the data.
+
+This script requires the presence of the "trace/" folder to work.
+
+This script depends on the `bt_plugin_plot.py` babeltrace 2 plugins. Hence this
+script requires Babeltrace 2 with python bindings and python plugin support.
+
+The `bt_plugin_plot.py` requires `matplotlib`.
+
+
+## Building
+
+Simply run the included Makefile.
+
+## Running the complete 
+
+1) Launch a session daemon using:
+        ```
+        $ lttng-sessiond
+        ```
+2) Launch `generate-data.sh`
+3) Wait ~25 hours
+3) Launch `generate-graph.sh`
diff --git a/doc/examples/trigger-on-event/performance/bt_plugin_plot.py b/doc/examples/trigger-on-event/performance/bt_plugin_plot.py
new file mode 100644 (file)
index 0000000..626a9a1
--- /dev/null
@@ -0,0 +1,260 @@
+import bt2
+import itertools
+import matplotlib.pyplot as plt
+import sys
+import statistics
+import csv
+from collections import defaultdict
+
+
+class DataLogger(object):
+    def __init__(self, name="Untitled"):
+        self._name = name
+
+    def get_name(self):
+        return self._name
+
+    def get_x_data(self):
+        raise NotImplementedError
+
+    def get_y_data(self):
+        raise NotImplementedError
+
+    def received_event(self, ts, event):
+        raise NotImplementedError
+
+
+class DurationDataLogger(DataLogger):
+    """
+        This class allow to create a duration histogram for the given pair of
+        event and unique tuple key generator.
+
+    """
+    def __init__(self, start_event, end_event, *args, **kwargs):
+        super(DurationDataLogger, self).__init__(*args, **kwargs)
+
+        (self._event_start, self._start_fields) = start_event
+        (self._event_end, self._end_fields) = end_event
+
+        self._durations = []
+        self._pair = dict()
+
+    def get_x_data(self):
+        return self._durations
+
+    def received_event(self, ts, event):
+        if event.name == self._event_start:
+            key = ()
+            for field in self._start_fields:
+                value = event.payload_field[str(field)]
+                key = key + (value,)
+            self._pair[key] = ts
+            return
+
+        if event.name == self._event_end:
+            key = ()
+            for field in self._end_fields:
+                value = event.payload_field[str(field)]
+                key = key + (value,)
+
+            if key not in self._pair:
+                print("unmatched end event")
+                return
+
+            start_ts = self._pair[key]
+            duration = (ts - start_ts) / 1000000.0
+            self._durations.append(duration)
+
+class DurationCSVDataLogger(DataLogger):
+    """
+        This class allow to create a duration histogram for the given csv.
+    """
+    def __init__(self, filepath, *args, **kwargs):
+        super(DurationCSVDataLogger, self).__init__(*args, **kwargs)
+
+        self._filepath = filepath
+
+
+        self._durations = []
+        with open(filepath, newline='') as file:
+            reader = csv.reader(file, quoting=csv.QUOTE_NONE)
+            next(reader)
+            for row in reader:
+                self._durations.append(float(row[0]))
+
+    def get_x_data(self):
+        return self._durations
+
+    def received_event(self, ts, event):
+        return
+
+
+class Plot(object):
+    def __init__(
+        self, loggers, title="Untitled", x_label="Untitled", y_label="Untitled"
+    ):
+        self._loggers = loggers
+        self._title = title
+        self._x_label = x_label
+        self._y_label = y_label
+
+    def received_event(self, ts, event):
+        for logger in self._loggers:
+            logger.received_event(ts, event)
+
+    def plot(self):
+        raise NotImplementedError
+
+    def generate_csv(self):
+        raise NotImplementedError
+
+    @staticmethod
+    def _format_filename(title, ext):
+        title = title.lower()
+        title = "".join("-" if not c.isalnum() else c for c in title)
+        title = "".join(
+            ["".join(j) if i != "-" else i for (i, j) in itertools.groupby(title)]
+        )
+        return f"{title}.{ext}"
+
+class HistogramPlot(Plot):
+    def __init__(self, *args, **kwargs):
+        super(HistogramPlot, self).__init__(*args, **kwargs)
+
+    @staticmethod
+    def get_statistics_header():
+        return ["minimum", "maximum", "mean", "pstdev", "count"]
+
+    @staticmethod
+    def get_statistics(samples):
+        stats = []
+        stats.append('%f' % min(samples))
+        stats.append('%f' % max(samples))
+        stats.append('%f' % statistics.mean(samples))
+        stats.append('%f' % statistics.pstdev(samples))
+        stats.append('%d' % len(samples))
+        return stats
+
+    def plot(self):
+        sys.argv = ['']
+        complete_set = [];
+        logger_statistic = defaultdict(dict)
+
+        figure = plt.figure()
+        plt.title(self._title)
+        plt.xlabel(self._x_label, figure=figure)
+        plt.ylabel(self._y_label, figure=figure)
+        plt.yscale('log', nonposy='clip')
+
+        table_rows_label = []
+        table_celltext = []
+        for logger in self._loggers:
+            x = logger.get_x_data()
+            table_rows_label.append(logger.get_name())
+            table_celltext.append(HistogramPlot.get_statistics(x))
+
+            complete_set +=x;
+            plt.hist(x, bins='auto', alpha=0.5, figure=figure, label=logger.get_name())
+
+        table_rows_label.append("all")
+        table_celltext.append(HistogramPlot.get_statistics(complete_set))
+        the_table = plt.table(cellText=table_celltext,
+                rowLabels=table_rows_label,
+                colLabels=HistogramPlot.get_statistics_header(),
+                loc='bottom',
+                bbox=[0.0,-0.45,1,.28],
+                )
+
+        the_table.auto_set_font_size(False)
+        the_table.set_fontsize(8)
+
+        plt.subplots_adjust(bottom=0.20)
+        plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
+        plt.savefig(Plot._format_filename(self._title, "pdf"), bbox_inches="tight")
+
+    def generate_csv(self):
+        for logger in self._loggers:
+            x_data = logger.get_x_data()
+            with open(Plot._format_filename(self._title, "%s.csv" % logger.get_name()), 'w', newline='') as export:
+                wr = csv.writer(export, quoting=csv.QUOTE_NONE)
+                wr.writerow([self._x_label])
+                for x in x_data:
+                    wr.writerow([x])
+
+
+@bt2.plugin_component_class
+class PlotSink(bt2._UserSinkComponent):
+    def __init__(self, config, params, obj):
+        self._plots = []
+
+        if "histograms" in params:
+            for plot in params["histograms"]:
+                self._plots.append(PlotSink.create_histogram(plot))
+
+        self._add_input_port("in")
+
+    def _user_consume(self):
+        msg = next(self._iter)
+        if type(msg) in [
+            bt2._PacketBeginningMessageConst,
+            bt2._PacketEndMessageConst,
+            bt2._StreamBeginningMessageConst,
+            bt2._StreamEndMessageConst,
+        ]:
+            return
+
+        ts = msg.default_clock_snapshot.value
+        for plot in self._plots:
+            plot.received_event(ts, msg.event)
+
+    def _user_finalize(self):
+            {plot.plot() for plot in self._plots}
+            {plot.generate_csv () for plot in self._plots}
+            return
+
+    def _user_graph_is_configured(self):
+        self._iter = self._create_message_iterator(self._input_ports["in"])
+
+    @staticmethod
+    def create_histogram(params):
+        loggers = []
+        for logger in params[3]:
+            if logger[0] == "duration":
+                logger = PlotSink.create_duration_logger(logger)
+            elif logger[0] == "duration-csv":
+                logger = PlotSink.create_duration_logger_csv(logger)
+            else:
+                raise ValueError
+
+            loggers.append(logger)
+
+        title = str(params[0])
+        x_label = str(params[1])
+        y_label = str(params[2])
+
+        return HistogramPlot(loggers, title=title, x_label=x_label,
+                y_label=y_label)
+
+    @staticmethod
+    def create_duration_logger(params):
+        return DurationDataLogger(
+            (str(params[2]), params[3]),
+            (str(params[4]), params[5]),
+            name=str(params[1]),
+        )
+
+    def create_duration_logger_csv(params):
+        return DurationCSVDataLogger(
+            str(params[2]),
+            name=str(params[1]),
+        )
+
+
+bt2.register_plugin(
+    module_name=__name__,
+    name="plot",
+    description="Plot Sink",
+    author="EfficiOS inc.",
+    license="GPL",
+    version=(1, 0, 0),
+)
diff --git a/doc/examples/trigger-on-event/performance/consumer.c b/doc/examples/trigger-on-event/performance/consumer.c
new file mode 100644 (file)
index 0000000..31c4f14
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2020 Jonathan Rajotte-Julien
+ * <jonathan.rajotte-julein@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+#include "performance.h"
+
+#include <lttng/tracepoint.h>
+
+#include <lttng/condition/event-rule.h>
+#include <lttng/lttng.h>
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.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(
+                                               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 main(int argc, char **argv)
+{
+       int ret;
+       int id, nb_reception;
+       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;
+       struct lttng_notification *notification;
+       enum lttng_notification_channel_status channel_status;
+       const struct lttng_evaluation *evaluation;
+       enum lttng_condition_type type;
+
+       if (argc != 4) {
+               fprintf(stderr, "Missing unique_id\n");
+               fprintf(stderr, "Missing nb_event\n");
+               fprintf(stderr, "Missing trigger name\n");
+               fprintf(stderr, "Usage: notification-client TRIGGER_NAME\n");
+               ret = 1;
+               goto end;
+       }
+
+       id = atoi(argv[1]);
+       nb_reception = atoi(argv[2]);
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       if (!notification_channel) {
+               fprintf(stderr, "Failed to create notification channel\n");
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_list_triggers(&triggers);
+       if (ret != LTTNG_OK) {
+               fprintf(stderr, "Failed to list triggers\n");
+               goto end;
+       }
+
+       trigger_status = lttng_triggers_get_count(triggers, &count);
+       if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+               fprintf(stderr, "Failed to get trigger count\n");
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_trigger *trigger =
+                               lttng_triggers_get_at_index(triggers, i);
+               const struct lttng_condition *condition =
+                               lttng_trigger_get_const_condition(trigger);
+               const struct lttng_action *action =
+                               lttng_trigger_get_const_action(trigger);
+               const enum lttng_action_type action_type =
+                               lttng_action_get_type(action);
+               enum lttng_notification_channel_status channel_status;
+               const char *trigger_name = NULL;
+
+               lttng_trigger_get_name(trigger, &trigger_name);
+               if (strcmp(trigger_name, argv[3])) {
+                       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 (int i = 0; i < nb_reception; i++) {
+               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");
+                       sleep(1);
+                       continue;
+                       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;
+               }
+
+               evaluation = lttng_notification_get_evaluation(notification);
+               type = lttng_evaluation_get_type(evaluation);
+
+               if (type != LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+                       assert(0);
+               }
+
+               tracepoint(performance, receive, id, i);
+               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/performance/generate-data.sh b/doc/examples/trigger-on-event/performance/generate-data.sh
new file mode 100755 (executable)
index 0000000..58065a5
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/bash -x
+#
+# Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: MIT
+
+cpu_stressor=$(nproc)
+
+while read -r load delay count; do
+       stress-ng --cpu-load "$load" --cpu "$cpu_stressor" &
+       stress_ng_id=$!
+
+       # Let the workload stabilize
+       sleep 5
+
+       ./perform-experience.sh 0 "${load}_cpuload_${delay}ms" "$delay" "$count"
+       kill "$stress_ng_id"
+       wait
+done << EOF
+0 1 10000
+25 1 10000
+50 1 10000
+75 1 10000
+100 1 10000
+0 10 5000
+25 10 5000
+50 10 5000
+75 10 5000
+100 10 5000
+0 100 6000
+25 100 6000
+50 100 6000
+75 100 6000
+100 100 6000
+0 1000 6000
+25 1000 6000
+50 1000 6000
+75 1000 6000
+100 1000 6000
+0 10000 600
+25 10000 600
+50 10000 600
+75 10000 600
+100 10000 600
+0 60000 100
+25 60000 100
+50 60000 100
+75 60000 100
+100 60000 60
+EOF
diff --git a/doc/examples/trigger-on-event/performance/generate-graph.sh b/doc/examples/trigger-on-event/performance/generate-graph.sh
new file mode 100755 (executable)
index 0000000..3140eb5
--- /dev/null
@@ -0,0 +1,108 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: MIT
+
+plugin_path=$(dirname "$0")
+
+DATA1="[\"duration\", \"D1\", \"performance:hit\", [\"source\", \"iteration\"], \"performance:receive\", [\"source\", \"iteration\"]]"
+
+while read -r load delay count; do
+       S=$(echo "scale=3; 1 / ( $delay / 1000 )" | bc | awk '{printf "%.3f", $0}' );
+       hz=${S/.000/}
+       hz_title=${hz/./-}
+
+       echo "Graphing Hz: ${hz} CPU: ${load}"
+
+       PLOT1="[\"Trigger latency, Freq:${hz}Hz, CPU load: ${load}%\", \"T (ms)\", \"count\", [$DATA1]]"
+
+       babeltrace2 --plugin-path="$plugin_path" --component sink.plot.PlotSink \
+               --params="histograms=[$PLOT1]"  \
+               "./trace/${load}_cpuload_${delay}ms"
+done << EOF
+0 1 10000
+25 1 10000
+50 1 10000
+75 1 10000
+100 1 10000
+0 10 5000
+25 10 5000
+50 10 5000
+75 10 5000
+100 10 5000
+0 100 6000
+25 100 6000
+50 100 6000
+75 100 6000
+100 100 6000
+0 1000 1500
+25 1000 1500
+50 1000 1500
+75 1000 1500
+100 1000 1500
+0 10000 300
+25 10000 300
+50 10000 300
+75 10000 300
+100 10000 300
+0 60000 50
+25 60000 50
+50 60000 50
+75 60000 50
+100 60000 50
+EOF
+
+pdf_unite=""
+csvs=""
+# Generate united graph and base pdf list to unite
+while read -r delay ; do
+       S=$(echo "scale=3; 1 / ( $delay / 1000 )" | bc | awk '{printf "%.3f", $0}' );
+       hz=${S/.000/}
+       hz_title=${hz/./-}
+       local_pdf_unite=""
+       echo "Combining graphs for Hz: ${hz} hz_title: $hz_title"
+
+       loggers=""
+       for load in 100 75 50 25 0; do
+               path=trigger-latency-freq-${hz_title}hz-cpu-load-${load}-.D1.csv
+               csvs="$csvs $path"
+               loggers="[\"duration-csv\", \"${load}% CPU\", \"${path}\"], $loggers"
+               local_pdf_unite="${local_pdf_unite} trigger-latency-freq-${hz_title}hz-cpu-load-${load}-.pdf"
+       done
+       pdf_unite="$pdf_unite $local_pdf_unite"
+
+       PLOT1="[\"Trigger latency, Freq:${hz}Hz\", \"T (ms)\", \"count\", [$loggers]]"
+       babeltrace2 --plugin-path="$plugin_path" --component sink.plot.PlotSink \
+               --params="histograms=[$PLOT1]"  \
+               "./trace/0_cpuload_${delay}ms"
+done << EOF
+1
+10
+100
+1000
+10000
+60000
+EOF
+
+# Add united graphs to the pdfunite cmd
+while read -r delay ; do
+       S=$(echo "scale=3; 1 / ( $delay / 1000 )" | bc | awk '{printf "%.3f", $0}' );
+       hz=${S/.000/}
+       hz_title=${hz/./-}
+
+       pdf_unite="trigger-latency-freq-${hz_title}hz.pdf $pdf_unite"
+
+done << EOF
+60000
+10000
+1000
+100
+10
+1
+EOF
+
+rm -rf $csvs
+
+pdfunite $pdf_unite summary.pdf
+
diff --git a/doc/examples/trigger-on-event/performance/perform-experience.sh b/doc/examples/trigger-on-event/performance/perform-experience.sh
new file mode 100755 (executable)
index 0000000..02e2cd4
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: MIT
+
+EVENT_NAME_HIT=performance:hit
+EVENT_NAME_RECEIVE=performance:receive
+TRIGGER_NAME=performance_hit
+
+if [ -z "$1" ]; then
+       echo "missing source id int value"
+       exit 1
+fi
+
+if [ -z "$2" ]; then
+       echo "missing trace directory name"
+       exit 1
+fi
+
+if [ -z "$3" ]; then
+       echo "missing loop delay"
+       exit 1
+fi
+
+if [ -z "$4" ]; then
+       echo "missing loop count"
+       exit 1
+fi
+
+key_id="$1"
+trace_directory="$(pwd)/trace/$2"
+delay=$3
+count=$4
+
+if ! lttng list > /dev/null 2>&1 ; then
+    echo "Could not connect to session daemon, are you sure it is running?"
+    exit 1
+fi
+
+lttng create performance --output="$trace_directory"
+lttng enable-event -u $EVENT_NAME_HIT,$EVENT_NAME_RECEIVE -s performance
+lttng start
+
+filter="source==$key_id"
+lttng add-trigger --id ${TRIGGER_NAME} --condition on-event --userspace $EVENT_NAME_HIT --filter="$filter" --action notify
+
+./consumer "$key_id" "$count" $TRIGGER_NAME &
+
+# Cheap way to synchronize and ensure that the consumer is ready to consume
+sleep 2
+
+./producer "$key_id" "$count" "$delay" &
+
+wait
+
+lttng remove-trigger ${TRIGGER_NAME}
+
+lttng stop
+lttng destroy performance
+
diff --git a/doc/examples/trigger-on-event/performance/performance.c b/doc/examples/trigger-on-event/performance/performance.c
new file mode 100644 (file)
index 0000000..311b034
--- /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 "performance.h"
diff --git a/doc/examples/trigger-on-event/performance/performance.h b/doc/examples/trigger-on-event/performance/performance.h
new file mode 100644 (file)
index 0000000..79e0a36
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER performance
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./performance.h"
+
+#if !defined(_TRACEPOINT_PERFORMANCE_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_PERFORMANCE_H
+
+#include <lttng/tracepoint.h>
+
+TRACEPOINT_EVENT(performance, hit,
+       TP_ARGS(int, source,
+               int, iteration),
+       TP_FIELDS(
+               ctf_integer(uint64_t, source, source)
+               ctf_integer(uint64_t, iteration, iteration)
+       )
+)
+
+TRACEPOINT_EVENT(performance, receive,
+       TP_ARGS(int, source,
+               int, iteration),
+       TP_FIELDS(
+               ctf_integer(uint64_t, source, source)
+               ctf_integer(uint64_t, iteration, iteration)
+       )
+)
+
+#endif /* _TRACEPOINT_PERFORMANCE_H */
+
+#include <lttng/tracepoint-event.h>
diff --git a/doc/examples/trigger-on-event/performance/producer.c b/doc/examples/trigger-on-event/performance/producer.c
new file mode 100644 (file)
index 0000000..3aefaae
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julein@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+#include "performance.h"
+
+#include <lttng/tracepoint.h>
+
+#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>
+#include <unistd.h>
+#include <assert.h>
+
+int main(int argc, char **argv)
+{
+       int ret = 0;
+       int nb_hit;
+       int id;
+       long long sleep_ms;
+       struct timespec sleep_time;
+       struct timespec sleep_rm;
+
+       if (argc != 4) {
+               fprintf(stderr, "Missing unique_id\n");
+               fprintf(stderr, "Missing number of event \n");
+               fprintf(stderr, "Missing delay between event in ms \n");
+               fprintf(stderr, "Usage: producer id bn_event delay_ms\n");
+               ret = 1;
+               goto end;
+       }
+       
+       id = atoi(argv[1]);
+       nb_hit = atoi(argv[2]);
+       sleep_ms = atoll(argv[3]);
+
+       sleep_time.tv_sec = sleep_ms / 1000;
+       sleep_time.tv_nsec = (sleep_ms % 1000) * 1000000;
+
+       for (int i = 0; i < nb_hit; i++) {
+               tracepoint(performance, hit, 0, i);
+               nanosleep(&sleep_time, &sleep_rm);
+       }
+
+end:
+       return !!ret;
+}
diff --git a/doc/examples/trigger-on-event/tracepoint-trigger-example.c b/doc/examples/trigger-on-event/tracepoint-trigger-example.c
new file mode 100644 (file)
index 0000000..3f781e3
--- /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 "tracepoint-trigger-example.h"
diff --git a/doc/examples/trigger-on-event/tracepoint-trigger-example.h b/doc/examples/trigger-on-event/tracepoint-trigger-example.h
new file mode 100644 (file)
index 0000000..e82e3c7
--- /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_example
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./tracepoint-trigger-example.h"
+
+#if !defined(_TRACEPOINT_TRIGGER_EXAMPLE_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_TRIGGER_EXAMPLE_H
+
+#include <lttng/tracepoint.h>
+
+TRACEPOINT_EVENT(trigger_example, my_event,
+       TP_ARGS(int, iteration),
+       TP_FIELDS(
+               ctf_integer(uint64_t, iteration, iteration)
+       )
+)
+
+#endif /* _TRACEPOINT_TRIGGER_EXAMPLE_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-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 =
 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..5e2e90e
--- /dev/null
@@ -0,0 +1,207 @@
+lttng-add-trigger(1)
+=====================
+:revdate: 27 May 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.
+
++
+Fields to capture can be specified with the option:--capture option, followed by
+a capture expression. Zero or more captures can be configured. See the
+<<capture-expr, Capture expression>> section below for more information.
+
+[[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.
+
+
+[[capture-expr]]
+Capture expression
+~~~~~~~~~~~~~~~~~~
+
+A capture expression can be specified with the option:--capture option when
+creating a new on-event condition. If the capture expression corresponds with an
+event's field when tracing, the runtime dynamic value corresponding to the
+capture expression is captured.
+
+NOTE: Make sure to **single-quote** the capture expression when running
+the command from a shell, as capture expressions typically include
+characters having a special meaning for most shells.
+
+* Supported field types:
+ - integer,
+ - unsigned integer,
+ - floating point value,
+ - fixed-size array of integers,
+ - variable-size array of integers (sequence),
+ - enumeration,
+ - text string,
+ - element of any allowing previous type.
+
+* The dynamic value of an event field is captured by using its name as a C
+  identifier.
++
+The square bracket notation is available, like in the C
+language, to access array/sequence field.
+Only a constant, positive integer number can be used within square
+brackets. If the index is out of bounds, the capture expression
+evaluates to `unavailable`.
++
+An enumeration field's value is an integer.
++
+When the capture's field does not exist, the capture expression
+evaluates to `unavailable`.
++
+Examples: `my_field`, `target_cpu`, `seq[7]`
+
+* The dynamic value of a statically-known context field is captured by
+  prefixing its name with `$ctx.`. See man:lttng-add-context(1) to get a list of
+  available contexts.
++
+When the expression's statically-known context field does not exist,
+the capture expression evaluates to `unavailable`.
++
+Examples: `$ctx.prio`, `$ctx.preemptible`,
+`$ctx.perf:cpu:stalled-cycles-frontend`.
++
+NOTE: The statically-known context field does NOT need to be added using the
+man:lttng-add-context(1) command. The statically-known context fields are
+always available in the context of triggers.
+
+* The dynamic value of an application-specific context field is captured by
+  prefixing its name with `$app.` (follows the format used to add such a context
+  field with the man:lttng-add-context(1) command).
++
+When the expression's application-specific context field does not exist,
+the capture expression evaluates to `unavailable`.
++
+Example: `$app.server:cur_user`.
++
+NOTE: The application-specific context field does NOT need to be added using the
+man:lttng-add-context(1) command. The application-specific context fields fields
+are always available in the context of triggers.
+
+
+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 1cfcd832012f03a4e7b997d22ce29d581441809e..9a7da5f224c40e5d8e68f740040f7a61e08f47db 100644 (file)
@@ -103,6 +103,8 @@ lttnginclude_HEADERS = \
        lttng/channel.h \
        lttng/domain.h \
        lttng/event.h \
        lttng/channel.h \
        lttng/domain.h \
        lttng/event.h \
+       lttng/event-expr.h \
+       lttng/event-field-value.h \
        lttng/handle.h \
        lttng/session.h \
        lttng/lttng-error.h \
        lttng/handle.h \
        lttng/session.h \
        lttng/lttng-error.h \
@@ -132,6 +134,7 @@ lttngactioninclude_HEADERS= \
 lttngconditioninclude_HEADERS= \
        lttng/condition/condition.h \
        lttng/condition/buffer-usage.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
        lttng/condition/session-consumed-size.h \
        lttng/condition/session-rotation.h \
        lttng/condition/evaluation.h
@@ -164,6 +167,7 @@ noinst_HEADERS = \
        lttng/action/stop-session-internal.h \
        lttng/condition/condition-internal.h \
        lttng/condition/buffer-usage-internal.h \
        lttng/action/stop-session-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 \
        lttng/condition/session-consumed-size-internal.h \
        lttng/condition/evaluation-internal.h \
        lttng/condition/session-rotation-internal.h \
@@ -172,7 +176,10 @@ noinst_HEADERS = \
        lttng/endpoint-internal.h \
        lttng/notification/channel-internal.h \
        lttng/channel-internal.h \
        lttng/endpoint-internal.h \
        lttng/notification/channel-internal.h \
        lttng/channel-internal.h \
+       lttng/domain-internal.h \
        lttng/event-internal.h \
        lttng/event-internal.h \
+       lttng/event-expr-internal.h \
+       lttng/event-field-value-internal.h \
        lttng/rotate-internal.h \
        lttng/ref-internal.h \
        lttng/location-internal.h \
        lttng/rotate-internal.h \
        lttng/ref-internal.h \
        lttng/location-internal.h \
index 20faea69713e050db033beeaeccbef50aca6d913..9e43a34d026491726e064acc6bede25bf3e92b88 100644 (file)
@@ -61,10 +61,6 @@ LTTNG_HIDDEN
 ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view,
                struct lttng_action **action);
 
 ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view,
                struct lttng_action **action);
 
-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
 bool lttng_action_is_equal(const struct lttng_action *a,
                const struct lttng_action *b);
index 1309f85bd9128c34baa566064f1849ba7bf41cd8..be7e397d0629e6f94091ac4a0eccaf1061366d2b 100644 (file)
@@ -38,7 +38,7 @@ enum lttng_action_status {
  * Returns the type of an action on success, LTTNG_ACTION_TYPE_UNKNOWN on error.
  */
 extern enum lttng_action_type lttng_action_get_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.
 
 /*
  * Destroy (frees) an action object.
index 405199b428fcae3004e3b5fdb97caaaf9e993b86..92bb4de71bd8283877bc579af666ab7e74aee05d 100644 (file)
@@ -71,4 +71,7 @@ LTTNG_HIDDEN
 bool lttng_condition_is_equal(const struct lttng_condition *a,
                const struct lttng_condition *b);
 
 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 */
 #endif /* LTTNG_CONDITION_INTERNAL_H */
index 877ccd1a39d0bcc9534c55e4fbc9b578b285cbd0..e1b73239899c1e3077c83bb11a3840ef9ca97c64 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_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 {
 };
 
 enum lttng_condition_status {
@@ -29,6 +30,7 @@ enum lttng_condition_status {
        LTTNG_CONDITION_STATUS_UNKNOWN = -2,
        LTTNG_CONDITION_STATUS_INVALID = -3,
        LTTNG_CONDITION_STATUS_UNSET = -4,
        LTTNG_CONDITION_STATUS_UNKNOWN = -2,
        LTTNG_CONDITION_STATUS_INVALID = -3,
        LTTNG_CONDITION_STATUS_UNSET = -4,
+       LTTNG_CONDITION_STATUS_UNSUPPORTED = -4,
 };
 
 /*
 };
 
 /*
index 15ae4af4d05d7205c434e8772f88c3acd4b06406..eaef721fb40b7030930f3fa18dd4cc50b2750d30 100644 (file)
@@ -9,6 +9,7 @@
 #define LTTNG_EVALUATION_INTERNAL_H
 
 #include <lttng/condition/evaluation.h>
 #define LTTNG_EVALUATION_INTERNAL_H
 
 #include <lttng/condition/evaluation.h>
+#include <lttng/condition/condition.h>
 #include <common/macros.h>
 #include <stdbool.h>
 #include <sys/types.h>
 #include <common/macros.h>
 #include <stdbool.h>
 #include <sys/types.h>
@@ -38,7 +39,9 @@ void lttng_evaluation_init(struct lttng_evaluation *evaluation,
                enum lttng_condition_type type);
 
 LTTNG_HIDDEN
                enum lttng_condition_type type);
 
 LTTNG_HIDDEN
-ssize_t lttng_evaluation_create_from_payload(struct lttng_payload_view *view,
+ssize_t lttng_evaluation_create_from_payload(
+               const struct lttng_condition *condition,
+               struct lttng_payload_view *view,
                struct lttng_evaluation **evaluation);
 
 LTTNG_HIDDEN
                struct lttng_evaluation **evaluation);
 
 LTTNG_HIDDEN
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..7f71a7b
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#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>
+#include <common/dynamic-array.h>
+#include <lttng/event-field-value.h>
+
+struct lttng_capture_descriptor {
+       /* The index at which the capture for this descriptor in the received
+        * payload from the tracer. This is populated on sessiond side.
+        * -1 is uninitialized.
+        * This is necessary since a single trigger can have multiple notify
+        * action, only an ordered set of capture desciptor is passed to the tracer.
+        */
+       int32_t capture_index;
+       struct lttng_event_expr *event_expression;
+};
+
+struct lttng_condition_event_rule {
+       struct lttng_condition parent;
+       struct lttng_event_rule *rule;
+
+       /* Array of `struct lttng_capture_descriptor *` */
+       struct lttng_dynamic_pointer_array capture_descriptors;
+};
+
+struct lttng_evaluation_event_rule {
+       struct lttng_evaluation parent;
+       char *name;
+
+       /* MessagePack-encoded captured event field values */
+       struct lttng_dynamic_buffer capture_payload;
+
+       /*
+        * The content of this array event field value is the decoded
+        * version of `capture_payload` above.
+        *
+        * This is a cache: it's not serialized/deserialized in
+        * communications from/to the library and the session daemon.
+        */
+       struct lttng_event_field_value *captured_values;
+};
+
+struct lttng_evaluation_event_rule_comm {
+       uint32_t trigger_name_length;
+       /* Trigger name */
+       char payload[];
+} LTTNG_PACKED;
+
+struct lttng_condition_event_rule_capture_bytecode_element
+{
+       struct lttng_event_expr *expression;
+       struct lttng_bytecode *bytecode;
+};
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_event_rule_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **condition);
+
+LTTNG_HIDDEN
+enum lttng_condition_status
+lttng_condition_event_rule_get_rule_mutable(
+               const struct lttng_condition *condition,
+               struct lttng_event_rule **rule);
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_event_rule_create(
+               const struct lttng_condition_event_rule *condition,
+               const char* trigger_name,
+               const char *capture_payload, size_t capture_payload_size,
+               bool decode_capture_payload);
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_event_rule_create_from_payload(
+               const struct lttng_condition_event_rule *condition,
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation);
+
+/*
+ * The returned `lttng_dynamic_pointer_array` contains a index ordered set of
+ * `lttng_condition_event_rule_capture_bytecode_element`.
+ * This ensure that minimal work will be done by the tracer for cases where
+ * multiple identical capture expression are present.
+ */
+LTTNG_HIDDEN
+enum lttng_error_code
+lttng_condition_event_rule_generate_capture_descriptor_bytecode_set(
+               struct lttng_condition *condition,
+               struct lttng_dynamic_pointer_array *bytecode_set);
+
+LTTNG_HIDDEN
+struct lttng_capture_descriptor *
+lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+               const struct lttng_condition *condition, unsigned int index);
+
+#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..8047c6b
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#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
+
+struct lttng_event_expr;
+struct lttng_event_field_value;
+
+/**
+ * Event rule conditions allows an action to be taken whenever an event matching
+ * the event rule is hit by the tracers.
+ *
+ * An event rule condition can also specify payload to be captured at runtime.
+ * This is done via the capture descriptor.
+ *
+ * Note: the dynamic runtime capture of payload is only available for the
+ *       trigger notification subsystem.
+ */
+
+/*
+ * Create a newly allocated event rule condition.
+ *
+ * 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);
+
+/*
+ * Sets `*field_val` to the array event field value of the event rule
+ * condition evaluation `evaluation` which contains its captured values.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVALUATION_STATUS_OK`:
+ *     Success.
+ *
+ *     `*field_val` is an array event field value with a length of at
+ *     least one.
+ *
+ * `LTTNG_EVALUATION_STATUS_INVALID`:
+ *     * `evaluation` is `NULL`.
+ *     * The type of the condition of `evaluation` is not
+ *       `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`.
+ *     * The condition of `evaluation` has no capture descriptors.
+ *     * `field_val` is `NULL`.
+ */
+extern enum lttng_evaluation_status
+lttng_evaluation_get_captured_values(
+               const struct lttng_evaluation *evaluation,
+               const struct lttng_event_field_value **field_val);
+
+/*
+ * Appends (transfering the ownership) the capture descriptor `expr` to
+ * the event rule condition `condition`.
+ *
+ * Returns:
+ *
+ * `LTTNG_CONDITION_STATUS_OK`:
+ *     Success.
+ *
+ * `LTTNG_CONDITION_STATUS_ERROR`:
+ *     Memory error.
+ *
+ * `LTTNG_CONDITION_STATUS_INVALID`:
+ *     * `condition` is `NULL`.
+ *     * The type of `condition` is not
+ *       `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`.
+ *     * `expr` is `NULL`.
+ *     * `expr` is not a locator expression, that is, its type is not
+ *       one of:
+ *
+ *       * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD`
+ *       * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`
+ *       * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`
+ *       * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`
+ * `LTTNG_CONDITION_STATUS_UNSUPPORTED`:
+ *     * The associated event-rule does not support runtime capture.
+ */
+extern enum lttng_condition_status
+lttng_condition_event_rule_append_capture_descriptor(
+               struct lttng_condition *condition,
+               struct lttng_event_expr *expr);
+
+/*
+ * Sets `*count` to the number of capture descriptors in the event rule
+ * condition `condition`.
+ *
+ * Returns:
+ *
+ * `LTTNG_CONDITION_STATUS_OK`:
+ *     Success.
+ *
+ * `LTTNG_CONDITION_STATUS_INVALID`:
+ *     * `condition` is `NULL`.
+ *     * The type of `condition` is not
+ *       `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`.
+ *     * `count` is `NULL`.
+ */
+extern enum lttng_condition_status
+lttng_condition_event_rule_get_capture_descriptor_count(
+               const struct lttng_condition *condition, unsigned int *count);
+
+/*
+ * Returns the capture descriptor (borrowed) of the event rule condition
+ * `condition` at the index `index`, or `NULL` if:
+ *
+ * * `condition` is `NULL`.
+ * * The type of `condition` is not
+ *   `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`.
+ * * `index` is greater than or equal to the number of capture
+ *   descriptors in `condition` (as returned by
+ *   lttng_condition_event_rule_get_capture_descriptor_count()).
+ */
+extern const struct lttng_event_expr *
+lttng_condition_event_rule_get_capture_descriptor_at_index(
+               const struct lttng_condition *condition, unsigned int index);
+
+#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..877fa28
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#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 */
diff --git a/include/lttng/event-expr-internal.h b/include/lttng/event-expr-internal.h
new file mode 100644 (file)
index 0000000..8c87781
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_EXPR_INTERNAL_H
+#define LTTNG_EVENT_EXPR_INTERNAL_H
+
+#include <assert.h>
+#include <common/macros.h>
+#include <lttng/event-expr.h>
+#include <urcu/ref.h>
+
+struct lttng_event_expr {
+       struct urcu_ref ref;
+       enum lttng_event_expr_type type;
+};
+
+/*
+ * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD` and
+ * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`.
+ */
+struct lttng_event_expr_field {
+       struct lttng_event_expr parent;
+       char *name;
+};
+
+/* `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD` */
+struct lttng_event_expr_app_specific_context_field {
+       struct lttng_event_expr parent;
+       char *provider_name;
+       char *type_name;
+};
+
+/* `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT` */
+struct lttng_event_expr_array_field_element {
+       struct lttng_event_expr parent;
+
+       /* Owned by this */
+       struct lttng_event_expr *array_field_expr;
+
+       unsigned int index;
+};
+
+/*
+ * Returns whether or not `expr` is an l-value (locator value).
+ */
+static inline
+bool lttng_event_expr_is_lvalue(const struct lttng_event_expr *expr)
+{
+       assert(expr);
+       return expr->type == LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD ||
+                       expr->type == LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD ||
+                       expr->type == LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD ||
+                       expr->type == LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT;
+}
+
+LTTNG_HIDDEN
+void lttng_event_expr_get(struct lttng_event_expr *expr);
+
+LTTNG_HIDDEN
+void lttng_event_expr_put(struct lttng_event_expr *expr);
+
+#endif /* LTTNG_EVENT_EXPR_INTERNAL_H */
diff --git a/include/lttng/event-expr.h b/include/lttng/event-expr.h
new file mode 100644 (file)
index 0000000..9116487
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_EXPR_H
+#define LTTNG_EVENT_EXPR_H
+
+#include <stdbool.h>
+
+struct lttng_event_expr;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Types of an event expression.
+ */
+enum lttng_event_expr_type {
+       /*
+        * Returned by lttng_event_expr_get_type() with an invalid
+        * parameter.
+        */
+       LTTNG_EVENT_EXPR_TYPE_INVALID = -1,
+
+       /*
+        * The named payload field of an event.
+        *
+        * Command-line expression example:
+        *
+        *     next_prio
+        */
+       LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD = 0,
+
+       /*
+        * The named per-channel context field of an event.
+        *
+        * Command-line expression example:
+        *
+        *     $ctx.vpid
+        */
+       LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD = 1,
+
+       /*
+        * The named application-specific context field of an event.
+        *
+        * Command-line expression example:
+        *
+        *     $app.iga:active-clients
+        */
+       LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD = 2,
+
+       /*
+        * The element of an array field.
+        *
+        * Command-line expression example:
+        *
+        *     my_field[4]
+        *     $ctx.some_context[5][1]
+        */
+       LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT = 3,
+};
+
+/*
+ * Event expression API status codes.
+ */
+enum lttng_event_expr_status {
+       /*
+        * Invalid parameter.
+        */
+       LTTNG_EVENT_EXPR_STATUS_INVALID = -1,
+
+       /*
+        * Success.
+        */
+       LTTNG_EVENT_EXPR_STATUS_OK = 0,
+};
+
+/*
+ * Returns the type of the event expression `expr`, or
+ * `LTTNG_EVENT_EXPR_TYPE_INVALID` if `expr` is `NULL`.
+ */
+extern enum lttng_event_expr_type lttng_event_expr_get_type(
+               const struct lttng_event_expr *expr);
+
+/*
+ * Creates an event payload field expression for the payload field named
+ * `field_name`.
+ *
+ * Returns `NULL` if:
+ *
+ * * There's a memory error.
+ * * `field_name` is `NULL`.
+ */
+extern struct lttng_event_expr *lttng_event_expr_event_payload_field_create(
+               const char *field_name);
+
+/*
+ * Returns the field name of the event payload field expression `expr`,
+ * or `NULL` if:
+ *
+ * * `expr` is `NULL`.
+ * * The type of `expr` is not
+ *   `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD`.
+ */
+extern const char *lttng_event_expr_event_payload_field_get_name(
+               const struct lttng_event_expr *expr);
+
+/*
+ * Creates a per-channel context field expression for the per-channel
+ * context field named `field_name`.
+ *
+ * Returns `NULL` if:
+ *
+ * * There's a memory error.
+ * * `field_name` is `NULL`.
+ */
+extern struct lttng_event_expr *
+lttng_event_expr_channel_context_field_create(const char *field_name);
+
+/*
+ * Returns the field name of the per-channel context field
+ * expression `expr`, or `NULL` if:
+ *
+ * `expr` is `NULL`.
+ * * The type of `expr` is not
+ *   `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`.
+ */
+extern const char *lttng_event_expr_channel_context_field_get_name(
+               const struct lttng_event_expr *expr);
+
+/*
+ * Creates an application-specific context field expression for the
+ * application-specific context field provided by the provider named
+ * `provider_name` and having the type named `type_name`.
+ *
+ * Returns `NULL` if:
+ *
+ * * There's a memory error.
+ * * `provider_name` is `NULL`.
+ * * `type_name` is `NULL`.
+ */
+extern struct lttng_event_expr *
+lttng_event_expr_app_specific_context_field_create(
+               const char *provider_name, const char *type_name);
+
+/*
+ * Returns the provider name of the application-specific context field
+ * expression `expr`, or `NULL` if:
+ *
+ * * `expr` is `NULL`.
+ * * The type of `expr` is not
+ *   `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`.
+ */
+extern const char *
+lttng_event_expr_app_specific_context_field_get_provider_name(
+               const struct lttng_event_expr *expr);
+
+/*
+ * Returns the type name of the application-specific context field
+ * expression `expr`, or `NULL` if:
+ *
+ * * `expr` is `NULL`.
+ * * The type of `expr` is not
+ *   `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`.
+ */
+extern const char *
+lttng_event_expr_app_specific_context_field_get_type_name(
+               const struct lttng_event_expr *expr);
+
+/*
+ * Creates an array field element expression for the parent array field
+ * `array_field_expr` (transfering the ownership) and the index `index`.
+ *
+ * Returns `NULL` if:
+ *
+ * * There's a memory error.
+ * * `array_field_expr` is `NULL`.
+ * * `array_field_expr` is not a locator expression, that is, its type
+ *   is not one of:
+ *
+ *    * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD`
+ *    * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`
+ *    * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`
+ *    * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`
+ */
+extern struct lttng_event_expr *lttng_event_expr_array_field_element_create(
+               struct lttng_event_expr *array_field_expr,
+               unsigned int index);
+
+/*
+ * Returns the parent array field expression of the array field element
+ * expression `expr`, or `NULL` if:
+ *
+ * * `expr` is `NULL`.
+ * * The type of `expr` is not
+ *   `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`.
+ */
+extern const struct lttng_event_expr *
+lttng_event_expr_array_field_element_get_parent_expr(
+               const struct lttng_event_expr *expr);
+
+/*
+ * Sets `*index` to the index of the array field element expression
+ * `expr`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_EXPR_STATUS_OK`:
+ *     Success.
+ *
+ * `LTTNG_EVENT_EXPR_STATUS_INVALID`:
+ *     * `expr` is `NULL`.
+ *     * The type of `expr` is not
+ *       `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`.
+ *     * `index` is `NULL`.
+ */
+extern enum lttng_event_expr_status
+lttng_event_expr_array_field_element_get_index(
+               const struct lttng_event_expr *expr, unsigned int *index);
+
+/*
+ * Returns whether or not the event expressions `expr_a` and `expr_b`
+ * are equal.
+ *
+ * `expr_a` and `expr_b` can be `NULL`.
+ */
+extern bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a,
+               const struct lttng_event_expr *expr_b);
+
+/*
+ * Destroys the event expression `expr` if not `NULL`.
+ */
+extern void lttng_event_expr_destroy(struct lttng_event_expr *expr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_EXPR_H */
diff --git a/include/lttng/event-field-value-internal.h b/include/lttng/event-field-value-internal.h
new file mode 100644 (file)
index 0000000..3ef6d2c
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_FIELD_VALUE_INTERNAL_H
+#define LTTNG_EVENT_FIELD_VALUE_INTERNAL_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <lttng/event-field-value.h>
+#include <common/dynamic-array.h>
+
+struct lttng_event_field_value {
+       enum lttng_event_field_value_type type;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT`.
+ */
+struct lttng_event_field_value_uint {
+       struct lttng_event_field_value parent;
+       uint64_t val;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT`.
+ */
+struct lttng_event_field_value_int {
+       struct lttng_event_field_value parent;
+       int64_t val;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` and
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM` (base).
+ */
+struct lttng_event_field_value_enum {
+       struct lttng_event_field_value parent;
+
+       /*
+        * Array of `char *` (owned by this).
+        */
+       struct lttng_dynamic_pointer_array labels;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM`.
+ */
+struct lttng_event_field_value_enum_uint {
+       struct lttng_event_field_value_enum parent;
+       uint64_t val;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`.
+ */
+struct lttng_event_field_value_enum_int {
+       struct lttng_event_field_value_enum parent;
+       int64_t val;
+};
+
+/* `LTTNG_EVENT_FIELD_VALUE_TYPE_REAL` */
+struct lttng_event_field_value_real {
+       struct lttng_event_field_value parent;
+       double val;
+};
+
+/* `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING` */
+struct lttng_event_field_value_string {
+       struct lttng_event_field_value parent;
+
+       /* Owned by this */
+       char *val;
+};
+
+/* `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING` */
+struct lttng_event_field_value_array {
+       struct lttng_event_field_value parent;
+
+       /*
+        * Array of `struct lttng_event_field_value *` (owned by this).
+        *
+        * A `NULL` element means it's unavailable
+        * (`LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE` status).
+        */
+       struct lttng_dynamic_pointer_array elems;
+};
+
+/*
+ * NOTE JORAJ: This was previously public. The only slight problem with that is
+ * that as of today (2020-05-26) there is no plan/sessiond for the tracer to
+ * actually provide this information. This was already known in [1]. For now
+ * this code have no value since it only confuse the end user. At upstreaming
+ * time we will need to decide if we want to remove all code pertaining to enum
+ * label, at least on the lttng-tools API.
+ *
+ * [1] https://support.efficios.com/issues/792
+ *
+ * Sets `*count` to the number of labels of the enumeration event field
+ * value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ *     Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ *     * `field_val` is `NULL`.
+ *     * The type of `field_val` is not
+ *       `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` or
+ *       `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`.
+ *     * `count` is `NULL`.
+ */
+LTTNG_HIDDEN
+enum lttng_event_field_value_status
+lttng_event_field_value_enum_get_label_count(
+               const struct lttng_event_field_value *field_val,
+               unsigned int *count);
+
+/*
+ * NOTE JORAJ: see NOTE JORAJ off lttng_event_field_value_enum_get_label_count
+ *
+ * Returns the label at index `index` of the enumeration event field
+ * value `field_val`, or `NULL` if:
+ *
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ *   `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` or
+ *   `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`.
+ * * `index` is greater than or equal to the label count of `field_val`,
+ *   as returned by lttng_event_field_value_enum_get_label_count().
+ */
+LTTNG_HIDDEN
+const char *lttng_event_field_value_enum_get_label_at_index(
+               const struct lttng_event_field_value *field_val,
+               unsigned int index);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_uint_create(
+               uint64_t val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_int_create(
+               int64_t val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_enum_uint_create(
+               uint64_t val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_enum_int_create(
+               int64_t val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_real_create(double val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_string_create(
+               const char *val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_string_create_with_size(
+               const char *val, size_t size);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_array_create(void);
+
+LTTNG_HIDDEN
+int lttng_event_field_value_enum_append_label(
+               struct lttng_event_field_value *field_val, const char *label);
+
+LTTNG_HIDDEN
+int lttng_event_field_value_enum_append_label_with_size(
+               struct lttng_event_field_value *field_val, const char *label,
+               size_t size);
+
+LTTNG_HIDDEN
+int lttng_event_field_value_array_append(
+               struct lttng_event_field_value *array_field_val,
+               struct lttng_event_field_value *field_val);
+
+LTTNG_HIDDEN
+int lttng_event_field_value_array_append_unavailable(
+               struct lttng_event_field_value *array_field_val);
+
+LTTNG_HIDDEN
+void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val);
+
+#endif /* LTTNG_EVENT_FIELD_VALUE_INTERNAL_H */
diff --git a/include/lttng/event-field-value.h b/include/lttng/event-field-value.h
new file mode 100644 (file)
index 0000000..db4a4fb
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_FIELD_VALUE_H
+#define LTTNG_EVENT_FIELD_VALUE_H
+
+#include <stdint.h>
+
+struct lttng_event_field_value;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Types of a event field value expression.
+ */
+enum lttng_event_field_value_type {
+       /*
+        * Unknown.
+        */
+       LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN = -2,
+
+       /*
+        * Returned by lttng_event_field_value_get_type() with an
+        * invalid parameter.
+        */
+       LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID = -1,
+
+       /*
+        * Unsigned integer event field value.
+        */
+       LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT = 0,
+
+       /*
+        * Signed integer event field value.
+        */
+       LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT = 1,
+
+       /*
+        * Unsigned enumeration event field value.
+        *
+        * This type conceptually inherits
+        * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT`.
+        */
+       LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM = 2,
+
+       /*
+        * Signed enumeration event field value.
+        *
+        * This type conceptually inherits
+        * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT`.
+        */
+       LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM = 3,
+
+       /*
+        * Real event field value.
+        */
+       LTTNG_EVENT_FIELD_VALUE_TYPE_REAL = 4,
+
+       /*
+        * String event field value.
+        */
+       LTTNG_EVENT_FIELD_VALUE_TYPE_STRING = 5,
+
+       /*
+        * Array event field value.
+        */
+       LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY = 6,
+};
+
+/*
+ * Event field value API status codes.
+ */
+enum lttng_event_field_value_status {
+       /*
+        * Event field value is not available.
+        */
+       LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE = -2,
+
+       /*
+        * Invalid parameter.
+        */
+       LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID = -1,
+
+       /*
+        * Success.
+        */
+       LTTNG_EVENT_FIELD_VALUE_STATUS_OK = 0,
+};
+
+/*
+ * Returns the type of the event field value `field_val`, or:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN`:
+ *     The type of `field_val` is unknown as of this version of the
+ *     LTTng control library.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID`:
+ *     `field_val` is `NULL`.
+ */
+extern enum lttng_event_field_value_type lttng_event_field_value_get_type(
+               const struct lttng_event_field_value *field_val);
+
+/*
+ * Sets `*val` to the raw value of the unsigned integer/enumeration
+ * event field value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ *     Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ *     * `field_val` is `NULL`.
+ *     * The type of `field_val` is not
+ *       `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT` or
+ *       `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM`.
+ *     * `val` is `NULL`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_unsigned_int_get_value(
+               const struct lttng_event_field_value *field_val, uint64_t *val);
+
+/*
+ * Sets `*val` to the raw value of the signed integer/enumeration event
+ * field value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ *     Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ *     * `field_val` is `NULL`.
+ *     * The type of `field_val` is not
+ *       `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT` or
+ *       `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`.
+ *     * `val` is `NULL`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_signed_int_get_value(
+               const struct lttng_event_field_value *field_val, int64_t *val);
+
+/*
+ * Sets `*val` to the raw value of the real event field value
+ * `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ *     Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ *     * `field_val` is `NULL`.
+ *     * The type of `field_val` is not
+ *       `LTTNG_EVENT_FIELD_VALUE_TYPE_REAL`.
+ *     * `val` is `NULL`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_real_get_value(
+               const struct lttng_event_field_value *field_val, double *val);
+
+/*
+ * Returns the raw value (an UTF-8 C string) of the string event field
+ * value `field_val`, or `NULL` if:
+ *
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ *   `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING`.
+ */
+extern const char *lttng_event_field_value_string_get_value(
+               const struct lttng_event_field_value *field_val);
+
+/*
+ * Sets `*length` to the length (the number of contained elements) of
+ * the array event field value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ *     Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ *     * `field_val` is `NULL`.
+ *     * The type of `field_val` is not
+ *       `LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY`.
+ *     * `length` is `NULL`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_array_get_length(
+               const struct lttng_event_field_value *field_val,
+               unsigned int *length);
+
+/*
+ * Sets `*elem_field_val` to the event field value at index `index` in
+ * the array event field value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ *     Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ *     * `field_val` is `NULL`.
+ *     * The type of `field_val` is not
+ *       `LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY`.
+ *     * `index` is greater than or equal to the length of `field_val`,
+ *       as returned by lttng_event_field_value_array_get_length().
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE`:
+ *     * No event field value exists at index `index` within
+ *       `field_val`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_array_get_element_at_index(
+               const struct lttng_event_field_value *field_val,
+               unsigned int index,
+               const struct lttng_event_field_value **elem_field_val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_FIELD_VALUE_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);
 
 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 */
 #endif /* LTTNG_EVENT_INTERNAL_H */
index 9fb115caf670bd5b924c112be6369ec003361911..b0f8a76fc92bbfd37e3678da80e6a3c06ff3fd50 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <common/macros.h>
 #include <lttng/domain.h>
 
 #include <common/macros.h>
 #include <lttng/domain.h>
+#include <lttng/event.h>
 #include <lttng/event-rule/event-rule.h>
 #include <lttng/lttng-error.h>
 #include <stdbool.h>
 #include <lttng/event-rule/event-rule.h>
 #include <lttng/lttng-error.h>
 #include <stdbool.h>
@@ -35,11 +36,13 @@ typedef enum lttng_error_code (*event_rule_generate_filter_bytecode_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);
                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 *(
+typedef const struct lttng_bytecode *(
                *event_rule_get_filter_bytecode_cb)(
                const struct lttng_event_rule *event_rule);
 typedef struct lttng_event_exclusion *(*event_rule_generate_exclusions_cb)(
                const struct lttng_event_rule *event_rule);
                *event_rule_get_filter_bytecode_cb)(
                const struct lttng_event_rule *event_rule);
 typedef struct lttng_event_exclusion *(*event_rule_generate_exclusions_cb)(
                const 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;
 
 struct lttng_event_rule {
        struct urcu_ref ref;
@@ -52,6 +55,7 @@ struct lttng_event_rule {
        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_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 {
 };
 
 struct lttng_event_rule_comm {
@@ -106,7 +110,7 @@ const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule);
  * Caller DOES NOT own the returned object.
  */
 LTTNG_HIDDEN
  * Caller DOES NOT own the returned object.
  */
 LTTNG_HIDDEN
-const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode(
+const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode(
                const struct lttng_event_rule *rule);
 
 /*
                const struct lttng_event_rule *rule);
 
 /*
@@ -120,4 +124,18 @@ struct lttng_event_exclusion *lttng_event_rule_generate_exclusions(
 LTTNG_HIDDEN
 const char *lttng_event_rule_type_str(enum lttng_event_rule_type type);
 
 LTTNG_HIDDEN
 const char *lttng_event_rule_type_str(enum lttng_event_rule_type type);
 
+/*
+ * 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);
+
 #endif /* LTTNG_EVENT_RULE_INTERNAL_H */
 #endif /* LTTNG_EVENT_RULE_INTERNAL_H */
index e99b69ae44ec78874f93c0a40756b92712a1bf51..b72274f5b15841c1955d6e5ca6e77b7d88800603 100644 (file)
@@ -21,7 +21,7 @@ struct lttng_event_rule_syscall {
        /* Internal use only. */
        struct {
                char *filter;
        /* Internal use only. */
        struct {
                char *filter;
-               struct lttng_filter_bytecode *bytecode;
+               struct lttng_bytecode *bytecode;
        } internal_filter;
 };
 
        } internal_filter;
 };
 
index 039b0a6cf2adddda2a838aee9c6625f27ce03149..227fe6de92aa1f885e9cdf1160f2c577aabe0211 100644 (file)
@@ -39,7 +39,7 @@ struct lttng_event_rule_tracepoint {
        /* internal use only. */
        struct {
                char *filter;
        /* internal use only. */
        struct {
                char *filter;
-               struct lttng_filter_bytecode *bytecode;
+               struct lttng_bytecode *bytecode;
        } internal_filter;
 };
 
        } internal_filter;
 };
 
index 9170948a135016035ca4c5750813886138c826ce..97122ed3967d1255e24985af78e8058e6c7d824b 100644 (file)
@@ -174,6 +174,9 @@ enum lttng_error_code {
        LTTNG_ERR_GROUP_NOT_FOUND        = 161, /* Group not found. */
        LTTNG_ERR_UNSUPPORTED_DOMAIN     = 162,  /* Unsupported domain used. */
        LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY = 163, /* Operation does not apply to the process attribute tracker's tracking policy */
        LTTNG_ERR_GROUP_NOT_FOUND        = 161, /* Group not found. */
        LTTNG_ERR_UNSUPPORTED_DOMAIN     = 162,  /* Unsupported domain used. */
        LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY = 163, /* Operation does not apply to the process attribute tracker's tracking policy */
+       LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD = 164, /* Error initializing trigger group notification fd */
+       LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER = 165, /* Error initializing trigger group error counter */
+       LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER_FULL = 166, /* No bucket available in trigger group error counter */
 
        /* MUST be last element of the manually-assigned section of the enum */
        LTTNG_ERR_NR,
 
        /* MUST be last element of the manually-assigned section of the enum */
        LTTNG_ERR_NR,
index 1049e0f0d31e35fcebfedb3e440462007859966c..107471afe3d54ce32520278bf6d8d2f0ebec5283 100644 (file)
@@ -29,6 +29,7 @@
 #include <lttng/condition/buffer-usage.h>
 #include <lttng/condition/condition.h>
 #include <lttng/condition/evaluation.h>
 #include <lttng/condition/buffer-usage.h>
 #include <lttng/condition/condition.h>
 #include <lttng/condition/evaluation.h>
+#include <lttng/condition/event-rule.h>
 #include <lttng/condition/session-consumed-size.h>
 #include <lttng/condition/session-rotation.h>
 #include <lttng/destruction-handle.h>
 #include <lttng/condition/session-consumed-size.h>
 #include <lttng/condition/session-rotation.h>
 #include <lttng/destruction-handle.h>
@@ -40,6 +41,8 @@
 #include <lttng/event-rule/tracepoint.h>
 #include <lttng/event-rule/uprobe.h>
 #include <lttng/event.h>
 #include <lttng/event-rule/tracepoint.h>
 #include <lttng/event-rule/uprobe.h>
 #include <lttng/event.h>
+#include <lttng/event-expr.h>
+#include <lttng/event-field-value.h>
 #include <lttng/handle.h>
 #include <lttng/health.h>
 #include <lttng/load.h>
 #include <lttng/handle.h>
 #include <lttng/health.h>
 #include <lttng/load.h>
index 24f1652874472ebc3b7591e3200487e81aa98eea..e674c0c7bd11777cea95e5f4b5af894703039757 100644 (file)
@@ -39,15 +39,15 @@ void lttng_snapshot_output_destroy(struct lttng_snapshot_output *output);
  */
 
 /* Return snapshot ID. */
  */
 
 /* 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. */
 /* 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. */
 /* 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. */
 /* 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. */
 /* 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.
 
 /*
  * Snapshot output setter family functions.
index 4b031137c6473153fc005321bb4a226c4c51fb3f..2be0093c03105512b6cc82ac1ca73d3083265d4c 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <lttng/trigger/trigger.h>
 #include <common/credentials.h>
 
 #include <lttng/trigger/trigger.h>
 #include <common/credentials.h>
+#include <common/dynamic-array.h>
 #include <common/macros.h>
 #include <common/optional.h>
 #include <stdint.h>
 #include <common/macros.h>
 #include <common/optional.h>
 #include <stdint.h>
@@ -26,34 +27,100 @@ struct lttng_trigger {
 
        struct lttng_condition *condition;
        struct lttng_action *action;
 
        struct lttng_condition *condition;
        struct lttng_action *action;
-       LTTNG_OPTIONAL(struct lttng_credentials) creds;
+       char *name;
+       /* For now only the uid portion of the credentials is used. */
+       struct lttng_credentials creds;
+       LTTNG_OPTIONAL(uint64_t) error_count;
+       struct {
+               enum lttng_trigger_firing_policy_type type;
+               uint64_t threshold;
+               uint64_t current_count;
+       } firing_policy;
+       /*
+        * Internal use only.
+        * The unique token passed to the tracer to identify a event-rule
+        * notification.
+        */
+       LTTNG_OPTIONAL(uint64_t) tracer_token;
+       /*
+        * Internal use only.
+        * Error accounting counter index.
+        */
+       LTTNG_OPTIONAL(uint64_t) error_counter_index;
+
+       /*
+        * This ordered set is used to hold the capture bytecodoes and their
+        * expression. lttng_action_capture_bytecode_element.
+        * We could only have bytecodes here... the expression are a left over
+        * from the generation process of the set. They are used for comparison
+        * during the gathering process. They are refcounted (TODO) and are the same
+        * object that are present un the underlying action object/s
+        */
+       struct lttng_dynamic_pointer_array capture_bytecode_set;
+};
+
+struct lttng_triggers {
+       struct lttng_dynamic_pointer_array array;
 };
 
 struct lttng_trigger_comm {
        /* length excludes its own length. */
 };
 
 struct lttng_trigger_comm {
        /* length excludes its own length. */
+       uint32_t name_length /* Includes '\0' */;
        uint32_t length;
        uint32_t length;
-       /* A condition and action object follow. */
+       /* Credentials, only the uid portion is used for now */
+       int64_t uid;
+       /* Policy */
+       /* Maps to enum lttng_trigger_firing_policy_type. */
+       uint8_t policy_type;
+       uint64_t policy_threshold;
+       uint64_t error_count;
+       /* A name, condition and action object follow. */
        char payload[];
 } LTTNG_PACKED;
 
        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_payload(struct lttng_payload_view *view,
                struct lttng_trigger **trigger);
 
 LTTNG_HIDDEN
 LTTNG_HIDDEN
 ssize_t lttng_trigger_create_from_payload(struct lttng_payload_view *view,
                struct lttng_trigger **trigger);
 
 LTTNG_HIDDEN
-int lttng_trigger_serialize(struct lttng_trigger *trigger,
+int lttng_trigger_serialize(const struct lttng_trigger *trigger,
                struct lttng_payload *payload);
 
 LTTNG_HIDDEN
                struct lttng_payload *payload);
 
 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_name(
+               struct lttng_trigger *dst, const struct lttng_trigger *src);
+
+LTTNG_HIDDEN
+void lttng_trigger_set_tracer_token(
+               struct lttng_trigger *trigger, uint64_t token);
+
+LTTNG_HIDDEN
+uint64_t lttng_trigger_get_tracer_token(const struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+void lttng_trigger_set_error_counter_index(
+               struct lttng_trigger *trigger, uint64_t error_counter_index);
 
 LTTNG_HIDDEN
 
 LTTNG_HIDDEN
-const struct lttng_action *lttng_trigger_get_const_action(
+uint64_t lttng_trigger_get_error_counter_index(
                const struct lttng_trigger *trigger);
 
 LTTNG_HIDDEN
                const struct lttng_trigger *trigger);
 
 LTTNG_HIDDEN
-bool lttng_trigger_validate(struct lttng_trigger *trigger);
+int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset);
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_equal(
+               const struct lttng_trigger *a, const struct lttng_trigger *b);
 
 LTTNG_HIDDEN
 void lttng_trigger_get(struct lttng_trigger *trigger);
 
 LTTNG_HIDDEN
 void lttng_trigger_get(struct lttng_trigger *trigger);
@@ -61,13 +128,95 @@ void lttng_trigger_get(struct lttng_trigger *trigger);
 LTTNG_HIDDEN
 void lttng_trigger_put(struct lttng_trigger *trigger);
 
 LTTNG_HIDDEN
 void lttng_trigger_put(struct lttng_trigger *trigger);
 
+/*
+ * Allocate a new set 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);
+
+/*
+ * Add a trigger to the triggers object.
+ */
+LTTNG_HIDDEN
+int lttng_triggers_add(
+               struct lttng_triggers *triggers, struct lttng_trigger *trigger);
+
+/*
+ * Serialize a trigger set to a lttng_payload object.
+ * Return LTTNG_OK on success, negative lttng error code on error.
+ */
+LTTNG_HIDDEN
+int lttng_triggers_serialize(const struct lttng_triggers *triggers,
+               struct lttng_payload *payload);
+
+LTTNG_HIDDEN
+ssize_t lttng_triggers_create_from_payload(struct lttng_payload_view *view,
+               struct lttng_triggers **triggers);
+
 LTTNG_HIDDEN
 const struct lttng_credentials *lttng_trigger_get_credentials(
                const struct lttng_trigger *trigger);
 
 LTTNG_HIDDEN
 LTTNG_HIDDEN
 const struct lttng_credentials *lttng_trigger_get_credentials(
                const struct lttng_trigger *trigger);
 
 LTTNG_HIDDEN
-void lttng_trigger_set_credentials(
-               struct lttng_trigger *trigger,
+void lttng_trigger_set_credentials(struct lttng_trigger *trigger,
                const struct lttng_credentials *creds);
 
                const struct lttng_credentials *creds);
 
+
+/*
+ * Fire the trigger.
+ * Increment the firing count.
+ */
+LTTNG_HIDDEN
+void lttng_trigger_fire(struct lttng_trigger *trigger);
+
+/*
+ * Check if the trigger would fire.
+ */
+LTTNG_HIDDEN
+bool lttng_trigger_should_fire(const struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_ready_to_fire(
+               struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+uint64_t lttng_trigger_get_error_count(
+       const struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+void lttng_trigger_set_error_count(struct lttng_trigger *trigger,
+               uint64_t error_count);
+
+/*
+ * 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);
+
+LTTNG_HIDDEN
+struct lttng_trigger *lttng_trigger_copy(const struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+unsigned int lttng_trigger_get_capture_bytecode_count(
+               const struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+const struct lttng_bytecode *
+lttng_trigger_get_capture_bytecode_at_index(
+               const struct lttng_trigger *trigger, unsigned int index);
+
 #endif /* LTTNG_TRIGGER_INTERNAL_H */
 #endif /* LTTNG_TRIGGER_INTERNAL_H */
index feffc6a8f9fb0f1c05c8b1f19b9cb1f308988a7e..e7ade81668d339463935c4e559bc25ea178a2b4f 100644 (file)
@@ -8,9 +8,14 @@
 #ifndef LTTNG_TRIGGER_H
 #define LTTNG_TRIGGER_H
 
 #ifndef LTTNG_TRIGGER_H
 #define LTTNG_TRIGGER_H
 
+#include <sys/types.h>
+#include <inttypes.h>
+
 struct lttng_action;
 struct lttng_condition;
 struct lttng_trigger;
 struct lttng_action;
 struct lttng_condition;
 struct lttng_trigger;
+/* A set of triggers */
+struct lttng_triggers;
 
 #ifdef __cplusplus
 extern "C" {
 
 #ifdef __cplusplus
 extern "C" {
@@ -21,6 +26,21 @@ enum lttng_register_trigger_status {
        LTTNG_REGISTER_TRIGGER_STATUS_INVALID = -1,
 };
 
        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,
+       LTTNG_TRIGGER_STATUS_EPERM = -6,
+};
+
+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.
  *
 /*
  * Create a trigger object associating a condition and an action.
  *
@@ -32,6 +52,9 @@ enum lttng_register_trigger_status {
  * The caller retains the ownership of both the condition and action
  * and both must be kept alive for the lifetime of the trigger object.
  *
  * The caller retains the ownership of both the condition and action
  * and both must be kept alive for the lifetime of the trigger object.
  *
+ * If the action is a notification action with capture descriptors,
+ * the condition must be an event rule condition.
+ *
  * A trigger must be registered in order to become activate and can
  * be destroyed after its registration.
  *
  * A trigger must be registered in order to become activate and can
  * be destroyed after its registration.
  *
@@ -42,6 +65,28 @@ enum lttng_register_trigger_status {
 extern struct lttng_trigger *lttng_trigger_create(
                struct lttng_condition *condition, struct lttng_action *action);
 
 extern struct lttng_trigger *lttng_trigger_create(
                struct lttng_condition *condition, struct lttng_action *action);
 
+/*
+ * Set the user identity (uid) of a trigger.
+ *
+ * Only available for the root user (uid 0).
+ *
+ * Returns LTTNG_TRIGGER_STATUS_OK on success,
+ * LTTNG_TRIGGER_STATUS_EPERM if not authorized,
+ * LTTNG_TRIGGER_STATUS_INVALID if invalid parameters are passed.
+ */
+extern enum lttng_trigger_status lttng_trigger_set_user_identity(
+               struct lttng_trigger *trigger, uid_t uid);
+
+/*
+ * Get the user identity (uid) of a trigger.
+ *
+ * Returns LTTNG_TRIGGER_STATUS_OK on success,
+ * LTTNG_TRIGGER_STATUS_UNSET if unset,
+ * LTTNG_TRIGGER_STATUS_INVALID if invalid parameters are passed.
+ */
+extern enum lttng_trigger_status lttng_trigger_get_user_identity(
+               const struct lttng_trigger *trigger, uid_t *uid);
+
 /*
  * Get the condition of a trigger.
  *
 /*
  * Get the condition of a trigger.
  *
@@ -52,6 +97,9 @@ extern struct lttng_trigger *lttng_trigger_create(
 extern struct lttng_condition *lttng_trigger_get_condition(
                struct lttng_trigger *trigger);
 
 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.
  *
 /*
  * Get the action of a trigger.
  *
@@ -62,6 +110,68 @@ extern struct lttng_condition *lttng_trigger_get_condition(
 extern struct lttng_action *lttng_trigger_get_action(
                struct lttng_trigger *trigger);
 
 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 until 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,
+               uint64_t threshold);
+
+/*
+ * Get the trigger firing policy.
+ *
+ * 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_get_firing_policy(
+               const struct lttng_trigger *trigger,
+               enum lttng_trigger_firing_policy_type *policy_type,
+               uint64_t *threshold);
+
 /*
  * Destroy (frees) a trigger object.
  */
 /*
  * Destroy (frees) a trigger object.
  */
@@ -83,7 +193,46 @@ extern int lttng_register_trigger(struct lttng_trigger *trigger);
  *
  * Return 0 on success, a negative LTTng error code on error.
  */
  *
  * 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, a newly-allocated trigger set is returned.
+ * The trigger set must be destroyed by the caller (see
+ * lttng_triggers_destroy()).
+ *
+ * Returns LTTNG_OK on success, else a negative LTTng error code.
+ */
+extern enum lttng_error_code lttng_list_triggers(
+               struct lttng_triggers **triggers);
+
+/*
+ * Get a trigger from the set at a given index.
+ *
+ * Note that the set 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 set.
+ *
+ * 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 trigger set.
+ *
+ * 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 set.
+ */
+extern void lttng_triggers_destroy(struct lttng_triggers *ids);
+
 
 #ifdef __cplusplus
 }
 
 #ifdef __cplusplus
 }
index fce97f3d14c37abd7b5d43081012c043013fa71b..a4f99905dd720fd272424d0935b76e4a5297a9be 100644 (file)
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
 # SPDX-License-Identifier: GPL-2.0-only
 
-SUBDIRS = common lib bin
+SUBDIRS = vendor common lib bin
index dd807125b6342e243c4720f835ba01ba94c37f27..ef0ad8c21f7e1a8d4ba92ae587fe9875a165d31f 100644 (file)
@@ -16,10 +16,10 @@ lttng_sessiond_SOURCES = utils.c utils.h \
                        lttng-ust-ctl.h lttng-ust-abi.h lttng-ust-error.h \
                        ust-ctl-internal.h ust-abi-internal.h ust-error-internal.h \
                        ust-registry.h \
                        lttng-ust-ctl.h lttng-ust-abi.h lttng-ust-error.h \
                        ust-ctl-internal.h ust-abi-internal.h ust-error-internal.h \
                        ust-registry.h \
+                       condition-internal.c condition-internal.h \
                        context.c context.h \
                        channel.c channel.h \
                        event.c event.h \
                        context.c context.h \
                        channel.c channel.h \
                        event.c event.h \
-                       shm.c shm.h \
                        consumer.c consumer.h \
                        session.c session.h \
                        modprobe.c modprobe.h kern-modules.h \
                        consumer.c consumer.h \
                        session.c session.h \
                        modprobe.c modprobe.h kern-modules.h \
@@ -55,8 +55,11 @@ lttng_sessiond_SOURCES = utils.c utils.h \
                        manage-consumer.c manage-consumer.h \
                        clear.c clear.h \
                        tracker.c tracker.h \
                        manage-consumer.c manage-consumer.h \
                        clear.c clear.h \
                        tracker.c tracker.h \
+                       trigger-error-accounting.c trigger-error-accounting.h \
                        action-executor.c action-executor.h
 
                        action-executor.c action-executor.h
 
+lttng_sessiond_LDFLAGS = -rdynamic
+
 if HAVE_LIBLTTNG_UST_CTL
 lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \
                        ust-consumer.c ust-consumer.h notify-apps.c \
 if HAVE_LIBLTTNG_UST_CTL
 lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \
                        ust-consumer.c ust-consumer.h notify-apps.c \
index 2e6e27a8ce3febcfd0db7cbb100369ff7d974ca4..8df560e0fb1b69d07753db639e15b8ab38413dac 100644 (file)
 #include <common/optional.h>
 #include <lttng/action/action-internal.h>
 #include <lttng/action/group.h>
 #include <common/optional.h>
 #include <lttng/action/action-internal.h>
 #include <lttng/action/group.h>
+#include <lttng/action/notify-internal.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/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 <lttng/lttng-error.h>
 #include <lttng/trigger/trigger-internal.h>
 #include <pthread.h>
@@ -104,7 +106,7 @@ static const char *action_type_names[] = {
 
 static const char *get_action_name(const struct lttng_action *action)
 {
 
 static const char *get_action_name(const struct lttng_action *action)
 {
-       return action_type_names[lttng_action_get_type_const(action)];
+       return action_type_names[lttng_action_get_type(action)];
 }
 
 /* Check if this trigger allowed to interect with a given session. */
 }
 
 /* Check if this trigger allowed to interect with a given session. */
@@ -113,27 +115,37 @@ static bool is_trigger_allowed_for_session(const struct lttng_trigger *trigger,
 {
        bool is_allowed = false;
        const struct lttng_credentials session_creds = {
 {
        bool is_allowed = false;
        const struct lttng_credentials session_creds = {
-               .uid = session->uid,
-               .gid = session->gid,
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(session->uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(session->gid),
        };
        /* Can never be NULL. */
        const struct lttng_credentials *trigger_creds =
                        lttng_trigger_get_credentials(trigger);
 
        };
        /* Can never be NULL. */
        const struct lttng_credentials *trigger_creds =
                        lttng_trigger_get_credentials(trigger);
 
-       is_allowed = (trigger_creds->uid == session_creds.uid) ||
-                       (trigger_creds->uid == 0);
+       is_allowed = (lttng_credentials_is_equal_uid(trigger_creds, &session_creds)) ||
+                       (lttng_credentials_get_uid(trigger_creds) == 0);
        if (!is_allowed) {
        if (!is_allowed) {
-               WARN("Trigger is not allowed to interact with session `%s`: session uid = %ld, session gid = %ld, trigger uid = %ld, trigger gid = %ld",
+               WARN("Trigger is not allowed to interact with session `%s`: session uid = %ld, session gid = %ld, trigger uid = %ld",
                                session->name,
                                (long int) session->uid,
                                (long int) session->gid,
                                session->name,
                                (long int) session->uid,
                                (long int) session->gid,
-                               (long int) trigger_creds->uid,
-                               (long int) trigger_creds->gid);
+                               (long int) lttng_credentials_get_uid(trigger_creds));
        }
 
        return is_allowed;
 }
 
        }
 
        return is_allowed;
 }
 
+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,
 static int client_handle_transmission_status(
                struct notification_client *client,
                enum client_transmission_status status,
@@ -184,9 +196,10 @@ static int action_executor_notify_handler(struct action_executor *executor,
                        lttng_trigger_get_const_condition(work_item->trigger),
                        work_item->evaluation,
                        lttng_trigger_get_credentials(work_item->trigger),
                        lttng_trigger_get_const_condition(work_item->trigger),
                        work_item->evaluation,
                        lttng_trigger_get_credentials(work_item->trigger),
-                       LTTNG_OPTIONAL_GET_PTR(work_item->object_creds),
-                       client_handle_transmission_status,
-                       executor);
+                       work_item->object_creds.is_set ?
+                                       &(work_item->object_creds.value) :
+                                       NULL,
+                       client_handle_transmission_status, executor);
 }
 
 static int action_executor_start_session_handler(struct action_executor *executor,
 }
 
 static int action_executor_start_session_handler(struct action_executor *executor,
@@ -211,9 +224,9 @@ static int action_executor_start_session_handler(struct action_executor *executo
        session_lock_list();
        session = session_find_by_name(session_name);
        if (!session) {
        session_lock_list();
        session = session_find_by_name(session_name);
        if (!session) {
-               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`",
+               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
                                session_name, get_action_name(action),
-                   work_item->trigger);
+                               get_trigger_name(work_item->trigger));
                goto error_unlock_list;
        }
 
                goto error_unlock_list;
        }
 
@@ -225,16 +238,16 @@ static int action_executor_start_session_handler(struct action_executor *executo
        cmd_ret = cmd_start_trace(session);
        switch (cmd_ret) {
        case LTTNG_OK:
        cmd_ret = cmd_start_trace(session);
        switch (cmd_ret) {
        case LTTNG_OK:
-               DBG("Successfully started session `%s` on behalf of trigger `%p`",
-                               session_name, work_item->trigger);
+               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:
                break;
        case LTTNG_ERR_TRACE_ALREADY_STARTED:
-               DBG("Attempted to start session `%s` on behalf of trigger `%p` but it was already started",
-                               session_name, work_item->trigger);
+               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:
                break;
        default:
-               WARN("Failed to start session `%s` on behalf of trigger `%p`: %s",
-                               session_name, work_item->trigger,
+               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;
        }
                                lttng_strerror(-cmd_ret));
                break;
        }
@@ -270,9 +283,9 @@ static int action_executor_stop_session_handler(struct action_executor *executor
        session_lock_list();
        session = session_find_by_name(session_name);
        if (!session) {
        session_lock_list();
        session = session_find_by_name(session_name);
        if (!session) {
-               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`",
+               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
                                session_name, get_action_name(action),
-                   work_item->trigger);
+                               get_trigger_name(work_item->trigger));
                goto error_unlock_list;
        }
 
                goto error_unlock_list;
        }
 
@@ -284,16 +297,16 @@ static int action_executor_stop_session_handler(struct action_executor *executor
        cmd_ret = cmd_stop_trace(session);
        switch (cmd_ret) {
        case LTTNG_OK:
        cmd_ret = cmd_stop_trace(session);
        switch (cmd_ret) {
        case LTTNG_OK:
-               DBG("Successfully stopped session `%s` on behalf of trigger `%p`",
-                               session_name, work_item->trigger);
+               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:
                break;
        case LTTNG_ERR_TRACE_ALREADY_STOPPED:
-               DBG("Attempted to stop session `%s` on behalf of trigger `%p` but it was already stopped",
-                               session_name, work_item->trigger);
+               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:
                break;
        default:
-               WARN("Failed to stop session `%s` on behalf of trigger `%p`: %s",
-                               session_name, work_item->trigger,
+               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;
        }
                                lttng_strerror(-cmd_ret));
                break;
        }
@@ -329,9 +342,9 @@ static int action_executor_rotate_session_handler(struct action_executor *execut
        session_lock_list();
        session = session_find_by_name(session_name);
        if (!session) {
        session_lock_list();
        session = session_find_by_name(session_name);
        if (!session) {
-               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`",
+               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
                                session_name, get_action_name(action),
-                   work_item->trigger);
+                               get_trigger_name(work_item->trigger));
                goto error_unlock_list;
        }
 
                goto error_unlock_list;
        }
 
@@ -344,21 +357,21 @@ static int action_executor_rotate_session_handler(struct action_executor *execut
                        LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED);
        switch (cmd_ret) {
        case LTTNG_OK:
                        LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED);
        switch (cmd_ret) {
        case LTTNG_OK:
-               DBG("Successfully started rotation of session `%s` on behalf of trigger `%p`",
-                               session_name, work_item->trigger);
+               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:
                break;
        case LTTNG_ERR_ROTATION_PENDING:
-               DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%p` but a rotation is already ongoing",
-                               session_name, work_item->trigger);
+               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:
                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 `%p` but a rotation has already been completed since the last stop or clear",
-                               session_name, work_item->trigger);
+               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:
                break;
        default:
-               WARN("Failed to start a rotation of session `%s` on behalf of trigger `%p`: %s",
-                               session_name, work_item->trigger,
+               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;
        }
                                lttng_strerror(-cmd_ret));
                break;
        }
@@ -424,12 +437,12 @@ static int action_executor_snapshot_session_handler(struct action_executor *exec
        cmd_ret = cmd_snapshot_record(session, snapshot_output, 0);
        switch (cmd_ret) {
        case LTTNG_OK:
        cmd_ret = cmd_snapshot_record(session, snapshot_output, 0);
        switch (cmd_ret) {
        case LTTNG_OK:
-               DBG("Successfully recorded snapshot of session `%s` on behalf of trigger `%p`",
-                               session_name, work_item->trigger);
+               DBG("Successfully recorded snapshot of session `%s` on behalf of trigger `%s`",
+                               session_name, get_trigger_name(work_item->trigger));
                break;
        default:
                break;
        default:
-               WARN("Failed to record snapshot of session `%s` on behalf of trigger `%p`: %s",
-                               session_name, work_item->trigger,
+               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;
        }
                                lttng_strerror(-cmd_ret));
                break;
        }
@@ -468,8 +481,8 @@ static int action_executor_group_handler(struct action_executor *executor,
                ret = action_executor_generic_handler(
                                executor, work_item, action);
                if (ret) {
                ret = action_executor_generic_handler(
                                executor, work_item, action);
                if (ret) {
-                       ERR("Stopping the execution of the action group of trigger `%p` following a fatal error",
-                                       work_item->trigger);
+                       ERR("Stopping the execution of the action group of trigger `%s` following a fatal error",
+                                       get_trigger_name(work_item->trigger));
                        goto end;
                }
        }
                        goto end;
                }
        }
@@ -481,12 +494,12 @@ static int action_executor_generic_handler(struct action_executor *executor,
                const struct action_work_item *work_item,
                const struct lttng_action *action)
 {
                const struct action_work_item *work_item,
                const struct lttng_action *action)
 {
-       DBG("Executing action `%s` of trigger `%p` action work item %" PRIu64,
+       DBG("Executing action `%s` of trigger `%s` action work item %" PRIu64,
                        get_action_name(action),
                        get_action_name(action),
-                       work_item->trigger,
+                       get_trigger_name(work_item->trigger),
                        work_item->id);
 
                        work_item->id);
 
-       return action_executors[lttng_action_get_type_const(action)](
+       return action_executors[lttng_action_get_type(action)](
                        executor, work_item, action);
 }
 
                        executor, work_item, action);
 }
 
@@ -497,11 +510,11 @@ static int action_work_item_execute(struct action_executor *executor,
        const struct lttng_action *action =
                        lttng_trigger_get_const_action(work_item->trigger);
 
        const struct lttng_action *action =
                        lttng_trigger_get_const_action(work_item->trigger);
 
-       DBG("Starting execution of action work item %" PRIu64 " of trigger `%p`",
-                       work_item->id, 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);
        ret = action_executor_generic_handler(executor, work_item, action);
-       DBG("Completed execution of action work item %" PRIu64 " of trigger `%p`",
-                       work_item->id, work_item->trigger);
+       DBG("Completed execution of action work item %" PRIu64 " of trigger `%s`",
+                       work_item->id, get_trigger_name(work_item->trigger));
        return ret;
 }
 
        return ret;
 }
 
@@ -541,7 +554,7 @@ static void *action_executor_thread(void *_data)
                        continue;
                }
 
                        continue;
                }
 
-               /* Pop item from front of the listwith work lock held. */
+               /* Pop item from front of the list with 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);
                work_item = cds_list_first_entry(&executor->work.list,
                                struct action_work_item, list_node);
                cds_list_del(&work_item->list_node);
@@ -636,8 +649,8 @@ void action_executor_destroy(struct action_executor *executor)
        cds_list_for_each_entry_safe (
                        work_item, tmp, &executor->work.list, list_node) {
                WARN("Discarding action work item %" PRIu64
        cds_list_for_each_entry_safe (
                        work_item, tmp, &executor->work.list, list_node) {
                WARN("Discarding action work item %" PRIu64
-                               " associated to trigger `%p`",
-                               work_item->id, work_item->trigger);
+                               " 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);
        }
                cds_list_del(&work_item->list_node);
                action_work_item_destroy(work_item);
        }
@@ -662,9 +675,9 @@ enum action_executor_status action_executor_enqueue(
        /* Check for queue overflow. */
        if (executor->work.pending_count >= MAX_QUEUED_WORK_COUNT) {
                /* Most likely spammy, remove if it is the case. */
        /* 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 `%p` as work item %" PRIu64
-                   " (overflow)",
-                               trigger, work_item_id);
+               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;
        }
                executor_status = ACTION_EXECUTOR_STATUS_OVERFLOW;
                goto error_unlock;
        }
@@ -672,7 +685,7 @@ enum action_executor_status action_executor_enqueue(
        work_item = zmalloc(sizeof(*work_item));
        if (!work_item) {
                PERROR("Failed to allocate action executor work item on behalf of trigger `%p`",
        work_item = zmalloc(sizeof(*work_item));
        if (!work_item) {
                PERROR("Failed to allocate action executor work item on behalf of trigger `%p`",
-                               trigger);
+                               get_trigger_name(trigger));
                executor_status = ACTION_EXECUTOR_STATUS_ERROR;
                goto error_unlock;
        }
                executor_status = ACTION_EXECUTOR_STATUS_ERROR;
                goto error_unlock;
        }
@@ -703,7 +716,7 @@ enum action_executor_status action_executor_enqueue(
        cds_list_add_tail(&work_item->list_node, &executor->work.list);
        executor->work.pending_count++;
        DBG("Enqueued action for trigger `%p` as work item %" PRIu64,
        cds_list_add_tail(&work_item->list_node, &executor->work.list);
        executor->work.pending_count++;
        DBG("Enqueued action for trigger `%p` as work item %" PRIu64,
-                       trigger, work_item_id);
+                       get_trigger_name(trigger), work_item_id);
        signal = true;
 
 error_unlock:
        signal = true;
 
 error_unlock:
index 06ef377a3a181c1b7528df89b076340f789c0f70..44f41059bc7ae2cd277b590a67423cc4a9df7b7c 100644 (file)
@@ -59,6 +59,8 @@ static void update_agent_app(const struct agent_app *app)
 {
        struct ltt_session *session, *stmp;
        struct ltt_session_list *list;
 {
        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);
 
        list = session_get_list();
        assert(list);
@@ -82,6 +84,14 @@ static void update_agent_app(const struct agent_app *app)
                session_unlock(session);
                session_put(session);
        }
                session_unlock(session);
                session_put(session);
        }
+
+       /* 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);
+       }
+       rcu_read_unlock();
 }
 
 /*
 }
 
 /*
index e5978be5cef3ac24ecc7b7255cff563f87b13580..66c55fc46e870275a659af9f543d2089b12b20aa 100644 (file)
 #include <urcu/uatomic.h>
 #include <urcu/rculist.h>
 
 #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>
 
 #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)
  */
 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->enabled = 1;
+       event->user_refcount++;
        ret = LTTNG_OK;
 
 error:
        ret = LTTNG_OK;
 
 error:
@@ -791,6 +799,17 @@ int agent_disable_event(struct agent_event *event,
                goto end;
        }
 
                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,
        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:
        event->enabled = 0;
 
 error:
@@ -1109,7 +1129,7 @@ error:
  */
 struct agent_event *agent_create_event(const char *name,
                enum lttng_loglevel_type loglevel_type, int loglevel_value,
  */
 struct agent_event *agent_create_event(const char *name,
                enum lttng_loglevel_type loglevel_type, int loglevel_value,
-               struct lttng_filter_bytecode *filter, char *filter_expression)
+               struct lttng_bytecode *filter, char *filter_expression)
 {
        struct agent_event *event = NULL;
 
 {
        struct agent_event *event = NULL;
 
@@ -1206,6 +1226,66 @@ void agent_find_events_by_name(const char *name, struct agent *agt,
                        ht_match_event_by_name, &key, &iter->iter);
 }
 
                        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_log_level_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_log_level(
+                               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.
 /*
  * 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,
  * 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;
 {
        struct lttng_ht_node_str *node;
        struct lttng_ht_iter iter;
@@ -1347,6 +1429,21 @@ int agent_app_ht_alloc(void)
        return ret;
 }
 
        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.
  */
 /*
  * Destroy a agent application by socket.
  */
@@ -1396,6 +1493,32 @@ void agent_app_ht_clean(void)
        lttng_ht_destroy(agent_apps_ht_by_sock);
 }
 
        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.
  *
 /*
  * Update a agent application (given socket) using the given agent.
  *
@@ -1447,3 +1570,27 @@ void agent_update(const struct agent *agt, const struct agent_app *app)
 
        rcu_read_unlock();
 }
 
        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 f8e67efdae82b7130e951d721859841991fa2e76..72aa48108cfc5f4cacc2d0e5a3851d5ee38e9191 100644 (file)
  */
 extern struct lttng_ht *agent_apps_ht_by_sock;
 
  */
 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;
 struct agent_ht_key {
        const char *name;
        int loglevel_value;
        enum lttng_loglevel_type loglevel_type;
-       char *filter_expression;
+       const char *filter_expression;
 };
 
 /*
 };
 
 /*
@@ -84,9 +88,15 @@ struct agent_event {
        struct lttng_ht_node_str node;
 
        /* Filter associated with the event. NULL if none. */
        struct lttng_ht_node_str node;
 
        /* Filter associated with the event. NULL if none. */
-       struct lttng_filter_bytecode *filter;
+       struct lttng_bytecode *filter;
        char *filter_expression;
        struct lttng_event_exclusion *exclusion;
        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;
 };
 
 /*
 };
 
 /*
@@ -128,13 +138,15 @@ void agent_add(struct agent *agt, struct lttng_ht *ht);
 /* Agent event API. */
 struct agent_event *agent_create_event(const char *name,
                enum lttng_loglevel_type loglevel_type, int loglevel_value,
 /* Agent event API. */
 struct agent_event *agent_create_event(const char *name,
                enum lttng_loglevel_type loglevel_type, int loglevel_value,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                char *filter_expression);
 void agent_add_event(struct agent_event *event, struct agent *agt);
 
 struct agent_event *agent_find_event(const char *name,
                char *filter_expression);
 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,
 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(const struct agent *agt, const struct agent_app *app);
 int agent_list_events(struct lttng_event **events,
                enum lttng_domain_type domain);
 
 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 */
 #endif /* LTTNG_SESSIOND_AGENT_H */
index 8a2ef85b7345c33d89cd2914134bd075a71b4e9e..b1c8cee9f8d1ecd5377e6a04814bcccbd63802e5 100644 (file)
@@ -42,6 +42,7 @@
 #include "utils.h"
 #include "manage-consumer.h"
 #include "clear.h"
 #include "utils.h"
 #include "manage-consumer.h"
 #include "clear.h"
+#include "agent-thread.h"
 
 static bool is_root;
 
 
 static bool is_root;
 
@@ -770,6 +771,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 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);
 
 
        DBG("Processing client command %d", cmd_ctx->lsm.cmd_type);
 
@@ -793,19 +795,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_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_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;
        }
 
                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) {
        if (config.no_kernel && need_domain
                        && cmd_ctx->lsm.domain.type == LTTNG_DOMAIN_KERNEL) {
                if (!is_root) {
@@ -846,6 +858,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock,
        case LTTNG_DATA_PENDING:
        case LTTNG_ROTATE_SESSION:
        case LTTNG_ROTATION_GET_INFO:
        case LTTNG_DATA_PENDING:
        case LTTNG_ROTATE_SESSION:
        case LTTNG_ROTATION_GET_INFO:
+       case LTTNG_REGISTER_TRIGGER:
+       case LTTNG_LIST_TRIGGERS:
                break;
        default:
                /* Setup lttng message with no payload */
                break;
        default:
                /* Setup lttng message with no payload */
@@ -866,6 +880,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_SAVE_SESSION:
        case LTTNG_REGISTER_TRIGGER:
        case LTTNG_UNREGISTER_TRIGGER:
+       case LTTNG_LIST_TRIGGERS:
                need_tracing_session = 0;
                break;
        default:
                need_tracing_session = 0;
                break;
        default:
@@ -944,7 +959,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock,
                }
 
                /* Consumer is in an ERROR state. Report back to client */
                }
 
                /* 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;
                }
                        ret = LTTNG_ERR_NO_KERNCONSUMERD;
                        goto error;
                }
@@ -989,14 +1005,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:
        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;
                }
        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 */
                /* 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;
                }
                        ret = LTTNG_ERR_NO_USTCONSUMERD;
                        goto error;
                }
@@ -1445,7 +1468,7 @@ error_add_context:
        {
                struct lttng_event *ev = NULL;
                struct lttng_event_exclusion *exclusion = NULL;
        {
                struct lttng_event *ev = NULL;
                struct lttng_event_exclusion *exclusion = NULL;
-               struct lttng_filter_bytecode *bytecode = NULL;
+               struct lttng_bytecode *bytecode = NULL;
                char *filter_expression = NULL;
 
                /* Handle exclusion events and receive it from the client. */
                char *filter_expression = NULL;
 
                /* Handle exclusion events and receive it from the client. */
@@ -2024,8 +2047,39 @@ error_add_context:
        }
        case LTTNG_REGISTER_TRIGGER:
        {
        }
        case LTTNG_REGISTER_TRIGGER:
        {
+               struct lttng_trigger *return_trigger;
+               size_t original_payload_size;
+               size_t payload_size;
+
+               ret = setup_empty_lttng_msg(cmd_ctx);
+               if (ret) {
+                       ret = LTTNG_ERR_NOMEM;
+                       goto setup_error;
+               }
+
+               original_payload_size = cmd_ctx->reply_payload.buffer.size;
+
                ret = cmd_register_trigger(cmd_ctx, *sock,
                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, &cmd_ctx->reply_payload);
+               if (ret) {
+                       ERR("Failed to serialize trigger in reply to \"register trigger\" command");
+                       ret = LTTNG_ERR_NOMEM;
+                       lttng_trigger_destroy(return_trigger);
+                       goto error;
+               }
+               lttng_trigger_destroy(return_trigger);
+
+               payload_size = cmd_ctx->reply_payload.buffer.size -
+                       original_payload_size;
+
+               update_lttng_msg(cmd_ctx, 0, payload_size);
+
+               ret = LTTNG_OK;
                break;
        }
        case LTTNG_UNREGISTER_TRIGGER:
                break;
        }
        case LTTNG_UNREGISTER_TRIGGER:
@@ -2138,6 +2192,42 @@ error_add_context:
                ret = cmd_clear_session(cmd_ctx->session, sock);
                break;
        }
                ret = cmd_clear_session(cmd_ctx->session, sock);
                break;
        }
+       case LTTNG_LIST_TRIGGERS:
+       {
+               struct lttng_triggers *return_triggers;
+               size_t original_payload_size;
+               size_t payload_size;
+
+               ret = setup_empty_lttng_msg(cmd_ctx);
+               if (ret) {
+                       ret = LTTNG_ERR_NOMEM;
+                       goto setup_error;
+               }
+
+               original_payload_size = cmd_ctx->reply_payload.buffer.size;
+
+               ret = cmd_list_triggers(cmd_ctx,
+                               notification_thread_handle, &return_triggers);
+               if (ret != LTTNG_OK) {
+                       goto error;
+               }
+
+               ret = lttng_triggers_serialize(
+                               return_triggers, &cmd_ctx->reply_payload);
+               lttng_triggers_destroy(return_triggers);
+               if (ret) {
+                       ERR("Failed to serialize triggers in reply to \"list triggers\" command");
+                       ret = LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+               payload_size = cmd_ctx->reply_payload.buffer.size -
+                       original_payload_size;
+
+               update_lttng_msg(cmd_ctx, 0, payload_size);
+
+               ret = LTTNG_OK;
+               break;
+       }
        default:
                ret = LTTNG_ERR_UND;
                break;
        default:
                ret = LTTNG_ERR_UND;
                break;
index dc442035a3fe21e6b4c487a1007c9f05d1b38c56..65553a538a1e62a6e570a5edbf127d741ef4c40e 100644 (file)
 #include <lttng/location-internal.h>
 #include <lttng/trigger/trigger-internal.h>
 #include <lttng/condition/condition.h>
 #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>
 #include <lttng/action/action.h>
 #include <lttng/channel.h>
 #include <lttng/channel-internal.h>
@@ -110,7 +117,7 @@ static int cmd_enable_event_internal(struct ltt_session *session,
                const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
                const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                int wpipe);
 
                struct lttng_event_exclusion *exclusion,
                int wpipe);
 
@@ -2058,7 +2065,7 @@ static int _cmd_enable_event(struct ltt_session *session,
                const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
                const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                int wpipe, bool internal_event)
 {
                struct lttng_event_exclusion *exclusion,
                int wpipe, bool internal_event)
 {
@@ -2141,7 +2148,7 @@ static int _cmd_enable_event(struct ltt_session *session,
                case LTTNG_EVENT_ALL:
                {
                        char *filter_expression_a = NULL;
                case LTTNG_EVENT_ALL:
                {
                        char *filter_expression_a = NULL;
-                       struct lttng_filter_bytecode *filter_a = NULL;
+                       struct lttng_bytecode *filter_a = NULL;
 
                        /*
                         * We need to duplicate filter_expression and filter,
 
                        /*
                         * We need to duplicate filter_expression and filter,
@@ -2376,11 +2383,11 @@ static int _cmd_enable_event(struct ltt_session *session,
 
                {
                        char *filter_expression_copy = NULL;
 
                {
                        char *filter_expression_copy = NULL;
-                       struct lttng_filter_bytecode *filter_copy = NULL;
+                       struct lttng_bytecode *filter_copy = NULL;
 
                        if (filter) {
                                const size_t filter_size = sizeof(
 
                        if (filter) {
                                const size_t filter_size = sizeof(
-                                               struct lttng_filter_bytecode)
+                                               struct lttng_bytecode)
                                                + filter->len;
 
                                filter_copy = zmalloc(filter_size);
                                                + filter->len;
 
                                filter_copy = zmalloc(filter_size);
@@ -2456,7 +2463,7 @@ int cmd_enable_event(struct ltt_session *session,
                const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
                const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                int wpipe)
 {
                struct lttng_event_exclusion *exclusion,
                int wpipe)
 {
@@ -2473,7 +2480,7 @@ static int cmd_enable_event_internal(struct ltt_session *session,
                const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
                const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                int wpipe)
 {
                struct lttng_event_exclusion *exclusion,
                int wpipe)
 {
@@ -4256,8 +4263,59 @@ end:
        return ret;
 }
 
        return ret;
 }
 
+/*
+ * On success LTTNG_OK. On error, returns lttng_error code.
+ */
+static enum lttng_error_code prepare_trigger_object(struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds)
+{
+       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;
+               lttng_condition_event_rule_get_rule_mutable(
+                               condition, &event_rule);
+               ret = lttng_event_rule_generate_filter_bytecode(
+                               event_rule, lttng_credentials_get_uid(cmd_creds), lttng_credentials_get_gid(cmd_creds));
+               if (ret != LTTNG_OK) {
+                       goto end;
+               }
+
+               /* Generate the capture bytecode set */
+               ret = lttng_condition_event_rule_generate_capture_descriptor_bytecode_set(
+                               condition, &trigger->capture_bytecode_set);
+               if (ret != LTTNG_OK) {
+                       goto end;
+               }
+
+               ret = LTTNG_OK;
+               break;
+       }
+       default:
+       {
+               ret = LTTNG_OK;
+               break;
+       }
+       }
+
+end:
+       return ret;
+}
+
 int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock,
 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;
 {
        int ret;
        size_t trigger_len;
@@ -4265,8 +4323,8 @@ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock,
        struct lttng_trigger *trigger = NULL;
        struct lttng_payload trigger_payload;
        struct lttng_credentials cmd_creds = {
        struct lttng_trigger *trigger = NULL;
        struct lttng_payload trigger_payload;
        struct lttng_credentials cmd_creds = {
-               .uid = cmd_ctx->creds.uid,
-               .gid = cmd_ctx->creds.gid,
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid),
        };
 
        lttng_payload_init(&trigger_payload);
        };
 
        lttng_payload_init(&trigger_payload);
@@ -4314,13 +4372,118 @@ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock,
                }
        }
 
                }
        }
 
-       /* Set the trigger credential */
-       lttng_trigger_set_credentials(trigger, &cmd_creds);
+       /*
+        * Validate the trigger credentials against the command credentials.
+        * Only the root user can register a trigger with non-matching
+        * credentials.
+        */
+       if (!lttng_credentials_is_equal_uid(
+                       lttng_trigger_get_credentials(trigger),
+                       &cmd_creds)) {
+               if (lttng_credentials_get_uid(&cmd_creds) != 0) {
+                       ERR("Trigger credentials do not match the command credentials");
+                       ret = LTTNG_ERR_INVALID_TRIGGER;
+                       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);
+
+       /* 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, &cmd_creds);
+       if (ret != LTTNG_OK) {
+               goto end;
+       }
 
        /* Inform the notification thread */
        ret = notification_thread_command_register_trigger(notification_thread,
                        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
+                                */
+                               ret = kernel_register_trigger(trigger, &cmd_creds);
+                               if (ret != LTTNG_OK) {
+                                       enum lttng_error_code notif_thread_unregister_ret =
+                                                       notification_thread_command_unregister_trigger(
+                                                                       notification_thread,
+                                                                       trigger);
+                                       if (notif_thread_unregister_ret != LTTNG_OK) {
+                                               ERR("Error unregistering notification thread trigger after kernel registration failure.");
+                                       }
+                                       goto end;
+
+                               }
+                       } 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);
        trigger = NULL;
 end:
        lttng_trigger_destroy(trigger);
@@ -4337,8 +4500,8 @@ int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock,
        struct lttng_trigger *trigger = NULL;
        struct lttng_payload trigger_payload;
        struct lttng_credentials cmd_creds = {
        struct lttng_trigger *trigger = NULL;
        struct lttng_payload trigger_payload;
        struct lttng_credentials cmd_creds = {
-               .uid = cmd_ctx->creds.uid,
-               .gid = cmd_ctx->creds.gid,
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid),
        };
 
        lttng_payload_init(&trigger_payload);
        };
 
        lttng_payload_init(&trigger_payload);
@@ -4385,16 +4548,114 @@ int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock,
                }
        }
 
                }
        }
 
-       lttng_trigger_set_credentials(trigger, &cmd_creds);
+       /*
+        * Validate the trigger credentials against the command credentials.
+        * Only the root user can unregister a trigger with non-matching
+        * credentials.
+        */
+       if (!lttng_credentials_is_equal_uid(
+                       lttng_trigger_get_credentials(trigger),
+                       &cmd_creds)) {
+               if (lttng_credentials_get_uid(&cmd_creds) != 0) {
+                       ERR("Trigger credentials do not match the command credentials");
+                       ret = LTTNG_ERR_INVALID_TRIGGER;
+                       goto end;
+               }
+       }
+
+       ret = prepare_trigger_object(trigger, &cmd_creds);
+       if (ret != LTTNG_OK) {
+               goto end;
+       }
 
        ret = notification_thread_command_unregister_trigger(notification_thread,
                        trigger);
 
        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
+                                */
+                               ret = kernel_unregister_trigger(trigger);
+                       } 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_payload_reset(&trigger_payload);
        return ret;
 }
 
 end:
        lttng_trigger_destroy(trigger);
        lttng_payload_reset(&trigger_payload);
        return ret;
 }
 
+int cmd_list_triggers(struct command_ctx *cmd_ctx,
+               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, cmd_ctx->creds.uid, &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.
 /*
  * Send relayd sockets from snapshot output to consumer. Ignore request if the
  * snapshot output is *not* set with a remote destination.
index 76c53ea333da17c0fd09de321f435476183728c4..15763b59789b52db6817ee7c7b7b2549be920f44 100644 (file)
@@ -86,11 +86,11 @@ int cmd_add_context(struct ltt_session *session, enum lttng_domain_type domain,
                char *channel_name, const struct lttng_event_context *ctx, int kwpipe);
 int cmd_set_filter(struct ltt_session *session, enum lttng_domain_type domain,
                char *channel_name, struct lttng_event *event,
                char *channel_name, const struct lttng_event_context *ctx, int kwpipe);
 int cmd_set_filter(struct ltt_session *session, enum lttng_domain_type domain,
                char *channel_name, struct lttng_event *event,
-               struct lttng_filter_bytecode *bytecode);
+               struct lttng_bytecode *bytecode);
 int cmd_enable_event(struct ltt_session *session, const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
 int cmd_enable_event(struct ltt_session *session, const struct lttng_domain *domain,
                char *channel_name, struct lttng_event *event,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                int wpipe);
 
                struct lttng_event_exclusion *exclusion,
                int wpipe);
 
@@ -142,10 +142,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,
 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_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,
+               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,
 int cmd_rotate_session(struct ltt_session *session,
                struct lttng_rotate_session_return *rotate_return,
                bool quiet_rotation,
diff --git a/src/bin/lttng-sessiond/condition-internal.c b/src/bin/lttng-sessiond/condition-internal.c
new file mode 100644 (file)
index 0000000..43c214c
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/hashtable/utils.h>
+#include <common/hashtable/hashtable.h>
+
+#include <lttng/condition/condition.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 "condition-internal.h"
+
+static
+unsigned long lttng_condition_buffer_usage_hash(
+       const struct lttng_condition *_condition)
+{
+       unsigned long hash;
+       unsigned long condition_type;
+       struct lttng_condition_buffer_usage *condition;
+
+       condition = container_of(_condition,
+                       struct lttng_condition_buffer_usage, parent);
+
+       condition_type = (unsigned long) condition->parent.type;
+       hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
+       if (condition->session_name) {
+               hash ^= hash_key_str(condition->session_name, lttng_ht_seed);
+       }
+       if (condition->channel_name) {
+               hash ^= hash_key_str(condition->channel_name, lttng_ht_seed);
+       }
+       if (condition->domain.set) {
+               hash ^= hash_key_ulong(
+                               (void *) condition->domain.type,
+                               lttng_ht_seed);
+       }
+       if (condition->threshold_ratio.set) {
+               uint64_t val;
+
+               val = condition->threshold_ratio.value * (double) UINT32_MAX;
+               hash ^= hash_key_u64(&val, lttng_ht_seed);
+       } else if (condition->threshold_bytes.set) {
+               uint64_t val;
+
+               val = condition->threshold_bytes.value;
+               hash ^= hash_key_u64(&val, lttng_ht_seed);
+       }
+       return hash;
+}
+
+static
+unsigned long lttng_condition_session_consumed_size_hash(
+       const struct lttng_condition *_condition)
+{
+       unsigned long hash;
+       unsigned long condition_type =
+                       (unsigned long) LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE;
+       struct lttng_condition_session_consumed_size *condition;
+       uint64_t val;
+
+       condition = container_of(_condition,
+                       struct lttng_condition_session_consumed_size, parent);
+
+       hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
+       if (condition->session_name) {
+               hash ^= hash_key_str(condition->session_name, lttng_ht_seed);
+       }
+       val = condition->consumed_threshold_bytes.value;
+       hash ^= hash_key_u64(&val, lttng_ht_seed);
+       return hash;
+}
+
+static
+unsigned long lttng_condition_session_rotation_hash(
+       const struct lttng_condition *_condition)
+{
+       unsigned long hash, condition_type;
+       struct lttng_condition_session_rotation *condition;
+
+       condition = container_of(_condition,
+                       struct lttng_condition_session_rotation, parent);
+       condition_type = (unsigned long) condition->parent.type;
+       hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
+       assert(condition->session_name);
+       hash ^= hash_key_str(condition->session_name, lttng_ht_seed);
+       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
+ * don't want to link in liblttng-ctl.
+ */
+unsigned long lttng_condition_hash(const struct lttng_condition *condition)
+{
+       switch (condition->type) {
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+               return lttng_condition_buffer_usage_hash(condition);
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+               return lttng_condition_session_consumed_size_hash(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();
+       }
+}
diff --git a/src/bin/lttng-sessiond/condition-internal.h b/src/bin/lttng-sessiond/condition-internal.h
new file mode 100644 (file)
index 0000000..2863f7b
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_SESSIOND_CONDITION_INTERNAL_H
+#define LTTNG_SESSIOND_CONDITION_INTERNAL_H
+
+#include <lttng/condition/condition.h>
+
+/*
+ * 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
+ * don't want to link in liblttng-ctl.
+ */
+unsigned long lttng_condition_hash(const struct lttng_condition *condition);
+
+#endif /* LTTNG_SESSIOND_CONDITION_INTERNAL_H */
index 4bd9d80abc5655283435fa34473ddbb0215eda5b..115a46e2e1898c1e49213ef2dde91aaf07309ce9 100644 (file)
@@ -951,8 +951,10 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg,
                assert(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK);
                LTTNG_OPTIONAL_SET(&msg->u.ask_channel.chunk_id, chunk_id);
        }
                assert(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK);
                LTTNG_OPTIONAL_SET(&msg->u.ask_channel.chunk_id, chunk_id);
        }
-       msg->u.ask_channel.buffer_credentials.uid = buffer_credentials->uid;
-       msg->u.ask_channel.buffer_credentials.gid = buffer_credentials->gid;
+       msg->u.ask_channel.buffer_credentials.uid =
+                       lttng_credentials_get_uid(buffer_credentials);
+       msg->u.ask_channel.buffer_credentials.gid =
+                       lttng_credentials_get_gid(buffer_credentials);
 
        msg->cmd_type = LTTNG_CONSUMER_ASK_CHANNEL_CREATION;
        msg->u.ask_channel.subbuf_size = subbuf_size;
 
        msg->cmd_type = LTTNG_CONSUMER_ASK_CHANNEL_CREATION;
        msg->u.ask_channel.subbuf_size = subbuf_size;
@@ -1930,9 +1932,9 @@ int consumer_create_trace_chunk(struct consumer_socket *socket,
                assert(domain_dirfd >= 0);
 
                msg.u.create_trace_chunk.credentials.value.uid =
                assert(domain_dirfd >= 0);
 
                msg.u.create_trace_chunk.credentials.value.uid =
-                               chunk_credentials.uid;
+                               lttng_credentials_get_uid(&chunk_credentials);
                msg.u.create_trace_chunk.credentials.value.gid =
                msg.u.create_trace_chunk.credentials.value.gid =
-                               chunk_credentials.gid;
+                               lttng_credentials_get_gid(&chunk_credentials);
                msg.u.create_trace_chunk.credentials.is_set = 1;
        }
 
                msg.u.create_trace_chunk.credentials.is_set = 1;
        }
 
index 4fe3dfce70e40d22a05d361e3071e8966e610258..461c56d64a870502acd08cb332aa2ffedc05a3f7 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 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) {
 
        /* 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;
        }
 
                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) {
        /* 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;
                }
                if (!session_get(sess)) {
                        continue;
                }
@@ -55,26 +71,15 @@ static void update_ust_app(int app_sock)
                        goto unlock_session;
                }
 
                        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);
                ust_app_global_update(sess->ust_session, app);
-       unlock_rcu:
-               rcu_read_unlock();
        unlock_session:
                session_unlock(sess);
                session_put(sess);
        }
        unlock_session:
                session_unlock(sess);
                session_put(sess);
        }
+
+unlock_rcu:
+       rcu_read_unlock();
+
 }
 
 /*
 }
 
 /*
@@ -386,6 +391,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);
 
                                /* 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,
                                /* Send notify socket through the notify pipe. */
                                ret = send_socket_to_thread(
                                                notifiers->apps_cmd_notify_pipe_write_fd,
index 189236beb1171a05ba21e89b3ecb51216a7b5e47..28f296a037989bfa75f70f7b1444e85a4f5ffd8c 100644 (file)
 #include <string.h>
 
 #include <lttng/lttng.h>
 #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/bytecode/bytecode.h>
 #include <common/error.h>
 #include <common/sessiond-comm/sessiond-comm.h>
 #include <common/filter.h>
 #include <common/error.h>
 #include <common/sessiond-comm/sessiond-comm.h>
 #include <common/filter.h>
@@ -27,6 +32,7 @@
 #include "trace-kernel.h"
 #include "trace-ust.h"
 #include "agent.h"
 #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.
 
 /*
  * Add unique UST event based on the event name, filter bytecode and loglevel.
@@ -42,7 +48,7 @@ static void add_unique_ust_event(struct lttng_ht *ht,
        assert(event);
 
        key.name = event->attr.name;
        assert(event);
 
        key.name = event->attr.name;
-       key.filter = (struct lttng_filter_bytecode *) event->filter;
+       key.filter = (struct lttng_bytecode *) event->filter;
        key.loglevel_type = event->attr.loglevel_type;
        key.loglevel_value = event->attr.loglevel;
        key.exclusion = event->exclusion;
        key.loglevel_type = event->attr.loglevel_type;
        key.loglevel_value = event->attr.loglevel;
        key.exclusion = event->exclusion;
@@ -99,7 +105,7 @@ int event_kernel_disable_event(struct ltt_kernel_channel *kchan,
  */
 int event_kernel_enable_event(struct ltt_kernel_channel *kchan,
                struct lttng_event *event, char *filter_expression,
  */
 int event_kernel_enable_event(struct ltt_kernel_channel *kchan,
                struct lttng_event *event, char *filter_expression,
-               struct lttng_filter_bytecode *filter)
+               struct lttng_bytecode *filter)
 {
        int ret;
        struct ltt_kernel_event *kevent;
 {
        int ret;
        struct ltt_kernel_event *kevent;
@@ -149,7 +155,7 @@ end:
 int event_ust_enable_tracepoint(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct lttng_event *event,
                char *filter_expression,
 int event_ust_enable_tracepoint(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct lttng_event *event,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                bool internal_event)
 {
                struct lttng_event_exclusion *exclusion,
                bool internal_event)
 {
@@ -366,6 +372,20 @@ error:
        return ret;
 }
 
        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.
  *
 /*
  * Enable all agent event for a given UST session.
  *
@@ -373,11 +393,9 @@ error:
  */
 int event_agent_enable_all(struct ltt_ust_session *usess,
                struct agent *agt, struct lttng_event *event,
  */
 int event_agent_enable_all(struct ltt_ust_session *usess,
                struct agent *agt, struct lttng_event *event,
-               struct lttng_filter_bytecode *filter ,char *filter_expression)
+               struct lttng_bytecode *filter ,char *filter_expression)
 {
        int ret;
 {
        int ret;
-       struct agent_event *aevent;
-       struct lttng_ht_iter iter;
 
        assert(usess);
 
 
        assert(usess);
 
@@ -389,13 +407,7 @@ int event_agent_enable_all(struct ltt_ust_session *usess,
                goto error;
        }
 
                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;
 
 
        ret = LTTNG_OK;
 
@@ -410,7 +422,7 @@ error:
  * contexts yet. Not an issue for now, since they are not generated by
  * the lttng-ctl library.
  */
  * contexts yet. Not an issue for now, since they are not generated by
  * the lttng-ctl library.
  */
-static int add_filter_app_ctx(struct lttng_filter_bytecode *bytecode,
+static int add_filter_app_ctx(struct lttng_bytecode *bytecode,
                const char *filter_expression, struct agent *agt)
 {
        int ret = LTTNG_OK;
                const char *filter_expression, struct agent *agt)
 {
        int ret = LTTNG_OK;
@@ -467,28 +479,17 @@ end:
        return ret;
 }
 
        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,
+static int agent_enable(struct agent *agt,
+               struct lttng_event *event,
+               struct lttng_bytecode *filter,
                char *filter_expression)
 {
        int ret, created = 0;
        struct agent_event *aevent;
 
                char *filter_expression)
 {
        int ret, created = 0;
        struct agent_event *aevent;
 
-       assert(usess);
        assert(event);
        assert(agt);
 
        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) {
        aevent = agent_find_event(event->name, event->loglevel_type,
                        event->loglevel, filter_expression, agt);
        if (!aevent) {
@@ -542,6 +543,112 @@ end:
        return ret;
 }
 
        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_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_bytecode *filter_bytecode;
+       struct lttng_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 = bytecode_copy (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_tracer_token(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.
 /*
  * 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;
 }
 
        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_tracer_token(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.
  *
 /*
  * Disable a given agent event for a given UST session.
  *
@@ -659,6 +803,44 @@ error:
        return ret;
 }
 
        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_tracer_token(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_tracer_token(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.
  *
 /*
  * Disable all agent events matching a given name for a given UST session.
  *
index 1c646db37b7d4d7956d1bc50303ed1032415ac3f..8849ee7ccc8ec2bec202dc6ea32c4c74f5ae089a 100644 (file)
@@ -17,12 +17,12 @@ int event_kernel_disable_event(struct ltt_kernel_channel *kchan,
 
 int event_kernel_enable_event(struct ltt_kernel_channel *kchan,
                struct lttng_event *event, char *filter_expression,
 
 int event_kernel_enable_event(struct ltt_kernel_channel *kchan,
                struct lttng_event *event, char *filter_expression,
-               struct lttng_filter_bytecode *filter);
+               struct lttng_bytecode *filter);
 
 int event_ust_enable_tracepoint(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct lttng_event *event,
                char *filter_expression,
 
 int event_ust_enable_tracepoint(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan, struct lttng_event *event,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                bool internal_event);
 int event_ust_disable_tracepoint(struct ltt_ust_session *usess,
                struct lttng_event_exclusion *exclusion,
                bool internal_event);
 int event_ust_disable_tracepoint(struct ltt_ust_session *usess,
@@ -32,16 +32,21 @@ int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan);
 
 int event_agent_enable(struct ltt_ust_session *usess, struct agent *agt,
                struct ltt_ust_channel *uchan);
 
 int event_agent_enable(struct ltt_ust_session *usess, struct agent *agt,
-               struct lttng_event *event, struct lttng_filter_bytecode *filter,
+               struct lttng_event *event, struct lttng_bytecode *filter,
                char *filter_expression);
 int event_agent_enable_all(struct ltt_ust_session *usess, struct agent *agt,
                char *filter_expression);
 int event_agent_enable_all(struct ltt_ust_session *usess, struct agent *agt,
-               struct lttng_event *event, struct lttng_filter_bytecode *filter,
+               struct lttng_event *event, struct lttng_bytecode *filter,
                char *filter_expression);
 
 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);
 
                char *filter_expression);
 
 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 */
 const char *event_get_default_agent_ust_name(enum lttng_domain_type domain);
 
 #endif /* _LTT_EVENT_H */
index 20aa790a7540d4dcfd0adbb8897a7da81ff47582..8681d5d90749d84c5745015d52334f4c65a1bb18 100644 (file)
@@ -20,8 +20,10 @@ long page_size;
 struct health_app *health_sessiond;
 
 struct notification_thread_handle *notification_thread_handle;
 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 *agent_apps_ht_by_sock = NULL;
+struct lttng_ht *trigger_agents_ht_by_domain = NULL;
 
 struct lttng_kernel_tracer_version kernel_tracer_version;
 struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version;
 
 struct lttng_kernel_tracer_version kernel_tracer_version;
 struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version;
index d7c7984d201ba96c82d050eb71a48dca798c8e38..27b72a5afa133ae651b9e294a68871e50c5ee8ef 100644 (file)
@@ -15,6 +15,7 @@
 #include <sys/types.h>
 
 #include <common/common.h>
 #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/trace-chunk.h>
 #include <common/kernel-ctl/kernel-ctl.h>
 #include <common/kernel-ctl/kernel-ioctl.h>
 #include <lttng/lttng-error.h>
 #include <lttng/tracker.h>
 
 #include <lttng/lttng-error.h>
 #include <lttng/tracker.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 "lttng-sessiond.h"
 #include "lttng-syscall.h"
+#include "condition-internal.h"
 #include "consumer.h"
 #include "kernel.h"
 #include "kernel-consumer.h"
 #include "kern-modules.h"
 #include "consumer.h"
 #include "kernel.h"
 #include "kernel-consumer.h"
 #include "kern-modules.h"
+#include "sessiond-config.h"
 #include "utils.h"
 #include "rotate.h"
 #include "modprobe.h"
 #include "tracker.h"
 #include "utils.h"
 #include "rotate.h"
 #include "modprobe.h"
 #include "tracker.h"
+#include "trigger-error-accounting.h"
+#include "notification-thread-commands.h"
 
 /*
  * Key used to reference a channel between the sessiond and the consumer. This
 
 /*
  * Key used to reference a channel between the sessiond and the consumer. This
@@ -45,9 +58,10 @@ static uint64_t next_kernel_channel_key;
 static const char *module_proc_lttng = "/proc/lttng";
 
 static int kernel_tracer_fd = -1;
 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 cds_lfht *kernel_tracer_token_ht;
 
 
-#include <lttng/userspace-probe.h>
-#include <lttng/userspace-probe-internal.h>
 /*
  * Add context on a kernel channel.
  *
 /*
  * Add context on a kernel channel.
  *
@@ -219,6 +233,44 @@ error:
        return -1;
 }
 
        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.
 /*
  * Compute the offset of the instrumentation byte in the binary based on the
  * function probe location using the ELF lookup method.
@@ -230,7 +282,7 @@ error:
 static
 int extract_userspace_probe_offset_function_elf(
                const struct lttng_userspace_probe_location *probe_location,
 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;
 {
        int fd;
        int ret = 0;
@@ -267,8 +319,7 @@ int extract_userspace_probe_offset_function_elf(
                goto end;
        }
 
                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);
        if (ret < 0) {
                DBG("userspace probe offset calculation failed for "
                                "function %s", symbol);
@@ -292,7 +343,7 @@ end:
 static
 int extract_userspace_probe_offset_tracepoint_sdt(
                const struct lttng_userspace_probe_location *probe_location,
 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;
                uint32_t *offsets_count)
 {
        enum lttng_userspace_probe_location_lookup_method_type lookup_method_type;
@@ -338,7 +389,7 @@ int extract_userspace_probe_offset_tracepoint_sdt(
        }
 
        ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name,
        }
 
        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);
        if (ret < 0) {
                DBG("userspace probe offset calculation failed for sdt "
                                "probe %s:%s", provider_name, probe_name);
@@ -359,29 +410,16 @@ end:
        return ret;
 }
 
        return ret;
 }
 
-/*
- * Extract the offsets of the instrumentation point for the different lookup
- * methods.
- */
 static
 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_lookup_method *lookup_method = NULL;
        enum lttng_userspace_probe_location_lookup_method_type type;
-       const struct lttng_userspace_probe_location *location = NULL;
        int ret;
 
        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;
        if (!lookup_method) {
                ret = -1;
                goto end;
@@ -394,7 +432,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev,
                struct lttng_kernel_event_callsite callsite;
                uint64_t offset;
 
                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;
                if (ret) {
                        ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
                        goto end;
@@ -403,8 +442,7 @@ int userspace_probe_add_callsites(struct lttng_event *ev,
                callsite.u.uprobe.offset = offset;
                ret = kernctl_add_callsite(fd, &callsite);
                if (ret) {
                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;
                }
                        ret = LTTNG_ERR_KERN_ENABLE_FAIL;
                        goto end;
                }
@@ -421,8 +459,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev,
                 * This call allocates the offsets buffer. This buffer must be freed
                 * by the caller
                 */
                 * 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;
                if (ret) {
                        ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
                        goto end;
@@ -431,8 +469,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev,
                        callsite.u.uprobe.offset = offsets[i];
                        ret = kernctl_add_callsite(fd, &callsite);
                        if (ret) {
                        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;
                                ret = LTTNG_ERR_KERN_ENABLE_FAIL;
                                free(offsets);
                                goto end;
@@ -449,6 +487,71 @@ end:
        return ret;
 }
 
        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, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds), 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.
 /*
  * Create a kernel event, enable it to the kernel tracer and add it to the
  * channel event list of the kernel session.
@@ -457,7 +560,7 @@ end:
 int kernel_create_event(struct lttng_event *ev,
                struct ltt_kernel_channel *channel,
                char *filter_expression,
 int kernel_create_event(struct lttng_event *ev,
                struct ltt_kernel_channel *channel,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter)
+               struct lttng_bytecode *filter)
 {
        int err, fd;
        enum lttng_error_code ret;
 {
        int err, fd;
        enum lttng_error_code ret;
@@ -518,7 +621,8 @@ int kernel_create_event(struct lttng_event *ev,
        }
 
        if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) {
        }
 
        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;
                }
                if (ret) {
                        goto add_callsite_error;
                }
@@ -675,6 +779,42 @@ error:
        return ret;
 }
 
        return ret;
 }
 
+/*
+ * Disable a kernel trigger.
+ */
+static
+int kernel_disable_token_event_rule(struct ltt_kernel_token_event_rule *event)
+{
+       int ret;
+
+       assert(event);
+
+       rcu_read_lock();
+       cds_lfht_del(kernel_tracer_token_ht, &event->ht_node);
+       rcu_read_unlock();
+
+       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 process_attr_tracker *_kernel_get_process_attr_tracker(
                struct ltt_kernel_session *session,
 static
 struct process_attr_tracker *_kernel_get_process_attr_tracker(
                struct ltt_kernel_session *session,
@@ -1796,6 +1936,7 @@ LTTNG_HIDDEN
 int init_kernel_tracer(void)
 {
        int ret;
 int init_kernel_tracer(void)
 {
        int ret;
+       enum lttng_error_code error_code_ret;
        bool is_root = !getuid();
 
        /* Modprobe lttng kernel modules */
        bool is_root = !getuid();
 
        /* Modprobe lttng kernel modules */
@@ -1827,20 +1968,48 @@ int init_kernel_tracer(void)
        if (ret < 0) {
                goto error_modules;
        }
        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).");
        }
 
        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 {
+
+               error_code_ret = kernel_create_trigger_group_notification_fd(
+                               &kernel_tracer_trigger_group_notification_fd);
+               if (error_code_ret != LTTNG_OK) {
+                       goto error_modules;
+               }
+
+               trigger_error_accounting_register_kernel(kernel_tracer_trigger_group_fd);
+               if (ret < 0) {
+                       goto error_modules;
+               }
+       }
+
+       kernel_tracer_token_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
+               CDS_LFHT_AUTO_RESIZE|CDS_LFHT_ACCOUNTING, NULL);
+       if (!kernel_tracer_token_ht) {
+               goto error_modules;
+       }
+
        DBG("Kernel tracer fd %d", kernel_tracer_fd);
        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 notification 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.");
        }
 
        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:
        return 0;
 
 error_version:
@@ -1875,6 +2044,40 @@ LTTNG_HIDDEN
 void cleanup_kernel_tracer(void)
 {
        int ret;
 void cleanup_kernel_tracer(void)
 {
        int ret;
+       struct cds_lfht_iter iter;
+
+       struct ltt_kernel_token_event_rule *rule = NULL;
+        cds_lfht_for_each_entry(kernel_tracer_token_ht, &iter, rule, ht_node) {
+               kernel_disable_token_event_rule(rule);
+               trace_kernel_destroy_token_event_rule(rule);
+               cds_lfht_del(kernel_tracer_token_ht, &rule->ht_node);
+       }
+
+       DBG2("Closing kernel trigger group notification fd");
+       if (kernel_tracer_trigger_group_notification_fd >= 0) {
+               
+               ret = notification_thread_command_remove_application(
+                               notification_thread_handle,
+                               kernel_tracer_trigger_group_notification_fd);
+               if (ret != LTTNG_OK) {
+                       ERR("Failed to remove kernel trigger notification from notification thread");
+               }
+               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) {
 
        DBG2("Closing kernel fd");
        if (kernel_tracer_fd >= 0) {
@@ -1884,6 +2087,7 @@ void cleanup_kernel_tracer(void)
                }
                kernel_tracer_fd = -1;
        }
                }
                kernel_tracer_fd = -1;
        }
+
        DBG("Unloading kernel modules");
        modprobe_remove_lttng_all();
        free(syscall_table);
        DBG("Unloading kernel modules");
        modprobe_remove_lttng_all();
        free(syscall_table);
@@ -1974,3 +2178,322 @@ end:
        rcu_read_unlock();
        return status;
 }
        rcu_read_unlock();
        return status;
 }
+
+enum lttng_error_code kernel_create_trigger_group_notification_fd(
+               int *trigger_group_notification_fd)
+{
+       enum lttng_error_code error_code_ret;
+       int local_fd = -1, ret;
+
+       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");
+               error_code_ret = LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD;
+               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");
+               error_code_ret = LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD;
+               goto error;
+       }
+
+       DBG("Kernel trigger group notification created (fd: %d)",
+                       local_fd);
+       error_code_ret = LTTNG_OK;
+       *trigger_group_notification_fd = local_fd;
+
+error:
+       return error_code_ret;
+}
+
+enum lttng_error_code kernel_create_trigger_group_error_counter(
+               int *error_counter_fd, size_t nb_bucket)
+{
+       enum lttng_error_code error_code_ret;
+       int local_fd = -1, ret;
+       struct lttng_kernel_counter_conf error_counter_conf;
+
+       assert(error_counter_fd);
+
+       error_counter_conf.arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR;
+       error_counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_64BITS;
+       error_counter_conf.global_sum_step = 0;
+       error_counter_conf.number_dimensions = 1;
+       error_counter_conf.dimensions[0].size = nb_bucket;
+       error_counter_conf.dimensions[0].has_underflow = false;
+       error_counter_conf.dimensions[0].has_overflow = false;
+
+       ret = kernctl_create_trigger_group_error_counter(
+                       kernel_tracer_trigger_group_fd, &error_counter_conf);
+       if (ret < 0) {
+               PERROR("ioctl kernel create trigger group error counter");
+               error_code_ret = LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER;
+               goto error;
+       }
+
+       /* Store locally */
+       local_fd = ret;
+
+       /* Prevent fd duplication after execlp() */
+       ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC);
+       if (ret < 0) {
+               error_code_ret = LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER;
+               PERROR("fcntl session fd");
+       }
+
+       DBG("Kernel trigger group error counter (fd: %d)",
+                       local_fd);
+       error_code_ret = LTTNG_OK;
+
+error:
+       *error_counter_fd = local_fd;
+       return error_code_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
+unsigned long hash_trigger(struct lttng_trigger *trigger)
+{
+       const struct lttng_condition *condition =
+                       lttng_trigger_get_const_condition(trigger);
+       return lttng_condition_hash(condition);
+}
+
+static
+int match_trigger(struct cds_lfht_node *node, const void *key)
+{
+       struct ltt_kernel_token_event_rule *token;
+       const struct lttng_trigger *trigger = key;
+
+       token = caa_container_of(node, struct ltt_kernel_token_event_rule, ht_node);
+
+       return lttng_trigger_is_equal(trigger, token->trigger);
+}
+
+static enum lttng_error_code kernel_create_token_event_rule(struct lttng_trigger *trigger,
+               const struct lttng_credentials *creds, uint64_t token)
+{
+       int err, fd, ret = 0;
+       enum lttng_error_code error_code_ret;
+       struct ltt_kernel_token_event_rule *event;
+       struct lttng_kernel_trigger kernel_trigger = {};
+       unsigned int capture_bytecode_count = 0;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+
+       assert(trigger);
+
+       condition = lttng_trigger_get_condition(trigger);
+       assert(condition);
+       assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+       lttng_condition_event_rule_get_rule_mutable(condition, &event_rule);
+       assert(event_rule);
+       assert(lttng_event_rule_get_type(event_rule) != LTTNG_EVENT_RULE_TYPE_UNKNOWN);
+
+       error_code_ret = trace_kernel_create_token_event_rule(trigger, token,
+                       lttng_trigger_get_error_counter_index(trigger), &event);
+       if (error_code_ret != LTTNG_OK) {
+               goto error;
+       }
+
+       trace_kernel_init_trigger_from_event_rule(event_rule, &kernel_trigger);
+       kernel_trigger.id = event->token;
+       kernel_trigger.error_counter_idx = lttng_trigger_get_error_counter_index(trigger);
+
+       fd = kernctl_create_trigger(kernel_tracer_trigger_group_fd, &kernel_trigger);
+       if (fd < 0) {
+               switch (-fd) {
+               case EEXIST:
+                       error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST;
+                       break;
+               case ENOSYS:
+                       WARN("Trigger type not implemented");
+                       error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS;
+                       break;
+               case ENOENT:
+                       WARN("Event %s not found!", kernel_trigger.name);
+                       error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+                       break;
+               default:
+                       error_code_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:
+                               error_code_ret = LTTNG_ERR_FILTER_NOMEM;
+                               break;
+                       default:
+                               error_code_ret = LTTNG_ERR_FILTER_INVAL;
+                               break;
+                       }
+                       goto filter_error;
+               }
+       }
+
+       if (lttng_event_rule_get_type(event_rule) ==
+                       LTTNG_EVENT_RULE_TYPE_UPROBE) {
+               ret = userspace_probe_event_rule_add_callsites(
+                               event_rule, creds, event->fd);
+               if (ret) {
+                       error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+                       goto add_callsite_error;
+               }
+       }
+
+       /* Set the capture bytecode */
+       capture_bytecode_count = lttng_trigger_get_capture_bytecode_count(trigger);
+       for (unsigned int i = 0; i < capture_bytecode_count; i++) {
+               const struct lttng_bytecode *capture_bytecode = lttng_trigger_get_capture_bytecode_at_index(trigger, i);
+               ret = kernctl_capture(event->fd, capture_bytecode);
+               if (ret < 0) {
+                       error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+                       goto error;
+               }
+       }
+
+       err = kernctl_enable(event->fd);
+       if (err < 0) {
+               switch (-err) {
+               case EEXIST:
+                       error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST;
+                       break;
+               default:
+                       PERROR("enable kernel trigger");
+                       error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+                       break;
+               }
+               goto enable_error;
+       }
+
+       /* Add trigger to kernel token mapping in the hashtable. */
+       cds_lfht_add(kernel_tracer_token_ht, hash_trigger(trigger),
+                       &event->ht_node);
+
+       DBG("Trigger %s created (fd: %d)", kernel_trigger.name, event->fd);
+
+       return LTTNG_OK;
+
+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 error_code_ret;
+}
+
+enum lttng_error_code kernel_register_trigger(struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds)
+{
+       enum lttng_error_code ret;
+       struct lttng_condition *condition;
+       struct lttng_event_rule *event_rule;
+       uint64_t token;
+
+       /* TODO error handling */
+
+       rcu_read_lock();
+       pthread_mutex_lock(&notification_trigger_tokens_ht_lock);
+
+       /* TODO: error checking and type checking */
+       token = lttng_trigger_get_tracer_token(trigger);
+       condition = lttng_trigger_get_condition(trigger);
+       (void) lttng_condition_event_rule_get_rule_mutable(condition, &event_rule);
+
+       assert(lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL);
+
+       ret = kernel_create_token_event_rule(trigger, cmd_creds, token);
+       if (ret != LTTNG_OK) {
+               ERR("Failed to create kernel trigger token.");
+       }
+
+       rcu_read_unlock();
+       pthread_mutex_unlock(&notification_trigger_tokens_ht_lock);
+       return ret;
+}
+
+enum lttng_error_code kernel_unregister_trigger(struct lttng_trigger *trigger)
+{
+       struct ltt_kernel_token_event_rule *token_event_rule_element;
+       struct cds_lfht_node *node;
+       struct cds_lfht_iter iter;
+       enum lttng_error_code error_code_ret;
+       int ret;
+
+       rcu_read_lock();
+       pthread_mutex_unlock(&notification_trigger_tokens_ht_lock);
+
+       cds_lfht_lookup(kernel_tracer_token_ht, hash_trigger(trigger),
+                       match_trigger, trigger, &iter);
+       node = cds_lfht_iter_get_node(&iter);
+       if (!node) {
+               error_code_ret = LTTNG_ERR_TRIGGER_NOT_FOUND;
+               goto error;
+       }
+
+       token_event_rule_element = caa_container_of(node,
+                       struct ltt_kernel_token_event_rule, ht_node);
+
+       ret = kernel_disable_token_event_rule(token_event_rule_element);
+       if (ret) {
+               error_code_ret = LTTNG_ERR_FATAL;
+               goto error;
+       }
+
+       trace_kernel_destroy_token_event_rule(token_event_rule_element);
+
+       rcu_read_unlock();
+       pthread_mutex_unlock(&notification_trigger_tokens_ht_lock);
+
+       error_code_ret = LTTNG_OK;
+
+error:
+       return error_code_ret;
+}
+
+int kernel_get_notification_fd(void)
+{
+       return kernel_tracer_trigger_group_notification_fd;
+}
index 53d480313ad456d63e8cecb869876ac78dc2d117..c9b17d260b2123031181f612e6b335e7b0d1cecb 100644 (file)
@@ -29,7 +29,7 @@ int kernel_create_session(struct ltt_session *session);
 int kernel_create_channel(struct ltt_kernel_session *session,
                struct lttng_channel *chan);
 int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel,
 int kernel_create_channel(struct ltt_kernel_session *session,
                struct lttng_channel *chan);
 int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel,
-               char *filter_expression, struct lttng_filter_bytecode *filter);
+               char *filter_expression, struct lttng_bytecode *filter);
 int kernel_disable_channel(struct ltt_kernel_channel *chan);
 int kernel_disable_event(struct ltt_kernel_event *event);
 int kernel_enable_event(struct ltt_kernel_event *event);
 int kernel_disable_channel(struct ltt_kernel_channel *chan);
 int kernel_disable_event(struct ltt_kernel_event *event);
 int kernel_enable_event(struct ltt_kernel_event *event);
@@ -81,4 +81,18 @@ 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_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_register_trigger(struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds);
+enum lttng_error_code kernel_unregister_trigger(struct lttng_trigger *trigger);
+enum lttng_error_code kernel_create_trigger_group_error_counter(
+               int *trigger_group_error_counter_fd, size_t nb_bucket);
+enum lttng_error_code kernel_trigger_update_error_count(
+               struct lttng_trigger *trigger);
+
+int kernel_get_notification_fd(void);
+
 #endif /* _LTT_KERNEL_CTL_H */
 #endif /* _LTT_KERNEL_CTL_H */
index edc1bc115fbb9f5268108a8383d34f5b02c726e0..9d0b9abd5dd40e2fe8b2b35cadaf4317e5e36952 100644 (file)
@@ -71,6 +71,7 @@ extern struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version;
 
 /* Notification thread handle. */
 extern struct notification_thread_handle *notification_thread_handle;
 
 /* 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
 
 /*
  * This contains extra data needed for processing a command received by the
index 4bdac5217978d28dd2af6c036d66f0650615a9bf..efb2117cb4bfa9d6ce5b6940c07bc6c303c0547e 100644 (file)
@@ -51,7 +51,6 @@
 #include "event.h"
 #include "kernel.h"
 #include "kernel-consumer.h"
 #include "event.h"
 #include "kernel.h"
 #include "kernel-consumer.h"
-#include "shm.h"
 #include "lttng-ust-ctl.h"
 #include "ust-consumer.h"
 #include "utils.h"
 #include "lttng-ust-ctl.h"
 #include "ust-consumer.h"
 #include "utils.h"
@@ -74,6 +73,7 @@
 #include "register.h"
 #include "manage-apps.h"
 #include "manage-kernel.h"
 #include "register.h"
 #include "manage-apps.h"
 #include "manage-kernel.h"
+#include "trigger-error-accounting.h"
 
 static const char *help_msg =
 #ifdef LTTNG_EMBED_HELP
 
 static const char *help_msg =
 #ifdef LTTNG_EMBED_HELP
@@ -83,6 +83,8 @@ NULL
 #endif
 ;
 
 #endif
 ;
 
+#define TRIGGER_ERROR_COUNTER_NUMBER_OF_BUCKET_MAX 65535
+
 const char *progname;
 static int lockfile_fd = -1;
 static int opt_print_version;
 const char *progname;
 static int lockfile_fd = -1;
 static int opt_print_version;
@@ -120,6 +122,7 @@ static const struct option long_options[] = {
        { "load", required_argument, 0, 'l' },
        { "kmod-probes", required_argument, 0, '\0' },
        { "extra-kmod-probes", required_argument, 0, '\0' },
        { "load", required_argument, 0, 'l' },
        { "kmod-probes", required_argument, 0, '\0' },
        { "extra-kmod-probes", required_argument, 0, '\0' },
+       { "trigger-error-number-of-bucket", required_argument, 0, '\0' },
        { NULL, 0, 0, 0 }
 };
 
        { NULL, 0, 0, 0 }
 };
 
@@ -314,6 +317,9 @@ static void sessiond_cleanup(void)
 
        pthread_mutex_destroy(&session_list->lock);
 
 
        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");
        DBG("Cleaning up all agent apps");
        agent_app_ht_clean();
        DBG("Closing all UST sockets");
@@ -694,6 +700,23 @@ static int set_option(int opt, const char *arg, const char *optname)
                                ret = -ENOMEM;
                        }
                }
                                ret = -ENOMEM;
                        }
                }
+       } else if (string_match(optname, "trigger-error-number-of-bucket")) {
+               unsigned long v;
+
+               errno = 0;
+               v = strtoul(arg, NULL, 0);
+               if (errno != 0 || !isdigit(arg[0])) {
+                       ERR("Wrong value in --trigger-error-number-of-bucket parameter: %s", arg);
+                       return -1;
+               }
+               if (v == 0 || v >= TRIGGER_ERROR_COUNTER_NUMBER_OF_BUCKET_MAX) {
+                       ERR("Value out of range for --trigger-error-number-of-bucket parameter: %s", arg);
+                       return -1;
+               }
+               config.trigger_error_counter_bucket = (int) v;
+               DBG3("Number of error counter set to non default: %i",
+                               config.trigger_error_counter_bucket);
+               goto end;
        } else if (string_match(optname, "config") || opt == 'f') {
                /* This is handled in set_options() thus silent skip. */
                goto end;
        } else if (string_match(optname, "config") || opt == 'f') {
                /* This is handled in set_options() thus silent skip. */
                goto end;
@@ -1529,6 +1552,8 @@ int main(int argc, char **argv)
                goto stop_threads;
        }
 
                goto stop_threads;
        }
 
+       trigger_error_accounting_init(config.trigger_error_counter_bucket);
+
        /*
         * Initialize agent app hash table. We allocate the hash table here
         * since cleanup() can get called after this point.
        /*
         * Initialize agent app hash table. We allocate the hash table here
         * since cleanup() can get called after this point.
@@ -1539,6 +1564,11 @@ int main(int argc, char **argv)
                goto stop_threads;
        }
 
                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
        /*
         * 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
@@ -1636,7 +1666,8 @@ int main(int argc, char **argv)
        notification_thread_handle = notification_thread_handle_create(
                        ust32_channel_monitor_pipe,
                        ust64_channel_monitor_pipe,
        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");
        if (!notification_thread_handle) {
                retval = -1;
                ERR("Failed to create notification thread shared data");
@@ -1726,6 +1757,16 @@ int main(int argc, char **argv)
                        retval = -1;
                        goto stop_threads;
                }
                        retval = -1;
                        goto stop_threads;
                }
+
+               if (kernel_get_notification_fd() > -1) {
+                       ret = notification_thread_command_add_application(
+                                       notification_thread_handle, kernel_get_notification_fd(), LTTNG_DOMAIN_KERNEL);
+                       if (ret != LTTNG_OK) {
+                               ERR("Failed to add kernel trigger event source to notification thread");
+                               retval = -1;
+                               goto stop_threads;
+                       }
+               }
        }
 
        /* Load sessions. */
        }
 
        /* Load sessions. */
@@ -1749,6 +1790,8 @@ int main(int argc, char **argv)
        sessiond_wait_for_quit_pipe(-1);
 
 stop_threads:
        sessiond_wait_for_quit_pipe(-1);
 
 stop_threads:
+
+       trigger_error_accounting_fini();
        /*
         * Ensure that the client thread is no longer accepting new commands,
         * which could cause new sessions to be created.
        /*
         * Ensure that the client thread is no longer accepting new commands,
         * which could cause new sessions to be created.
index cf9c12b3c81b5bfad2bf1d55ecab21fd3d8ddc81..a093f2412c4006d5c1660d2b6e12bdf2f3a74968 100644 (file)
 #define LTTNG_MOD_OPTIONAL     0
 
 /* LTTng kernel tracer mandatory core modules list */
 #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[] = {
        { (char *) "lttng-ring-buffer-client-discard" },
        { (char *) "lttng-ring-buffer-client-overwrite" },
 struct kern_modules_param kern_modules_control_core[] = {
        { (char *) "lttng-ring-buffer-client-discard" },
        { (char *) "lttng-ring-buffer-client-overwrite" },
@@ -37,6 +41,9 @@ struct kern_modules_param kern_modules_control_core[] = {
        { (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-client-mmap-discard" },
        { (char *) "lttng-ring-buffer-client-mmap-overwrite" },
        { (char *) "lttng-ring-buffer-metadata-mmap-client" },
+       { (char *) "lttng-ring-buffer-trigger-client" },
+       { (char *) "lttng-counter-client-percpu-64-modular" },
+       { (char *) "lttng-counter-client-percpu-32-modular" },
 };
 
 /* LTTng kernel tracer probe modules list */
 };
 
 /* LTTng kernel tracer probe modules list */
index dc03e6a2df2fd9e7eab8634b5ece6ebb557c91de..3069c57d78ed4607342bb237d4a358dd244da572 100644 (file)
@@ -268,6 +268,112 @@ end:
        return ret_code;
 }
 
        return ret_code;
 }
 
+enum lttng_error_code notification_thread_command_add_application(
+               struct notification_thread_handle *handle,
+               int fd,
+               enum lttng_domain_type domain)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct notification_thread_command cmd = {};
+
+       assert(!(fd < 0));
+
+       init_notification_thread_command(&cmd);
+
+       cmd.type = NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION;
+       cmd.parameters.application.read_side_trigger_event_application_pipe = fd;
+       cmd.parameters.application.domain = domain;
+
+       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,
+               int fd)
+{
+       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 = fd;
+
+       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,
+               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;
+
+       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)
 {
 void notification_thread_command_quit(
                struct notification_thread_handle *handle)
 {
@@ -295,3 +401,52 @@ int notification_thread_client_communication_update(
        cmd.parameters.client_communication_update.status = transmission_status;
        return run_command_no_wait(handle, &cmd);
 }
        cmd.parameters.client_communication_update.status = transmission_status;
        return run_command_no_wait(handle, &cmd);
 }
+
+/*
+ * Takes ownership of the payload if present.
+ */
+LTTNG_HIDDEN
+struct lttng_trigger_notification *lttng_trigger_notification_create(
+               uint64_t id,
+               enum lttng_domain_type domain,
+               char *payload,
+               size_t payload_size)
+{
+       struct lttng_trigger_notification *notification = NULL;
+
+       assert(domain != LTTNG_DOMAIN_NONE);
+
+       if (payload) {
+               assert(payload_size > 0);
+       } else {
+               assert(payload_size == 0);
+       }
+
+       notification = zmalloc(sizeof(struct lttng_trigger_notification));
+       if (notification == NULL) {
+               ERR("[notification-thread] Error allocating notification ");
+               goto end;
+       }
+
+       notification->id = id;
+       notification->type = domain;
+       notification->capture_buffer = payload;
+       notification->capture_buf_size = payload_size;
+
+end:
+       return notification;
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_notification_destroy(
+               struct lttng_trigger_notification *notification)
+{
+       if (!notification) {
+               return;
+       }
+
+       if(notification->capture_buffer) {
+               free(notification->capture_buffer);
+       }
+       free(notification);
+}
index c09ebea46f0c72a0e0acc1e42da590dd3b096423..6556bf7c5cce08d30634e1c5b68250d9b5d18982 100644 (file)
@@ -27,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_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,
        NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE,
 };
        NOTIFICATION_COMMAND_TYPE_QUIT,
        NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE,
 };
@@ -64,6 +68,16 @@ struct notification_thread_command {
                        uint64_t trace_archive_chunk_id;
                        struct lttng_trace_archive_location *location;
                } session_rotation;
                        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;
+                       enum lttng_domain_type domain;
+               } application;
+               /* List triggers */
+               struct {
+                       /* Credential */
+                       uid_t uid;
+               } list_triggers;
                /* Client communication update. */
                struct {
                        notification_client_id id;
                /* Client communication update. */
                struct {
                        notification_client_id id;
@@ -72,6 +86,15 @@ struct notification_thread_command {
 
        } parameters;
 
 
        } 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;
        /* lttng_waiter on which to wait for command reply (optional). */
        struct lttng_waiter reply_waiter;
        enum lttng_error_code reply_code;
@@ -108,6 +131,28 @@ enum lttng_error_code notification_thread_command_session_rotation_completed(
                uint64_t trace_archive_chunk_id,
                struct lttng_trace_archive_location *location);
 
                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,
+               int fd,
+               enum lttng_domain_type domain
+               );
+
+enum lttng_error_code notification_thread_command_remove_application(
+               struct notification_thread_handle *handle,
+               int 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);
+
+enum lttng_error_code notification_thread_command_list_triggers(
+               struct notification_thread_handle *handle,
+               uid_t uid,
+               struct lttng_triggers **triggers);
+
 void notification_thread_command_quit(
                struct notification_thread_handle *handle);
 
 void notification_thread_command_quit(
                struct notification_thread_handle *handle);
 
index 95101178f2f08c7d40bd0ae242ddf79193e45095..b3c64d9c77d94ab57079df681e330fbc90413fcb 100644 (file)
 #include <common/macros.h>
 #include <lttng/condition/condition.h>
 #include <lttng/action/action-internal.h>
 #include <common/macros.h>
 #include <lttng/condition/condition.h>
 #include <lttng/action/action-internal.h>
+#include <lttng/action/group-internal.h>
+#include <lttng/domain-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/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/domain-internal.h>
 #include <lttng/notification/channel-internal.h>
 #include <lttng/notification/channel-internal.h>
+#include <lttng/trigger/trigger-internal.h>
 
 #include <time.h>
 #include <unistd.h>
 
 #include <time.h>
 #include <unistd.h>
 #include <inttypes.h>
 #include <fcntl.h>
 
 #include <inttypes.h>
 #include <fcntl.h>
 
+#include "condition-internal.h"
 #include "notification-thread.h"
 #include "notification-thread-events.h"
 #include "notification-thread-commands.h"
 #include "lttng-sessiond.h"
 #include "kernel.h"
 #include "notification-thread.h"
 #include "notification-thread-events.h"
 #include "notification-thread-commands.h"
 #include "lttng-sessiond.h"
 #include "kernel.h"
+#include "trigger-error-accounting.h"
 
 #define CLIENT_POLL_MASK_IN (LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP)
 #define CLIENT_POLL_MASK_IN_OUT (CLIENT_POLL_MASK_IN | LPOLLOUT)
 
 #define CLIENT_POLL_MASK_IN (LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP)
 #define CLIENT_POLL_MASK_IN_OUT (CLIENT_POLL_MASK_IN | LPOLLOUT)
@@ -110,6 +117,7 @@ struct lttng_session_trigger_list {
 struct lttng_trigger_ht_element {
        struct lttng_trigger *trigger;
        struct cds_lfht_node node;
 struct lttng_trigger_ht_element {
        struct lttng_trigger *trigger;
        struct cds_lfht_node node;
+       struct cds_lfht_node node_by_name_uid;
        /* call_rcu delayed reclaim. */
        struct rcu_head rcu_node;
 };
        /* call_rcu delayed reclaim. */
        struct rcu_head rcu_node;
 };
@@ -187,6 +195,9 @@ int client_handle_transmission_status(
                enum client_transmission_status transmission_status,
                struct notification_thread_state *state);
 
                enum client_transmission_status transmission_status,
                struct notification_thread_state *state);
 
+static
+void free_lttng_trigger_ht_element_rcu(struct rcu_head *node);
+
 static
 int match_client_socket(struct cds_lfht_node *node, const void *key)
 {
 static
 int match_client_socket(struct cds_lfht_node *node, const void *key)
 {
@@ -261,18 +272,26 @@ int match_channel_info(struct cds_lfht_node *node, const void *key)
 }
 
 static
 }
 
 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;
+       struct lttng_trigger *trigger_key = (struct lttng_trigger *) key;
+       struct lttng_trigger_ht_element *trigger_ht_element;
 
 
-       trigger = caa_container_of(node, struct lttng_trigger_ht_element,
+       trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element,
                        node);
                        node);
-       condition = lttng_trigger_get_condition(trigger->trigger);
-       assert(condition);
 
 
-       return !!lttng_condition_is_equal(condition_key, condition);
+       return !!lttng_trigger_is_equal(trigger_key, trigger_ht_element->trigger);
+}
+
+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
 }
 
 static
@@ -301,103 +320,42 @@ int match_session(struct cds_lfht_node *node, const void *key)
        return !strcmp(session_info->name, name);
 }
 
        return !strcmp(session_info->name, name);
 }
 
-static
-unsigned long lttng_condition_buffer_usage_hash(
-       const struct lttng_condition *_condition)
+/*
+ * Match trigger based on name and credentials only.
+ * Name duplication is NOT allowed for the same uid.
+ */
+static int match_name_uid(struct cds_lfht_node *node, const void *key)
 {
 {
-       unsigned long hash;
-       unsigned long condition_type;
-       struct lttng_condition_buffer_usage *condition;
+       bool match = false;
+       struct lttng_trigger_ht_element *trigger_ht_element;
+       const char *name;
+       const char *key_name;
+       enum lttng_trigger_status status;
+       const struct lttng_credentials *key_creds;
+       const struct lttng_credentials *node_creds;
+       struct lttng_trigger *trigger_key = (struct lttng_trigger *) key;
 
 
-       condition = container_of(_condition,
-                       struct lttng_condition_buffer_usage, parent);
+       trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element,
+                       node_by_name_uid);
 
 
-       condition_type = (unsigned long) condition->parent.type;
-       hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
-       if (condition->session_name) {
-               hash ^= hash_key_str(condition->session_name, lttng_ht_seed);
-       }
-       if (condition->channel_name) {
-               hash ^= hash_key_str(condition->channel_name, lttng_ht_seed);
-       }
-       if (condition->domain.set) {
-               hash ^= hash_key_ulong(
-                               (void *) condition->domain.type,
-                               lttng_ht_seed);
-       }
-       if (condition->threshold_ratio.set) {
-               uint64_t val;
+       status = lttng_trigger_get_name(trigger_ht_element->trigger, &name);
+       assert(status == LTTNG_TRIGGER_STATUS_OK);
 
 
-               val = condition->threshold_ratio.value * (double) UINT32_MAX;
-               hash ^= hash_key_u64(&val, lttng_ht_seed);
-       } else if (condition->threshold_bytes.set) {
-               uint64_t val;
+       status = lttng_trigger_get_name(trigger_key, &key_name);
+       assert(status == LTTNG_TRIGGER_STATUS_OK);
 
 
-               val = condition->threshold_bytes.value;
-               hash ^= hash_key_u64(&val, lttng_ht_seed);
+       if (strcmp(name, key_name) != 0) {
+               goto end;
        }
        }
-       return hash;
-}
 
 
-static
-unsigned long lttng_condition_session_consumed_size_hash(
-       const struct lttng_condition *_condition)
-{
-       unsigned long hash;
-       unsigned long condition_type =
-                       (unsigned long) LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE;
-       struct lttng_condition_session_consumed_size *condition;
-       uint64_t val;
+       /* Check the uid */
+       key_creds = lttng_trigger_get_credentials(trigger_key);
+       node_creds = lttng_trigger_get_credentials(trigger_ht_element->trigger);
 
 
-       condition = container_of(_condition,
-                       struct lttng_condition_session_consumed_size, parent);
+       match = lttng_credentials_is_equal_uid(key_creds, node_creds);
 
 
-       hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
-       if (condition->session_name) {
-               hash ^= hash_key_str(condition->session_name, lttng_ht_seed);
-       }
-       val = condition->consumed_threshold_bytes.value;
-       hash ^= hash_key_u64(&val, lttng_ht_seed);
-       return hash;
-}
-
-static
-unsigned long lttng_condition_session_rotation_hash(
-       const struct lttng_condition *_condition)
-{
-       unsigned long hash, condition_type;
-       struct lttng_condition_session_rotation *condition;
-
-       condition = container_of(_condition,
-                       struct lttng_condition_session_rotation, parent);
-       condition_type = (unsigned long) condition->parent.type;
-       hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
-       assert(condition->session_name);
-       hash ^= hash_key_str(condition->session_name, lttng_ht_seed);
-       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
- * don't want to link in liblttng-ctl.
- */
-static
-unsigned long lttng_condition_hash(const struct lttng_condition *condition)
-{
-       switch (condition->type) {
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-               return lttng_condition_buffer_usage_hash(condition);
-       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
-               return lttng_condition_session_consumed_size_hash(condition);
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               return lttng_condition_session_rotation_hash(condition);
-       default:
-               ERR("[notification-thread] Unexpected condition type caught");
-               abort();
-       }
+end:
+       return match;
 }
 
 static
 }
 
 static
@@ -442,6 +400,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_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;
        }
        default:
                return LTTNG_OBJECT_TYPE_UNKNOWN;
        }
@@ -952,6 +912,7 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger,
                                &evaluation, &object_uid, &object_gid);
                break;
        case LTTNG_OBJECT_TYPE_NONE:
                                &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:
                ret = 0;
                goto end;
        case LTTNG_OBJECT_TYPE_UNKNOWN:
@@ -1623,7 +1584,7 @@ int handle_notification_thread_command_add_channel(
 
        DBG("[notification-thread] Adding channel %s from session %s, channel key = %" PRIu64 " in %s domain",
                        channel_name, session_name, channel_key_int,
 
        DBG("[notification-thread] Adding channel %s from session %s, channel key = %" PRIu64 " in %s domain",
                        channel_name, session_name, channel_key_int,
-                       channel_domain == LTTNG_DOMAIN_KERNEL ? "kernel" : "user space");
+                       lttng_domain_type_str(channel_domain));
 
        CDS_INIT_LIST_HEAD(&trigger_list);
 
 
        CDS_INIT_LIST_HEAD(&trigger_list);
 
@@ -1724,7 +1685,7 @@ int handle_notification_thread_command_remove_channel(
        struct channel_info *channel_info;
 
        DBG("[notification-thread] Removing channel key = %" PRIu64 " in %s domain",
        struct channel_info *channel_info;
 
        DBG("[notification-thread] Removing channel key = %" PRIu64 " in %s domain",
-                       channel_key, domain == LTTNG_DOMAIN_KERNEL ? "kernel" : "user space");
+                       channel_key, lttng_domain_type_str(domain));
 
        rcu_read_lock();
 
 
        rcu_read_lock();
 
@@ -1807,8 +1768,8 @@ int handle_notification_thread_command_session_rotation(
        struct lttng_trigger_list_element *trigger_list_element;
        struct session_info *session_info;
        const struct lttng_credentials session_creds = {
        struct lttng_trigger_list_element *trigger_list_element;
        struct session_info *session_info;
        const struct lttng_credentials session_creds = {
-               .uid = session_uid,
-               .gid = session_gid,
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(session_uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(session_gid),
        };
 
        rcu_read_lock();
        };
 
        rcu_read_lock();
@@ -1917,6 +1878,218 @@ end:
        return ret;
 }
 
        return ret;
 }
 
+static
+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_domain_type domain_type,
+       enum lttng_error_code *_cmd_result)
+{
+       int ret = 0;
+       enum lttng_error_code cmd_result = LTTNG_OK;
+       struct notification_event_trigger_source_element *element = NULL;
+
+       element = zmalloc(sizeof(*element));
+       if (!element) {
+               cmd_result = LTTNG_ERR_NOMEM;
+               ret = -1;
+               goto end;
+       }
+
+       CDS_INIT_LIST_HEAD(&element->node);
+       element->fd = read_side_trigger_event_application_pipe;
+       element->domain = domain_type;
+
+       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);
+
+       /* 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;
+}
+
+static
+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;
+       enum lttng_error_code cmd_result = LTTNG_OK;
+       /* Used for logging */
+       enum lttng_domain_type domain = LTTNG_DOMAIN_NONE;
+
+       /* TODO: missing a lock propably to revisit */
+       struct notification_event_trigger_source_element *source_element = NULL, *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;
+               }
+
+               cds_list_del(&source_element->node);
+               break;
+       }
+
+       /* It should always be found */
+       assert(source_element);
+
+       DBG3("[notification-thread] Removing application event source from fd: %d of domain: %s",
+                       read_side_trigger_event_application_pipe,
+                       lttng_domain_type_str(domain));
+       free(source_element);
+
+       /* 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;
+               }
+
+               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 trigger_update_error_count(struct lttng_trigger *trigger)
+{
+       int ret = 0;
+       uint64_t error_count = 0;
+       enum trigger_error_accounting_status status;
+
+       status = trigger_error_accounting_get_count(trigger, &error_count);
+       if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+               ERR("Error getting trigger error count");
+       }
+
+       lttng_trigger_set_error_count(trigger, error_count);
+       return ret;
+}
+
+static int handle_notification_thread_command_list_triggers(
+               struct notification_thread_handle *handle,
+               struct notification_thread_state *state,
+               uid_t uid,
+               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);
+
+       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.
+                * Root user have visibility over all triggers.
+                */
+               creds = lttng_trigger_get_credentials(trigger_ht_element->trigger);
+               if (uid != lttng_credentials_get_uid(creds) && uid != 0) {
+                       continue;
+               }
+
+               ret = trigger_update_error_count(trigger_ht_element->trigger);
+               assert(!ret);
+
+               ret = lttng_triggers_add(local_triggers, trigger_ht_element->trigger);
+               if (ret < 0) {
+                       ret = -1;
+                       goto end;
+               }
+
+               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)
 {
 static
 int condition_is_supported(struct lttng_condition *condition)
 {
@@ -1948,6 +2121,15 @@ int condition_is_supported(struct lttng_condition *condition)
                ret = kernel_supports_ring_buffer_snapshot_sample_positions();
                break;
        }
                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;
        }
        default:
                ret = 1;
        }
@@ -1955,6 +2137,42 @@ end:
        return ret;
 }
 
        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,
 /* Must be called with RCU read lock held. */
 static
 int bind_trigger_to_matching_session(struct lttng_trigger *trigger,
@@ -2058,7 +2276,7 @@ bool is_trigger_action_notify(const struct lttng_trigger *trigger)
        enum lttng_action_type action_type;
 
        assert(action);
        enum lttng_action_type action_type;
 
        assert(action);
-       action_type = lttng_action_get_type_const(action);
+       action_type = lttng_action_get_type(action);
        if (action_type == LTTNG_ACTION_TYPE_NOTIFY) {
                is_notify = true;
                goto end;
        if (action_type == LTTNG_ACTION_TYPE_NOTIFY) {
                is_notify = true;
                goto end;
@@ -2074,7 +2292,7 @@ bool is_trigger_action_notify(const struct lttng_trigger *trigger)
                                lttng_action_group_get_at_index(
                                                action, i);
 
                                lttng_action_group_get_at_index(
                                                action, i);
 
-               action_type = lttng_action_get_type_const(inner_action);
+               action_type = lttng_action_get_type(inner_action);
                if (action_type == LTTNG_ACTION_TYPE_NOTIFY) {
                        is_notify = true;
                        goto end;
                if (action_type == LTTNG_ACTION_TYPE_NOTIFY) {
                        is_notify = true;
                        goto end;
@@ -2085,6 +2303,52 @@ end:
        return is_notify;
 }
 
        return is_notify;
 }
 
+static bool trigger_name_taken(struct notification_thread_state *state,
+               const struct lttng_trigger *trigger)
+{
+       struct cds_lfht_node *triggers_by_name_uid_ht_node;
+       struct cds_lfht_iter iter;
+
+       /*
+        * No duplicata is allowed in the triggers_by_name_uid_ht.
+        * The match is done against the trigger name and uid.
+        */
+       cds_lfht_lookup(state->triggers_by_name_uid_ht,
+                       hash_key_str(trigger->name, lttng_ht_seed),
+                       match_name_uid,
+                       trigger,
+                       &iter);
+       triggers_by_name_uid_ht_node = cds_lfht_iter_get_node(&iter);
+       if (triggers_by_name_uid_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;
+       enum lttng_trigger_status status;
+       do {
+               lttng_trigger_generate_name(trigger, state->trigger_id.name_offset);
+
+               status = lttng_trigger_get_name(trigger, name);
+               assert(status == LTTNG_TRIGGER_STATUS_OK);
+
+               taken = trigger_name_taken(state, trigger);
+               if (taken) {
+                       state->trigger_id.name_offset++;
+               }
+       } while (taken || state->trigger_id.name_offset == UINT32_MAX);
+}
+
 /*
  * FIXME A client's credentials are not checked when registering a trigger.
  *
 /*
  * FIXME A client's credentials are not checked when registering a trigger.
  *
@@ -2107,32 +2371,60 @@ int handle_notification_thread_command_register_trigger(
                enum lttng_error_code *cmd_result)
 {
        int ret = 0;
                enum lttng_error_code *cmd_result)
 {
        int ret = 0;
+       int is_supported;
        struct lttng_condition *condition;
        struct lttng_condition *condition;
+       struct lttng_action *action;
        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;
        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;
+       struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL;
        struct cds_lfht_node *node;
        struct cds_lfht_iter iter;
        struct cds_lfht_node *node;
        struct cds_lfht_iter iter;
+       const char* trigger_name;
        bool free_trigger = true;
        struct lttng_evaluation *evaluation = NULL;
        struct lttng_credentials object_creds;
        bool free_trigger = true;
        struct lttng_evaluation *evaluation = NULL;
        struct lttng_credentials object_creds;
+       uid_t object_uid;
+       gid_t object_gid;
        enum action_executor_status executor_status;
 
        rcu_read_lock();
 
        enum action_executor_status executor_status;
 
        rcu_read_lock();
 
+       /* Set the trigger's tracer token */
+       lttng_trigger_set_tracer_token(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)) {
+               /* Not a fatal error */
+               *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+               ret = 0;
+               goto error;
+       }
+
        condition = lttng_trigger_get_condition(trigger);
        assert(condition);
 
        condition = lttng_trigger_get_condition(trigger);
        assert(condition);
 
-       ret = condition_is_supported(condition);
-       if (ret < 0) {
+       action = lttng_trigger_get_action(trigger);
+       assert(action);
+
+       is_supported = condition_is_supported(condition);
+       if (is_supported < 0) {
                goto error;
                goto error;
-       } else if (ret == 0) {
+       } else if (is_supported == 0) {
                *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
                goto error;
                *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
                goto error;
-       } else {
-               /* Feature is supported, continue. */
+       }
+
+       is_supported = action_is_supported(action);
+       if (is_supported < 0) {
+               goto error;
+       } else if (is_supported == 0) {
                ret = 0;
                ret = 0;
+               *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
+               goto error;
        }
 
        trigger_ht_element = zmalloc(sizeof(*trigger_ht_element));
        }
 
        trigger_ht_element = zmalloc(sizeof(*trigger_ht_element));
@@ -2143,12 +2435,13 @@ int handle_notification_thread_command_register_trigger(
 
        /* Add trigger to the trigger_ht. */
        cds_lfht_node_init(&trigger_ht_element->node);
 
        /* Add trigger to the trigger_ht. */
        cds_lfht_node_init(&trigger_ht_element->node);
+       cds_lfht_node_init(&trigger_ht_element->node_by_name_uid);
        trigger_ht_element->trigger = trigger;
 
        node = cds_lfht_add_unique(state->triggers_ht,
                        lttng_condition_hash(condition),
        trigger_ht_element->trigger = trigger;
 
        node = cds_lfht_add_unique(state->triggers_ht,
                        lttng_condition_hash(condition),
-                       match_condition,
-                       condition,
+                       match_trigger,
+                       trigger,
                        &trigger_ht_element->node);
        if (node != &trigger_ht_element->node) {
                /* Not a fatal error, simply report it to the client. */
                        &trigger_ht_element->node);
        if (node != &trigger_ht_element->node) {
                /* Not a fatal error, simply report it to the client. */
@@ -2156,10 +2449,66 @@ int handle_notification_thread_command_register_trigger(
                goto error_free_ht_element;
        }
 
                goto error_free_ht_element;
        }
 
+       node = cds_lfht_add_unique(state->triggers_by_name_uid_ht,
+                       hash_key_str(trigger_name, lttng_ht_seed), match_name_uid,
+                       trigger, &trigger_ht_element->node_by_name_uid);
+       if (node != &trigger_ht_element->node_by_name_uid) {
+               /* Not a fatal error, simply report it to the client. */
+               cds_lfht_del(state->triggers_ht, &trigger_ht_element->node);
+               *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+               goto error_free_ht_element;
+       }
+
+       if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+               uint64_t error_counter_index = 0;
+               enum trigger_error_accounting_status error_accounting_status;
+
+               trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element));
+               if (!trigger_tokens_ht_element) {
+                       ret = -1;
+                       cds_lfht_del(state->triggers_ht, &trigger_ht_element->node);
+                       cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid);
+                       goto error;
+               }
+
+               error_accounting_status = trigger_error_accounting_register_trigger(
+                               trigger, &error_counter_index);
+               if (error_accounting_status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+                       ERR("Error registering trigger for error accounting");
+                       cds_lfht_del(state->triggers_ht, &trigger_ht_element->node);
+                       cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid);
+                       *cmd_result = LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER_FULL;
+                       goto error_free_ht_element;
+               }
+
+               lttng_trigger_set_error_counter_index(trigger, error_counter_index);
+
+               /* Add trigger token to the trigger_tokens_ht. */
+               cds_lfht_node_init(&trigger_tokens_ht_element->node);
+               trigger_tokens_ht_element->token = LTTNG_OPTIONAL_GET(trigger->tracer_token);
+               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;
+                       cds_lfht_del(state->triggers_ht, &trigger_ht_element->node);
+                       cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid);
+                       trigger_error_accounting_unregister_trigger(trigger);
+                       goto error_free_ht_element;
+               }
+       }
+
        /*
         * Ownership of the trigger and of its wrapper was transfered to
        /*
         * Ownership of the trigger and of its wrapper was transfered to
-        * the triggers_ht.
+        * the triggers_ht. Same for token ht element if necessary.
         */
         */
+       trigger_tokens_ht_element = NULL;
        trigger_ht_element = NULL;
        free_trigger = false;
 
        trigger_ht_element = NULL;
        free_trigger = false;
 
@@ -2260,21 +2609,21 @@ int handle_notification_thread_command_register_trigger(
        switch (get_condition_binding_object(condition)) {
        case LTTNG_OBJECT_TYPE_SESSION:
                ret = evaluate_session_condition_for_client(condition, state,
        switch (get_condition_binding_object(condition)) {
        case LTTNG_OBJECT_TYPE_SESSION:
                ret = evaluate_session_condition_for_client(condition, state,
-                               &evaluation, &object_creds.uid,
-                               &object_creds.gid);
+                               &evaluation, &object_uid,
+                               &object_gid);
                break;
        case LTTNG_OBJECT_TYPE_CHANNEL:
                ret = evaluate_channel_condition_for_client(condition, state,
                break;
        case LTTNG_OBJECT_TYPE_CHANNEL:
                ret = evaluate_channel_condition_for_client(condition, state,
-                               &evaluation, &object_creds.uid,
-                               &object_creds.gid);
+                               &evaluation, &object_uid,
+                               &object_gid);
                break;
        case LTTNG_OBJECT_TYPE_NONE:
                ret = 0;
                break;
        case LTTNG_OBJECT_TYPE_NONE:
                ret = 0;
-               goto error_put_client_list;
+               break;
        case LTTNG_OBJECT_TYPE_UNKNOWN:
        default:
                ret = -1;
        case LTTNG_OBJECT_TYPE_UNKNOWN:
        default:
                ret = -1;
-               goto error_put_client_list;
+               break;
        }
 
        if (ret) {
        }
 
        if (ret) {
@@ -2282,12 +2631,15 @@ int handle_notification_thread_command_register_trigger(
                goto error_put_client_list;
        }
 
                goto error_put_client_list;
        }
 
+       LTTNG_OPTIONAL_SET(&object_creds.uid, object_uid);
+       LTTNG_OPTIONAL_SET(&object_creds.gid, object_gid);
+
        DBG("Newly registered trigger's condition evaluated to %s",
                        evaluation ? "true" : "false");
        if (!evaluation) {
                /* Evaluation yielded nothing. Normal exit. */
                ret = 0;
        DBG("Newly registered trigger's condition evaluated to %s",
                        evaluation ? "true" : "false");
        if (!evaluation) {
                /* Evaluation yielded nothing. Normal exit. */
                ret = 0;
-               goto error_put_client_list;
+               goto end;
        }
 
        /*
        }
 
        /*
@@ -2318,18 +2670,26 @@ int handle_notification_thread_command_register_trigger(
                 */
                WARN("No space left when enqueuing action associated to newly registered trigger");
                ret = 0;
                 */
                WARN("No space left when enqueuing action associated to newly registered trigger");
                ret = 0;
-               goto error_put_client_list;
+               goto end;
        default:
                abort();
        }
 
        default:
                abort();
        }
 
+end:
+       /* Increment the trigger unique id generator */
+       state->trigger_id.token_generator++;
        *cmd_result = LTTNG_OK;
 
 error_put_client_list:
        notification_client_list_put(client_list);
 
 error_free_ht_element:
        *cmd_result = LTTNG_OK;
 
 error_put_client_list:
        notification_client_list_put(client_list);
 
 error_free_ht_element:
-       free(trigger_ht_element);
+       if (trigger_ht_element) {
+               /* Delayed removal due to RCU constraint on delete. */
+               call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu);
+       }
+
+       free(trigger_tokens_ht_element);
 error:
        if (free_trigger) {
                lttng_trigger_destroy(trigger);
 error:
        if (free_trigger) {
                lttng_trigger_destroy(trigger);
@@ -2345,6 +2705,13 @@ void free_lttng_trigger_ht_element_rcu(struct rcu_head *node)
                        rcu_node));
 }
 
                        rcu_node));
 }
 
+static
+void free_notification_trigger_tokens_ht_element_rcu(struct rcu_head *node)
+{
+       free(caa_container_of(node, struct notification_trigger_tokens_ht_element,
+                       rcu_node));
+}
+
 static
 int handle_notification_thread_command_unregister_trigger(
                struct notification_thread_state *state,
 static
 int handle_notification_thread_command_unregister_trigger(
                struct notification_thread_state *state,
@@ -2364,8 +2731,8 @@ int handle_notification_thread_command_unregister_trigger(
 
        cds_lfht_lookup(state->triggers_ht,
                        lttng_condition_hash(condition),
 
        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) {
                        &iter);
        triggers_ht_node = cds_lfht_iter_get_node(&iter);
        if (!triggers_ht_node) {
@@ -2382,13 +2749,7 @@ int handle_notification_thread_command_unregister_trigger(
 
                cds_list_for_each_entry_safe(trigger_element, tmp,
                                &trigger_list->list, node) {
 
                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;
                        }
 
                                continue;
                        }
 
@@ -2399,21 +2760,44 @@ 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;
+                       }
 
 
-       /* Put new reference and the hashtable's reference. */
-       notification_client_list_put(client_list);
-       notification_client_list_put(client_list);
-       client_list = NULL;
+                       trigger_error_accounting_unregister_trigger(trigger_tokens_ht_element->trigger);
+
+                       /* 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 (is_trigger_action_notify(trigger)) {
+               /*
+                * 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;
+       }
 
        /* Remove trigger from triggers_ht. */
        trigger_ht_element = caa_container_of(triggers_ht_node,
                        struct lttng_trigger_ht_element, node);
 
        /* 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_uid_ht, &trigger_ht_element->node_by_name_uid);
        cds_lfht_del(state->triggers_ht, triggers_ht_node);
 
        /* Release the ownership of the trigger. */
        cds_lfht_del(state->triggers_ht, triggers_ht_node);
 
        /* Release the ownership of the trigger. */
@@ -2496,6 +2880,43 @@ int handle_notification_thread_command(
                                cmd->parameters.session_rotation.location,
                                &cmd->reply_code);
                break;
                                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->parameters.application.domain,
+                               &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;
+       }
+       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,
+                               &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;
        case NOTIFICATION_COMMAND_TYPE_QUIT:
                DBG("[notification-thread] Received quit command");
                cmd->reply_code = LTTNG_OK;
@@ -3582,11 +4003,15 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger,
                struct notification_thread_state *state,
                uid_t object_uid, gid_t object_gid)
 {
                struct notification_thread_state *state,
                uid_t object_uid, gid_t object_gid)
 {
+       const struct lttng_credentials creds = {
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(object_uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(object_gid),
+       };
+
        return notification_client_list_send_evaluation(client_list,
                        lttng_trigger_get_const_condition(trigger), evaluation,
                        lttng_trigger_get_credentials(trigger),
        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},
+                       &creds,
                        client_handle_transmission_status_wrapper, state);
 }
 
                        client_handle_transmission_status_wrapper, state);
 }
 
@@ -3693,8 +4118,8 @@ int notification_client_list_send_evaluation(
                }
 
                if (source_object_creds) {
                }
 
                if (source_object_creds) {
-                       if (client->uid != source_object_creds->uid &&
-                                       client->gid != source_object_creds->gid &&
+                       if (client->uid != lttng_credentials_get_uid(source_object_creds) &&
+                                       client->gid != lttng_credentials_get_gid(source_object_creds) &&
                                        client->uid != 0) {
                                /*
                                 * Client is not allowed to monitor this
                                        client->uid != 0) {
                                /*
                                 * Client is not allowed to monitor this
@@ -3705,7 +4130,7 @@ int notification_client_list_send_evaluation(
                        }
                }
 
                        }
                }
 
-               if (client->uid != trigger_creds->uid && client->gid != trigger_creds->gid) {
+               if (client->uid != lttng_credentials_get_uid(trigger_creds) && client->gid != lttng_credentials_get_gid(trigger_creds)) {
                        DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this trigger");
                        goto skip_client;
                }
                        DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this trigger");
                        goto skip_client;
                }
@@ -3760,6 +4185,273 @@ end:
        return ret;
 }
 
        return ret;
 }
 
+static struct lttng_trigger_notification *receive_notification(int pipe,
+               enum lttng_domain_type domain)
+{
+       int ret;
+       uint64_t id;
+       struct lttng_trigger_notification *notification = NULL;
+       char *capture_buffer = NULL;
+       size_t capture_buffer_size;
+       void *reception_buffer;
+       size_t reception_size;
+
+       struct lttng_ust_trigger_notification ust_notification;
+       struct lttng_kernel_trigger_notification kernel_notification;
+
+       /* Init lttng_trigger_notification */
+
+       switch(domain) {
+       case LTTNG_DOMAIN_UST:
+               reception_buffer = (void *) &ust_notification;
+               reception_size = sizeof(ust_notification);
+               break;
+       case LTTNG_DOMAIN_KERNEL:
+               reception_buffer = (void *) &kernel_notification;
+               reception_size = sizeof(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) {
+               PERROR("Failed to read from event source pipe (fd = %i, size to read=%zu, ret=%d)",
+                               pipe, reception_size, ret);
+               /* 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:
+               id = ust_notification.id;
+               capture_buffer_size =
+                               ust_notification.capture_buf_size;
+               break;
+       case LTTNG_DOMAIN_KERNEL:
+               id = kernel_notification.id;
+               capture_buffer_size =
+                       kernel_notification.capture_buf_size;
+               break;
+       default:
+               assert(0);
+       }
+
+       if (capture_buffer_size == 0) {
+               capture_buffer = NULL;
+               goto skip_capture;
+       }
+
+       capture_buffer = zmalloc(capture_buffer_size);
+       if (!capture_buffer) {
+               ERR("[notification-thread] Failed to allocate capture buffer");
+               goto end;
+       }
+
+       /*
+        * Fetch additional payload (capture).
+        */
+       ret = lttng_read(pipe, capture_buffer, capture_buffer_size);
+       if (ret != capture_buffer_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;
+       }
+
+skip_capture:
+       notification = lttng_trigger_notification_create(
+                       id, domain, capture_buffer, capture_buffer_size);
+       if (notification == NULL) {
+               goto end;
+       }
+
+       /* Ownership transfered to the lttng_trigger_notification object */
+       capture_buffer = NULL;
+
+end:
+       free(capture_buffer);
+       return notification;
+}
+
+int handle_notification_thread_event(struct notification_thread_state *state,
+               int pipe,
+               enum lttng_domain_type domain)
+{
+       int ret;
+       enum lttng_trigger_status trigger_status;
+       struct cds_lfht_node *node;
+       struct cds_lfht_iter iter;
+       struct notification_trigger_tokens_ht_element *element;
+       struct lttng_evaluation *evaluation = NULL;
+       struct lttng_trigger_notification *notification = NULL;
+       enum action_executor_status executor_status;
+       struct notification_client_list *client_list = NULL;
+       const char *trigger_name;
+       unsigned int capture_count = 0;
+
+       notification = receive_notification(pipe, domain);
+       if (notification == NULL) {
+               ERR("[notification-thread] Error receiving notification from tracer (fd = %i, domain = %s)",
+                               pipe, lttng_domain_type_str(domain));
+               ret = -1;
+               goto end;
+       }
+
+       /* 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_should_fire(element->trigger)) {
+               ret = 0;
+               goto end_unlock;
+       }
+
+       lttng_trigger_fire(element->trigger);
+
+       trigger_status = lttng_trigger_get_name(element->trigger, &trigger_name);
+       assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       if (LTTNG_CONDITION_STATUS_OK !=
+                       lttng_condition_event_rule_get_capture_descriptor_count(
+                                       lttng_trigger_get_const_condition(
+                                                       element->trigger),
+                                       &capture_count)) {
+               ERR("Get capture count");
+               ret = -1;
+               goto end;
+       }
+
+       if (!notification->capture_buffer && capture_count != 0) {
+               ERR("Expected capture but capture buffer is null");
+               ret = -1;
+               goto end;
+       }
+
+       evaluation = lttng_evaluation_event_rule_create(
+                       container_of(lttng_trigger_get_const_condition(
+                                                    element->trigger),
+                                       struct lttng_condition_event_rule,
+                                       parent),
+                       trigger_name,
+                       notification->capture_buffer,
+                       notification->capture_buf_size, false);
+
+       if (evaluation == NULL) {
+               ERR("[notification-thread] Failed to create event rule hit evaluation");
+               ret = -1;
+               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, evaluation, NULL, 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_unlock(&client_list->lock);
+               break;
+       }
+       case ACTION_EXECUTOR_STATUS_INVALID:
+       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:
+       lttng_trigger_notification_destroy(notification);
+       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)
 int handle_notification_thread_channel_sample(
                struct notification_thread_state *state, int pipe,
                enum lttng_domain_type domain)
@@ -3813,8 +4505,7 @@ int handle_notification_thread_channel_sample(
                 */
                DBG("[notification-thread] Received a sample for an unknown channel from consumerd, key = %" PRIu64 " in %s domain",
                                latest_sample.key.key,
                 */
                DBG("[notification-thread] Received a sample for an unknown channel from consumerd, key = %" PRIu64 " in %s domain",
                                latest_sample.key.key,
-                               domain == LTTNG_DOMAIN_KERNEL ? "kernel" :
-                                       "user space");
+                               lttng_domain_type_str(domain));
                goto end_unlock;
        }
        channel_info = caa_container_of(node, struct channel_info,
                goto end_unlock;
        }
        channel_info = caa_container_of(node, struct channel_info,
@@ -3894,8 +4585,8 @@ int handle_notification_thread_channel_sample(
        }
 
        channel_creds = (typeof(channel_creds)) {
        }
 
        channel_creds = (typeof(channel_creds)) {
-               .uid = channel_info->session_info->uid,
-               .gid = channel_info->session_info->gid,
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(channel_info->session_info->uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(channel_info->session_info->gid),
        };
 
        trigger_list = caa_container_of(node, struct lttng_channel_trigger_list,
        };
 
        trigger_list = caa_container_of(node, struct lttng_channel_trigger_list,
@@ -3903,6 +4594,7 @@ int handle_notification_thread_channel_sample(
        cds_list_for_each_entry(trigger_list_element, &trigger_list->list,
                        node) {
                const struct lttng_condition *condition;
        cds_list_for_each_entry(trigger_list_element, &trigger_list->list,
                        node) {
                const struct lttng_condition *condition;
+               const struct lttng_action *action;
                struct lttng_trigger *trigger;
                struct notification_client_list *client_list = NULL;
                struct lttng_evaluation *evaluation = NULL;
                struct lttng_trigger *trigger;
                struct notification_client_list *client_list = NULL;
                struct lttng_evaluation *evaluation = NULL;
@@ -3912,6 +4604,18 @@ int handle_notification_thread_channel_sample(
                trigger = trigger_list_element->trigger;
                condition = lttng_trigger_get_const_condition(trigger);
                assert(condition);
                trigger = trigger_list_element->trigger;
                condition = lttng_trigger_get_const_condition(trigger);
                assert(condition);
+               action = lttng_trigger_get_const_action(trigger);
+
+               if (!lttng_trigger_should_fire(trigger)) {
+                       goto put_list;
+               }
+
+               lttng_trigger_fire(trigger);
+
+               /* Notify actions are the only type currently supported. */
+               /* TODO support other type of action */
+               assert(lttng_action_get_type(action) ==
+                               LTTNG_ACTION_TYPE_NOTIFY);
 
                /*
                 * Check if any client is subscribed to the result of this
 
                /*
                 * Check if any client is subscribed to the result of this
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);
 
                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 */
 #endif /* NOTIFICATION_THREAD_EVENTS_H */
index eb23d1f78fa2df8d106cdb9fe4caddf1177bb90d..53ee6064ccb529c155673f7d3527b8fc11bcf3dc 100644 (file)
@@ -75,6 +75,17 @@ struct channel_info {
        struct rcu_head rcu_node;
 };
 
        struct rcu_head rcu_node;
 };
 
+/*
+ * Facilities to carry the different notifications type in the action
+ * processing code path.
+ */
+struct lttng_trigger_notification {
+       uint64_t id;
+       enum lttng_domain_type type;
+       size_t capture_buf_size;
+       char *capture_buffer;
+};
+
 struct notification_client_list_element {
        struct notification_client *client;
        struct cds_list_head node;
 struct notification_client_list_element {
        struct notification_client *client;
        struct cds_list_head node;
@@ -239,4 +250,18 @@ int notification_thread_client_communication_update(
                notification_client_id id,
                enum client_transmission_status transmission_status);
 
                notification_client_id id,
                enum client_transmission_status transmission_status);
 
+/*
+ * Takes ownership of the payload if present.
+ */
+LTTNG_HIDDEN
+struct lttng_trigger_notification *lttng_trigger_notification_create(
+               uint64_t id,
+               enum lttng_domain_type domain,
+               char *payload,
+               size_t payload_size);
+
+LTTNG_HIDDEN
+void lttng_trigger_notification_destroy(
+               struct lttng_trigger_notification *trigger_notification);
+
 #endif /* NOTIFICATION_THREAD_INTERNAL_H */
 #endif /* NOTIFICATION_THREAD_INTERNAL_H */
index 3ae8741d65b10a346bec2c965c1581873c8ea155..5a7c0075aa1f24ae72592a1ce7adac61fa9b68e1 100644 (file)
 #include "lttng-sessiond.h"
 #include "health-sessiond.h"
 #include "thread.h"
 #include "lttng-sessiond.h"
 #include "health-sessiond.h"
 #include "thread.h"
+#include "testpoint.h"
+
+#include "kernel.h"
+#include <common/kernel-ctl/kernel-ctl.h>
 
 #include <urcu.h>
 #include <urcu/list.h>
 #include <urcu/rculfhash.h>
 
 
 #include <urcu.h>
 #include <urcu/list.h>
 #include <urcu/rculfhash.h>
 
+
+int trigger_consumption_paused;
 /*
  * Destroy the thread data previously created by the init function.
  */
 /*
  * Destroy the thread data previously created by the init function.
  */
@@ -70,14 +76,21 @@ void notification_thread_handle_destroy(
                        PERROR("close kernel consumer channel monitoring pipe");
                }
        }
                        PERROR("close kernel consumer channel monitoring pipe");
                }
        }
+
 end:
        free(handle);
 }
 
 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 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;
 {
        int ret;
        struct notification_thread_handle *handle;
@@ -135,6 +148,12 @@ struct notification_thread_handle *notification_thread_handle_create(
        } else {
                handle->channel_monitoring_pipes.kernel_consumer = -1;
        }
        } else {
                handle->channel_monitoring_pipes.kernel_consumer = -1;
        }
+
+       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:
 end:
        return handle;
 error:
@@ -305,7 +324,7 @@ int init_poll_set(struct lttng_poll_event *poll_set,
                goto error;
        }
        if (handle->channel_monitoring_pipes.kernel_consumer < 0) {
                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,
        }
        ret = lttng_poll_add(poll_set,
                        handle->channel_monitoring_pipes.kernel_consumer,
@@ -314,6 +333,8 @@ 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;
        }
                ERR("[notification-thread] Failed to add kernel channel monitoring pipe fd to pollset");
                goto error;
        }
+
+skip_kernel_consumer:
 end:
        return ret;
 error:
 end:
        return ret;
 error:
@@ -363,6 +384,14 @@ void fini_thread_state(struct notification_thread_state *state)
                ret = cds_lfht_destroy(state->sessions_ht, NULL);
                assert(!ret);
        }
                ret = cds_lfht_destroy(state->sessions_ht, NULL);
                assert(!ret);
        }
+       if (state->triggers_by_name_uid_ht) {
+               ret = cds_lfht_destroy(state->triggers_by_name_uid_ht, NULL);
+               assert(!ret);
+       }
+       if (state->trigger_tokens_ht) {
+               ret = cds_lfht_destroy(state->trigger_tokens_ht, NULL);
+               assert(!ret);
+       }
        /*
         * Must be destroyed after all channels have been destroyed.
         * See comment in struct lttng_session_trigger_list.
        /*
         * Must be destroyed after all channels have been destroyed.
         * See comment in struct lttng_session_trigger_list.
@@ -404,6 +433,7 @@ int init_thread_state(struct notification_thread_handle *handle,
 
        memset(state, 0, sizeof(*state));
        state->notification_channel_socket = -1;
 
        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();
        lttng_poll_init(&state->events);
 
        ret = notification_channel_socket_create();
@@ -476,7 +506,17 @@ int init_thread_state(struct notification_thread_handle *handle,
        if (!state->triggers_ht) {
                goto error;
        }
        if (!state->triggers_ht) {
                goto error;
        }
+       state->triggers_by_name_uid_ht = cds_lfht_new(DEFAULT_HT_SIZE,
+                       1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+       if (!state->triggers_by_name_uid_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->executor = action_executor_create(handle);
        if (!state->executor) {
                goto error;
        state->executor = action_executor_create(handle);
        if (!state->executor) {
                goto error;
@@ -525,6 +565,63 @@ end:
        return ret;
 }
 
        return ret;
 }
 
+static int handle_trigger_event_pipe(int fd,
+               enum lttng_domain_type domain,
+               uint32_t revents,
+               struct notification_thread_state *state)
+{
+       int ret = 0;
+
+       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 (testpoint(sessiond_handle_trigger_event_pipe)) {
+               ret = 0;
+               goto end;
+       }
+
+       if (caa_unlikely(trigger_consumption_paused)) {
+               DBG("Trigger consumption paused, sleeping...");
+               sleep(1);
+               goto end;
+       }
+
+       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;
+}
+
+/*
+ * Return the event source domain type via parameter.
+ */
+static bool fd_is_event_source(struct notification_thread_handle *handle, int fd, enum lttng_domain_type *domain)
+{
+       struct notification_event_trigger_source_element *source_element, *tmp;
+
+       assert(domain);
+
+       cds_list_for_each_entry_safe(source_element, tmp,
+                       &handle->event_trigger_sources.list, node) {
+               if (source_element->fd != fd) {
+                       continue;
+               }
+               *domain = source_element->domain;
+               return true;
+       }
+       return false;
+}
+
 /*
  * This thread services notification channel clients and commands received
  * from various lttng-sessiond components over a command queue.
 /*
  * This thread services notification channel clients and commands received
  * from various lttng-sessiond components over a command queue.
@@ -535,6 +632,7 @@ void *thread_notification(void *data)
        int ret;
        struct notification_thread_handle *handle = data;
        struct notification_thread_state state;
        int ret;
        struct notification_thread_handle *handle = data;
        struct notification_thread_state state;
+       enum lttng_domain_type domain;
 
        DBG("[notification-thread] Started notification thread");
 
 
        DBG("[notification-thread] Started notification thread");
 
@@ -554,6 +652,10 @@ void *thread_notification(void *data)
                goto end;
        }
 
                goto end;
        }
 
+       if (testpoint(sessiond_thread_notification)) {
+               goto end;
+       }
+
        while (true) {
                int fd_count, i;
 
        while (true) {
                int fd_count, i;
 
@@ -612,6 +714,11 @@ void *thread_notification(void *data)
                                if (ret) {
                                        goto error;
                                }
                                if (ret) {
                                        goto error;
                                }
+                       } else if (fd_is_event_source(handle, fd, &domain)) {
+                               ret = handle_trigger_event_pipe(fd, domain, revents, &state);
+                               if (ret) {
+                                       goto error;
+                               }
                        } else {
                                /* Activity on a client's socket. */
                                if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
                        } else {
                                /* Activity on a client's socket. */
                                if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
index 134804f9d0718a76415e898f556e612921c95f9f..2afcfe15f62afca0d6177a2845742ccbfb90caf2 100644 (file)
 #include <common/hashtable/hashtable.h>
 #include <common/pipe.h>
 #include <lttng/trigger/trigger.h>
 #include <common/hashtable/hashtable.h>
 #include <common/pipe.h>
 #include <lttng/trigger/trigger.h>
+#include <lttng/domain.h>
 #include <pthread.h>
 #include <semaphore.h>
 #include <urcu.h>
 #include <urcu/list.h>
 #include <urcu/rculfhash.h>
 
 #include <pthread.h>
 #include <semaphore.h>
 #include <urcu.h>
 #include <urcu/list.h>
 #include <urcu/rculfhash.h>
 
-
 typedef uint64_t notification_client_id;
 
 typedef uint64_t notification_client_id;
 
+struct notification_event_trigger_source_element {
+       int fd;
+       enum lttng_domain_type domain;
+       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 {
        /*
         * Queue of struct notification command.
 struct notification_thread_handle {
        /*
         * Queue of struct notification command.
@@ -43,6 +57,15 @@ struct notification_thread_handle {
                int ust64_consumer;
                int kernel_consumer;
        } channel_monitoring_pipes;
                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;
+       } event_trigger_sources;
        /* Used to wait for the launch of the notification thread. */
        sem_t ready;
 };
        /* Used to wait for the launch of the notification thread. */
        sem_t ready;
 };
@@ -108,9 +131,14 @@ struct notification_thread_handle {
  *             channels through their struct channel_info (ref-counting is used).
  *
  *   - triggers_ht:
  *             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.
  *             The hash table holds the ownership of the
  *             lttng_trigger_ht_elements along with the triggers themselves.
+ *   - triggers_by_name_uid_ht:
+ *             associates a trigger (name, uid) tuple 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,
  *
  * The thread reacts to the following internal events:
  *   1) creation of a tracing channel,
@@ -154,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),
  *          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_uid_ht
  *    - add trigger to triggers_ht
  *    - evaluate the trigger's condition right away to react if that condition
  *      is true from the beginning.
  *    - add trigger to triggers_ht
  *    - evaluate the trigger's condition right away to react if that condition
  *      is true from the beginning.
@@ -163,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 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 from triggers_by_name_uid_ht
  *    - remove trigger from triggers_ht
  *
  * 5) Reception of a channel monitor sample from the consumer daemon
  *    - remove trigger from triggers_ht
  *
  * 5) Reception of a channel monitor sample from the consumer daemon
@@ -210,6 +240,12 @@ struct notification_thread_state {
        struct cds_lfht *channels_ht;
        struct cds_lfht *sessions_ht;
        struct cds_lfht *triggers_ht;
        struct cds_lfht *channels_ht;
        struct cds_lfht *sessions_ht;
        struct cds_lfht *triggers_ht;
+       struct cds_lfht *triggers_by_name_uid_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_client_id next_notification_client_id;
        struct action_executor *executor;
 };
@@ -218,7 +254,8 @@ struct notification_thread_state {
 struct notification_thread_handle *notification_thread_handle_create(
                struct lttng_pipe *ust32_channel_monitor_pipe,
                struct lttng_pipe *ust64_channel_monitor_pipe,
 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(
 void notification_thread_handle_destroy(
                struct notification_thread_handle *handle);
 struct lttng_thread *launch_notification_thread(
index 6381c117950f767ef705d2ccbb66a218d3c1701d..ac1583dc24eb8f3610dfcb481d8cfb19b726d962 100644 (file)
@@ -12,6 +12,7 @@
 #include <urcu.h>
 #include <common/futex.h>
 #include <common/macros.h>
 #include <urcu.h>
 #include <common/futex.h>
 #include <common/macros.h>
+#include <common/shm.h>
 #include <common/utils.h>
 #include <sys/stat.h>
 
 #include <common/utils.h>
 #include <sys/stat.h>
 
@@ -20,7 +21,6 @@
 #include "testpoint.h"
 #include "health-sessiond.h"
 #include "fd-limit.h"
 #include "testpoint.h"
 #include "health-sessiond.h"
 #include "fd-limit.h"
-#include "shm.h"
 #include "utils.h"
 #include "thread.h"
 
 #include "utils.h"
 #include "thread.h"
 
index 30ec241249e5a1020b88b63ee01845122fbb9b2a..77524ed8e8538588b0ef3cb274266e2014d4c324 100644 (file)
@@ -48,8 +48,8 @@ int subscribe_session_consumed_size_rotation(struct ltt_session *session, uint64
        enum lttng_notification_channel_status nc_status;
        struct lttng_action *action;
        const struct lttng_credentials session_creds = {
        enum lttng_notification_channel_status nc_status;
        struct lttng_action *action;
        const struct lttng_credentials session_creds = {
-               .uid = session->uid,
-               .gid = session->gid,
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(session->uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(session->gid),
        };
 
        session->rotate_condition = lttng_condition_session_consumed_size_create();
        };
 
        session->rotate_condition = lttng_condition_session_consumed_size_create();
index f8134f8ad966a950b0c97f13bbb18f2ba12d89b9..a4d1163399e7cd74975b754b8c51160d1eb189f1 100644 (file)
@@ -574,8 +574,8 @@ struct lttng_trace_chunk *session_create_new_trace_chunk(
        const char *base_path;
        struct lttng_directory_handle *session_output_directory = NULL;
        const struct lttng_credentials session_credentials = {
        const char *base_path;
        struct lttng_directory_handle *session_output_directory = NULL;
        const struct lttng_credentials session_credentials = {
-               .uid = session->uid,
-               .gid = session->gid,
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(session->uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(session->gid),
        };
        uint64_t next_chunk_id;
        const struct consumer_output *output;
        };
        uint64_t next_chunk_id;
        const struct consumer_output *output;
index f0ab4ce0423530b25a6de8beb599a46fcd08ecec..3a9b7725d41d630bf4771021e75a8e5f8d69b0f0 100644 (file)
@@ -24,6 +24,7 @@ struct sessiond_config sessiond_config_build_defaults = {
        .verbose_consumer =                     0,
 
        .agent_tcp_port =                       { .begin = DEFAULT_AGENT_TCP_PORT_RANGE_BEGIN, .end = DEFAULT_AGENT_TCP_PORT_RANGE_END },
        .verbose_consumer =                     0,
 
        .agent_tcp_port =                       { .begin = DEFAULT_AGENT_TCP_PORT_RANGE_BEGIN, .end = DEFAULT_AGENT_TCP_PORT_RANGE_END },
+       .trigger_error_counter_bucket =         4096,
        .app_socket_timeout =                   DEFAULT_APP_SOCKET_RW_TIMEOUT,
 
        .no_kernel =                            false,
        .app_socket_timeout =                   DEFAULT_APP_SOCKET_RW_TIMEOUT,
 
        .no_kernel =                            false,
index 9ce036e70a8e56e2fbd2f25ad4db4ee89e101f2e..7e0a789effbf8aa39ab1ef68a41c8157c93ec4b8 100644 (file)
@@ -29,6 +29,8 @@ struct sessiond_config {
        int verbose_consumer;
        /* Agent TCP port range for registration. Used by the agent thread. */
        struct config_int_range agent_tcp_port;
        int verbose_consumer;
        /* Agent TCP port range for registration. Used by the agent thread. */
        struct config_int_range agent_tcp_port;
+
+       int trigger_error_counter_bucket;
        /* Socket timeout for receiving and sending (in seconds). */
        int app_socket_timeout;
 
        /* Socket timeout for receiving and sending (in seconds). */
        int app_socket_timeout;
 
diff --git a/src/bin/lttng-sessiond/shm.c b/src/bin/lttng-sessiond/shm.c
deleted file mode 100644 (file)
index 1fc07db..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <fcntl.h>
-#include <limits.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <urcu.h>
-
-#include <common/error.h>
-
-#include "shm.h"
-
-/*
- * Using fork to set umask in the child process (not multi-thread safe). We
- * deal with the shm_open vs ftruncate race (happening when the sessiond owns
- * the shm and does not let everybody modify it, to ensure safety against
- * shm_unlink) by simply letting the mmap fail and retrying after a few
- * seconds. For global shm, everybody has rw access to it until the sessiond
- * starts.
- */
-static int get_wait_shm(char *shm_path, size_t mmap_size, int global)
-{
-       int wait_shm_fd, ret;
-       mode_t mode;
-
-       assert(shm_path);
-
-       /* Default permissions */
-       mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
-
-       /*
-        * Change owner of the shm path.
-        */
-       if (global) {
-               /*
-                * If global session daemon, any application can
-                * register. Make it initially writeable so applications
-                * registering concurrently can do ftruncate() by
-                * themselves.
-                */
-               mode |= S_IROTH | S_IWOTH;
-       }
-
-       /*
-        * We're alone in a child process, so we can modify the process-wide
-        * umask.
-        */
-       umask(~mode);
-
-       /*
-        * Try creating shm (or get rw access). We don't do an exclusive open,
-        * because we allow other processes to create+ftruncate it concurrently.
-        *
-        * A sysctl, fs.protected_regular may prevent the session daemon from
-        * opening a previously created shm when the O_CREAT flag is provided.
-        * Systemd enables this ABI-breaking change by default since v241.
-        *
-        * First, attempt to use the create-or-open semantic that is
-        * desired here. If this fails with EACCES, work around this broken
-        * behaviour and attempt to open the shm without the O_CREAT flag.
-        *
-        * The two attempts are made in this order since applications are
-        * expected to race with the session daemon to create this shm.
-        * Attempting an shm_open() without the O_CREAT flag first could fail
-        * because the file doesn't exist. It could then be created by an
-        * application, which would cause a second try with the O_CREAT flag to
-        * fail with EACCES.
-        *
-        * Note that this introduces a new failure mode where a user could
-        * launch an application (creating the shm) and unlink the shm while
-        * the session daemon is launching, causing the second attempt
-        * to fail. This is not recovered-from as unlinking the shm will
-        * prevent userspace tracing from succeeding anyhow: the sessiond would
-        * use a now-unlinked shm, while the next application would create
-        * a new named shm.
-        */
-       wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode);
-       if (wait_shm_fd < 0) {
-               if (errno == EACCES) {
-                       /* Work around sysctl fs.protected_regular. */
-                       DBG("shm_open of %s returned EACCES, this may be caused "
-                                       "by the fs.protected_regular sysctl. "
-                                       "Attempting to open the shm without "
-                                       "creating it.", shm_path);
-                       wait_shm_fd = shm_open(shm_path, O_RDWR, mode);
-               }
-               if (wait_shm_fd < 0) {
-                       PERROR("Failed to open wait shm at %s", shm_path);
-                       goto error;
-               }
-       }
-
-       ret = ftruncate(wait_shm_fd, mmap_size);
-       if (ret < 0) {
-               PERROR("ftruncate wait shm");
-               exit(EXIT_FAILURE);
-       }
-
-#ifndef __FreeBSD__
-       if (global) {
-               ret = fchown(wait_shm_fd, 0, 0);
-               if (ret < 0) {
-                       PERROR("fchown");
-                       exit(EXIT_FAILURE);
-               }
-               /*
-                * If global session daemon, any application can
-                * register so the shm needs to be set in read-only mode
-                * for others.
-                */
-               mode &= ~S_IWOTH;
-               ret = fchmod(wait_shm_fd, mode);
-               if (ret < 0) {
-                       PERROR("fchmod");
-                       exit(EXIT_FAILURE);
-               }
-       } else {
-               ret = fchown(wait_shm_fd, getuid(), getgid());
-               if (ret < 0) {
-                       PERROR("fchown");
-                       exit(EXIT_FAILURE);
-               }
-       }
-#else
-#warning "FreeBSD does not support setting file mode on shm FD."
-#endif
-
-       DBG("Got the wait shm fd %d", wait_shm_fd);
-
-       return wait_shm_fd;
-
-error:
-       DBG("Failing to get the wait shm fd");
-
-       return -1;
-}
-
-/*
- * Return the wait shm mmap for UST application notification. The global
- * variable is used to indicate if the the session daemon is global
- * (root:tracing) or running with an unprivileged user.
- *
- * This returned value is used by futex_wait_update() in futex.c to WAKE all
- * waiters which are UST application waiting for a session daemon.
- */
-char *shm_ust_get_mmap(char *shm_path, int global)
-{
-       size_t mmap_size;
-       int wait_shm_fd, ret;
-       char *wait_shm_mmap;
-       long sys_page_size;
-
-       assert(shm_path);
-
-       sys_page_size = sysconf(_SC_PAGE_SIZE);
-       if (sys_page_size < 0) {
-               PERROR("sysconf PAGE_SIZE");
-               goto error;
-       }
-       mmap_size = sys_page_size;
-
-       wait_shm_fd = get_wait_shm(shm_path, mmap_size, global);
-       if (wait_shm_fd < 0) {
-               goto error;
-       }
-
-       wait_shm_mmap = mmap(NULL, mmap_size, PROT_WRITE | PROT_READ,
-                       MAP_SHARED, wait_shm_fd, 0);
-
-       /* close shm fd immediately after taking the mmap reference */
-       ret = close(wait_shm_fd);
-       if (ret) {
-               PERROR("Error closing fd");
-       }
-
-       if (wait_shm_mmap == MAP_FAILED) {
-               DBG("mmap error (can be caused by race with ust).");
-               goto error;
-       }
-
-       return wait_shm_mmap;
-
-error:
-       return NULL;
-}
diff --git a/src/bin/lttng-sessiond/shm.h b/src/bin/lttng-sessiond/shm.h
deleted file mode 100644 (file)
index 94d2b72..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#ifndef _LTT_SHM_H
-#define _LTT_SHM_H
-
-char *shm_ust_get_mmap(char *shm_path, int global);
-
-#endif /* _LTT_SHM_H */
index 8524e1fd3186d54088840fb22d770527c1f627a8..37bb93b8487e4da93f8d2d3e6e47bb145d342d9b 100644 (file)
@@ -22,5 +22,7 @@ TESTPOINT_DECL(sessiond_thread_manage_consumer);
 TESTPOINT_DECL(sessiond_thread_ht_cleanup);
 TESTPOINT_DECL(sessiond_thread_app_manage_notify);
 TESTPOINT_DECL(sessiond_thread_app_reg_dispatch);
 TESTPOINT_DECL(sessiond_thread_ht_cleanup);
 TESTPOINT_DECL(sessiond_thread_app_manage_notify);
 TESTPOINT_DECL(sessiond_thread_app_reg_dispatch);
+TESTPOINT_DECL(sessiond_thread_notification);
+TESTPOINT_DECL(sessiond_handle_trigger_event_pipe);
 
 #endif /* SESSIOND_TESTPOINT_H */
 
 #endif /* SESSIOND_TESTPOINT_H */
index dfdc9fd14e96b9b2124034983dc0d1db4525633a..198e1dd4feca71f1e15333d9c1dfcbb2e0f6110a 100644 (file)
 
 #include <lttng/event.h>
 #include <lttng/lttng-error.h>
 
 #include <lttng/event.h>
 #include <lttng/lttng-error.h>
+#include <lttng/kernel-probe.h>
 #include <lttng/userspace-probe.h>
 #include <lttng/userspace-probe-internal.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/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>
 #include <common/common.h>
 #include <common/defaults.h>
 #include <common/trace-chunk.h>
@@ -62,7 +71,7 @@ struct ltt_kernel_channel *trace_kernel_get_channel_by_name(
 struct ltt_kernel_event *trace_kernel_find_event(
                char *name, struct ltt_kernel_channel *channel,
                enum lttng_event_type type,
 struct ltt_kernel_event *trace_kernel_find_event(
                char *name, struct ltt_kernel_channel *channel,
                enum lttng_event_type type,
-               struct lttng_filter_bytecode *filter)
+               struct lttng_bytecode *filter)
 {
        struct ltt_kernel_event *ev;
        int found = 0;
 {
        struct ltt_kernel_event *ev;
        int found = 0;
@@ -321,7 +330,7 @@ error:
  */
 enum lttng_error_code trace_kernel_create_event(
                struct lttng_event *ev, char *filter_expression,
  */
 enum lttng_error_code trace_kernel_create_event(
                struct lttng_event *ev, char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct ltt_kernel_event **kernel_event)
 {
        enum lttng_error_code ret;
                struct ltt_kernel_event **kernel_event)
 {
        enum lttng_error_code ret;
@@ -468,6 +477,212 @@ error:
        return ret;
 }
 
        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_trigger *trigger,
+               uint64_t token,
+               uint64_t trigger_error_counter_index,
+               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;
+       const struct lttng_condition *condition = NULL;
+       const struct lttng_event_rule *event_rule = NULL;
+
+       assert(kernel_token_event_rule);
+
+       condition = lttng_trigger_get_condition(trigger);
+       assert(condition);
+       assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+       assert(lttng_condition_event_rule_get_rule(condition, &event_rule) == LTTNG_CONDITION_STATUS_OK);
+       assert(event_rule);
+       assert(lttng_event_rule_get_type(event_rule) != LTTNG_EVENT_RULE_TYPE_UNKNOWN);
+
+       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;
+       local_kernel_token_event_rule->error_counter_index = trigger_error_counter_index;
+
+       /* Get the reference of the event rule */
+       lttng_trigger_get(trigger);
+
+       local_kernel_token_event_rule->trigger = trigger;
+       /* 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:
+       {
+               uint64_t address = 0, offset = 0;
+               const char *symbol_name = NULL;
+               const struct lttng_kernel_probe_location *location = NULL;
+               enum lttng_kernel_probe_location_status k_status;
+
+               status = lttng_event_rule_kprobe_get_location(rule, &location);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+                       goto error;
+               }
+
+               switch (lttng_kernel_probe_location_get_type(location)) {
+               case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS:
+               {
+                       k_status = lttng_kernel_probe_location_address_get_address(
+                                       location, &address);
+                       if (k_status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) {
+                               ERR("Getting kernel probe address failed.");
+                       }
+
+                       break;
+               }
+               case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET:
+               {
+                       k_status = lttng_kernel_probe_location_symbol_get_offset(
+                                       location, &offset);
+                       if (k_status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) {
+                               ERR("Getting kernel probe symbol offset failed.");
+                       }
+
+                       symbol_name = lttng_kernel_probe_location_symbol_get_name(
+                                       location);
+                       break;
+               }
+               default:
+                       ERR("Unknown kernel probe location type (%d)",
+                                       lttng_kernel_probe_location_get_type(
+                                                       location));
+                       ret = LTTNG_ERR_INVALID;
+                       goto error;
+               }
+
+               kernel_trigger->instrumentation = LTTNG_KERNEL_KPROBE;
+               kernel_trigger->u.kprobe.addr = address;
+               kernel_trigger->u.kprobe.offset = offset;
+               if (symbol_name) {
+                       strncpy(kernel_trigger->u.kprobe.symbol_name,
+                                       symbol_name, 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);
+               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.
  *
 /*
  * Allocate and initialize a kernel metadata.
  *
@@ -623,6 +838,30 @@ void trace_kernel_destroy_event(struct ltt_kernel_event *event)
        free(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);
+
+       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_trigger_put(event->trigger);
+       free(event);
+}
 /*
  * Cleanup kernel context structure.
  */
 /*
  * Cleanup kernel context structure.
  */
index dd6f21edf2bb06452d8f2c8f399d73aec65cf4ee..0721a57d3da202051f117840d07b9e08de304572 100644 (file)
@@ -23,6 +23,11 @@ struct ltt_kernel_event_list {
        struct cds_list_head head;
 };
 
        struct cds_list_head head;
 };
 
+/* Kernel event rule token list */
+struct ltt_kernel_token_event_rule_ht {
+       struct cds_lfht *ht;
+};
+
 /* Channel stream list */
 struct ltt_kernel_stream_list {
        struct cds_list_head head;
 /* Channel stream list */
 struct ltt_kernel_stream_list {
        struct cds_list_head head;
@@ -48,8 +53,21 @@ struct ltt_kernel_event {
        struct lttng_kernel_event *event;
        struct cds_list_head list;
        char *filter_expression;
        struct lttng_kernel_event *event;
        struct cds_list_head list;
        char *filter_expression;
-       struct lttng_filter_bytecode *filter;
+       struct lttng_bytecode *filter;
+       struct lttng_userspace_probe_location *userspace_probe_location;
+};
+
+/* Kernel event */
+struct ltt_kernel_token_event_rule {
+       int fd;
+       uint64_t error_counter_index;
+       int enabled;
+       enum lttng_event_type type;
+       struct lttng_trigger *trigger;
+       uint64_t token;
+       const struct lttng_bytecode *filter;
        struct lttng_userspace_probe_location *userspace_probe_location;
        struct lttng_userspace_probe_location *userspace_probe_location;
+       struct cds_lfht_node ht_node;
 };
 
 /* Kernel channel */
 };
 
 /* Kernel channel */
@@ -132,7 +150,7 @@ struct ltt_kernel_event *trace_kernel_get_event_by_name(
 struct ltt_kernel_event *trace_kernel_find_event(
                char *name, struct ltt_kernel_channel *channel,
                enum lttng_event_type type,
 struct ltt_kernel_event *trace_kernel_find_event(
                char *name, struct ltt_kernel_channel *channel,
                enum lttng_event_type type,
-               struct lttng_filter_bytecode *filter);
+               struct lttng_bytecode *filter);
 struct ltt_kernel_channel *trace_kernel_get_channel_by_name(
                const char *name, struct ltt_kernel_session *session);
 
 struct ltt_kernel_channel *trace_kernel_get_channel_by_name(
                const char *name, struct ltt_kernel_session *session);
 
@@ -143,15 +161,22 @@ struct ltt_kernel_session *trace_kernel_create_session(void);
 struct ltt_kernel_channel *trace_kernel_create_channel(
                struct lttng_channel *chan);
 enum lttng_error_code trace_kernel_create_event(struct lttng_event *ev,
 struct ltt_kernel_channel *trace_kernel_create_channel(
                struct lttng_channel *chan);
 enum lttng_error_code trace_kernel_create_event(struct lttng_event *ev,
-               char *filter_expression, struct lttng_filter_bytecode *filter,
+               char *filter_expression, struct lttng_bytecode *filter,
                struct ltt_kernel_event **kernel_event);
 struct ltt_kernel_metadata *trace_kernel_create_metadata(void);
 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);
                struct ltt_kernel_event **kernel_event);
 struct ltt_kernel_metadata *trace_kernel_create_metadata(void);
 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_trigger *trigger,
+               uint64_t token,
+               uint64_t trigger_error_counter_index,
+               struct ltt_kernel_token_event_rule **kernel_token_event_rule);
 struct ltt_kernel_context *trace_kernel_copy_context(
                struct ltt_kernel_context *ctx);
 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
 
 /*
  * Destroy functions free() the data structure and remove from linked list if
@@ -163,6 +188,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_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 */
 void trace_kernel_free_session(struct ltt_kernel_session *session);
 
 #endif /* _LTT_TRACE_KERNEL_H */
index 7ee379ef898a0890becd9b90e413d10687719a0f..527b354f1a5e5c287e17733202d57670e78ce687 100644 (file)
@@ -194,7 +194,7 @@ error:
  * MUST be acquired before calling this.
  */
 struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht,
  * MUST be acquired before calling this.
  */
 struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht,
-               char *name, struct lttng_filter_bytecode *filter,
+               char *name, struct lttng_bytecode *filter,
                enum lttng_ust_loglevel_type loglevel_type, int loglevel_value,
                struct lttng_event_exclusion *exclusion)
 {
                enum lttng_ust_loglevel_type loglevel_type, int loglevel_value,
                struct lttng_event_exclusion *exclusion)
 {
@@ -446,7 +446,7 @@ end:
  */
 enum lttng_error_code trace_ust_create_event(struct lttng_event *ev,
                char *filter_expression,
  */
 enum lttng_error_code trace_ust_create_event(struct lttng_event *ev,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                bool internal_event,
                struct ltt_ust_event **ust_event)
                struct lttng_event_exclusion *exclusion,
                bool internal_event,
                struct ltt_ust_event **ust_event)
index ecd0b877166abf22e7453a62c1b3c243016d9e18..53e1ab2b5b2a98836ff520e7bf025426c909f74b 100644 (file)
@@ -24,7 +24,7 @@ struct agent;
 
 struct ltt_ust_ht_key {
        const char *name;
 
 struct ltt_ust_ht_key {
        const char *name;
-       const struct lttng_filter_bytecode *filter;
+       const struct lttng_bytecode *filter;
        enum lttng_ust_loglevel_type loglevel_type;
        int loglevel_value;
        const struct lttng_event_exclusion *exclusion;
        enum lttng_ust_loglevel_type loglevel_type;
        int loglevel_value;
        const struct lttng_event_exclusion *exclusion;
@@ -43,7 +43,7 @@ struct ltt_ust_event {
        struct lttng_ust_event attr;
        struct lttng_ht_node_str node;
        char *filter_expression;
        struct lttng_ust_event attr;
        struct lttng_ht_node_str node;
        char *filter_expression;
-       struct lttng_filter_bytecode *filter;
+       struct lttng_bytecode *filter;
        struct lttng_event_exclusion *exclusion;
        /*
         * An internal event is an event which was created by the session daemon
        struct lttng_event_exclusion *exclusion;
        /*
         * An internal event is an event which was created by the session daemon
@@ -182,7 +182,7 @@ int trace_ust_ht_match_event_by_name(struct cds_lfht_node *node,
  * Lookup functions. NULL is returned if not found.
  */
 struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht,
  * Lookup functions. NULL is returned if not found.
  */
 struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht,
-               char *name, struct lttng_filter_bytecode *filter,
+               char *name, struct lttng_bytecode *filter,
                enum lttng_ust_loglevel_type loglevel_type, int loglevel_value,
                struct lttng_event_exclusion *exclusion);
 struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht,
                enum lttng_ust_loglevel_type loglevel_type, int loglevel_value,
                struct lttng_event_exclusion *exclusion);
 struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht,
@@ -198,7 +198,7 @@ struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr,
                enum lttng_domain_type domain);
 enum lttng_error_code trace_ust_create_event(struct lttng_event *ev,
                char *filter_expression,
                enum lttng_domain_type domain);
 enum lttng_error_code trace_ust_create_event(struct lttng_event *ev,
                char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                bool internal_event, struct ltt_ust_event **ust_event);
 struct ltt_ust_context *trace_ust_create_context(
                struct lttng_event_exclusion *exclusion,
                bool internal_event, struct ltt_ust_event **ust_event);
 struct ltt_ust_context *trace_ust_create_context(
@@ -270,7 +270,7 @@ struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr,
 static inline
 enum lttng_error_code trace_ust_create_event(struct lttng_event *ev,
                const char *filter_expression,
 static inline
 enum lttng_error_code trace_ust_create_event(struct lttng_event *ev,
                const char *filter_expression,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                bool internal_event, struct ltt_ust_event **ust_event)
 {
                struct lttng_event_exclusion *exclusion,
                bool internal_event, struct ltt_ust_event **ust_event)
 {
@@ -310,7 +310,7 @@ int trace_ust_match_context(const struct ltt_ust_context *uctx,
 }
 static inline
 struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht,
 }
 static inline
 struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht,
-               char *name, struct lttng_filter_bytecode *filter,
+               char *name, struct lttng_bytecode *filter,
                enum lttng_ust_loglevel_type loglevel_type, int loglevel_value,
                struct lttng_event_exclusion *exclusion)
 {
                enum lttng_ust_loglevel_type loglevel_type, int loglevel_value,
                struct lttng_event_exclusion *exclusion)
 {
diff --git a/src/bin/lttng-sessiond/trigger-error-accounting.c b/src/bin/lttng-sessiond/trigger-error-accounting.c
new file mode 100644 (file)
index 0000000..9b50b64
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <urcu/compiler.h>
+#include <pthread.h>
+
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/index-allocator.h>
+#include <common/kernel-ctl/kernel-ctl.h>
+#include <common/shm.h>
+#include <lttng/trigger/trigger-internal.h>
+
+#include "lttng-ust-error.h"
+#include "trigger-error-accounting.h"
+#include "ust-app.h"
+
+struct index_ht_entry {
+       struct lttng_ht_node_u64 node;
+       uint64_t error_counter_index;
+       struct rcu_head rcu_head;
+};
+
+struct error_account_entry {
+       struct lttng_ht_node_u64 node;
+       struct rcu_head rcu_head;
+       struct lttng_ust_object_data *counter;
+       struct lttng_ust_object_data **cpu_counters;
+       struct ustctl_daemon_counter *daemon_counter;
+};
+
+struct kernel_error_account_entry {
+       int kernel_trigger_error_counter_fd;
+};
+
+static struct kernel_error_account_entry kernel_error_accountant = { 0 };
+
+/* Hashtable mapping trigger token to index_ht_entry */
+static struct lttng_ht *error_counter_indexes_ht;
+
+/* Hashtable mapping uid to error_account_entry */
+static struct lttng_ht *error_counter_uid_ht;
+
+static uint64_t error_counter_size = 0;
+struct lttng_index_allocator *index_allocator;
+
+
+void trigger_error_accounting_init(uint64_t nb_bucket)
+{
+       struct lttng_index_allocator *error_counter_index_allocator;
+
+       error_counter_index_allocator = lttng_index_allocator_create(nb_bucket);
+       if (!error_counter_index_allocator) {
+               ERR("Failed to allocate trigger error counter index");
+               goto error_index_allocator;
+       }
+
+       index_allocator = error_counter_index_allocator;
+
+       error_counter_indexes_ht = lttng_ht_new(16, LTTNG_HT_TYPE_U64);
+       error_counter_uid_ht = lttng_ht_new(16, LTTNG_HT_TYPE_U64);
+       error_counter_size = nb_bucket;
+
+error_index_allocator:
+       return;
+}
+
+static
+enum trigger_error_accounting_status get_error_counter_index_for_token(
+               uint64_t tracer_token, uint64_t *error_counter_index)
+{
+       struct lttng_ht_node_u64 *node;
+       struct lttng_ht_iter iter;
+       struct index_ht_entry *index_entry;;
+       enum trigger_error_accounting_status status;
+
+       lttng_ht_lookup(error_counter_indexes_ht, &tracer_token, &iter);
+       node = lttng_ht_iter_get_node_u64(&iter);
+       if (node) {
+               index_entry = caa_container_of(node, struct index_ht_entry, node);
+               *error_counter_index = index_entry->error_counter_index;
+               status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+       } else {
+               status = TRIGGER_ERROR_ACCOUNTING_STATUS_NOT_FOUND;
+       }
+
+       return status;
+}
+
+#ifdef HAVE_LIBLTTNG_UST_CTL
+static
+struct error_account_entry *get_uid_accounting_entry(const struct ust_app *app)
+{
+       struct error_account_entry *entry;
+       struct lttng_ht_node_u64 *node;
+       struct lttng_ht_iter iter;
+       uint64_t key = app->uid;
+
+       lttng_ht_lookup(error_counter_uid_ht, &key, &iter);
+       node = lttng_ht_iter_get_node_u64(&iter);
+       if(node == NULL) {
+               entry = NULL;
+       } else {
+               entry = caa_container_of(node, struct error_account_entry, node);
+       }
+
+       return entry;
+}
+
+static
+struct error_account_entry *create_uid_accounting_entry(
+               const struct ust_app *app)
+{
+       int i, ret, nr_counter_cpu_fds;
+       struct ustctl_counter_dimension dimension[1] = {0};
+       struct ustctl_daemon_counter *daemon_counter;
+       struct lttng_ust_object_data *counter, **counter_cpus;
+       int *counter_cpu_fds;
+       struct error_account_entry *entry = NULL;
+
+       entry = zmalloc(sizeof(struct error_account_entry));
+       if (!entry) {
+               PERROR("Allocating trigger error acounting entry")
+               goto error;
+       }
+
+       nr_counter_cpu_fds = ustctl_get_nr_cpu_per_counter();
+       counter_cpu_fds = zmalloc(nr_counter_cpu_fds * sizeof(*counter_cpu_fds));
+       if (!counter_cpu_fds) {
+               ret = -1;
+               goto error_counter_cpu_fds_alloc;
+       }
+
+       counter_cpus = zmalloc(nr_counter_cpu_fds * sizeof(*counter_cpus));
+       if (!counter_cpus) {
+               ret = -1;
+               goto error_counter_cpus_alloc;
+       }
+
+       for (i = 0; i < nr_counter_cpu_fds; i++) {
+               counter_cpu_fds[i] = shm_create_anonymous("trigger-error-accounting");
+               //FIXME error handling
+       }
+
+
+       dimension[0].size = error_counter_size;
+       dimension[0].has_underflow = false;
+       dimension[0].has_overflow = false;
+
+       daemon_counter = ustctl_create_counter(1, dimension, 0, -1,
+                       nr_counter_cpu_fds, counter_cpu_fds,
+                       USTCTL_COUNTER_BITNESS_32,
+                       USTCTL_COUNTER_ARITHMETIC_MODULAR,
+                       USTCTL_COUNTER_ALLOC_PER_CPU);
+       assert(daemon_counter);
+
+       ret = ustctl_create_counter_data(daemon_counter, &counter);
+       assert(ret == 0);
+
+       for (i = 0; i < nr_counter_cpu_fds; i++) {
+               ret = ustctl_create_counter_cpu_data(daemon_counter, i,
+                               &counter_cpus[i]);
+               assert(ret == 0);
+       }
+
+       entry->daemon_counter = daemon_counter;
+       entry->counter = counter;
+       entry->cpu_counters = counter_cpus;
+
+       lttng_ht_node_init_u64(&entry->node, app->uid);
+       lttng_ht_add_unique_u64(error_counter_uid_ht, &entry->node);
+
+       goto end;
+
+error_counter_cpus_alloc:
+       free(counter_cpu_fds);
+error_counter_cpu_fds_alloc:
+       free(entry);
+error:
+       entry = NULL;
+end:
+       return entry;
+}
+
+static
+enum trigger_error_accounting_status send_counter_data_to_ust(
+               struct ust_app *app,
+               struct lttng_ust_object_data *new_counter)
+{
+       int ret;
+       enum trigger_error_accounting_status status;
+
+       /* Attach counter to trigger group */
+       pthread_mutex_lock(&app->sock_lock);
+       ret = ustctl_send_counter_data_to_ust(app->sock,
+                       app->token_communication.handle->handle, new_counter);
+       pthread_mutex_unlock(&app->sock_lock);
+       if (ret < 0) {
+               if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+                       ERR("Error ustctl send counter data to app pid: %d with ret %d",
+                                       app->pid, ret);
+                       status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR;
+               } else {
+                       DBG3("UST app send counter data to ust failed. Application is dead.");
+                       status = TRIGGER_ERROR_ACCOUNTING_STATUS_APP_DEAD;
+               }
+               goto end;
+       }
+
+       status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+end:
+       return status;
+}
+
+static
+enum trigger_error_accounting_status send_counter_cpu_data_to_ust(
+               struct ust_app *app,
+               struct lttng_ust_object_data *new_counter,
+               struct lttng_ust_object_data *new_counter_cpu)
+{
+       int ret;
+       enum trigger_error_accounting_status status;
+
+       pthread_mutex_lock(&app->sock_lock);
+       ret = ustctl_send_counter_cpu_data_to_ust(app->sock,
+                       new_counter, new_counter_cpu);
+       pthread_mutex_unlock(&app->sock_lock);
+       if (ret < 0) {
+               if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+                       ERR("Error ustctl send counter cpu data to app pid: %d with ret %d",
+                                       app->pid, ret);
+                       status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR;
+               } else {
+                       DBG3("UST app send counter cpu data to ust failed. Application is dead.");
+                       status = TRIGGER_ERROR_ACCOUNTING_STATUS_APP_DEAD;
+               }
+               goto end;
+       }
+
+       status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+end:
+       return status;
+}
+
+enum trigger_error_accounting_status trigger_error_accounting_register_app(
+               struct ust_app *app)
+{
+       int ret;
+       uint64_t i, nr_counter_cpu_fds;
+       struct lttng_ust_object_data *new_counter;
+       struct error_account_entry *entry;
+       enum trigger_error_accounting_status status;
+
+       /*
+        * Check if we already have a error counter for the user id of this
+        * app. If not, create one.
+        */
+       entry = get_uid_accounting_entry(app);
+       if (entry == NULL) {
+               entry = create_uid_accounting_entry(app);
+       }
+
+       /* Duplicate counter object data*/
+       ret = ustctl_duplicate_ust_object_data(&new_counter,
+                       entry->counter);
+       assert(ret == 0);
+
+       status = send_counter_data_to_ust(app, new_counter);
+       if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+               goto end;
+       }
+
+       nr_counter_cpu_fds = ustctl_get_nr_cpu_per_counter();
+       for (i = 0; i < nr_counter_cpu_fds; i++) {
+               struct lttng_ust_object_data *new_counter_cpu = NULL;
+
+               ret = ustctl_duplicate_ust_object_data(&new_counter_cpu,
+                               entry->cpu_counters[i]);
+               assert(ret == 0);
+
+               status = send_counter_cpu_data_to_ust(app,
+                               new_counter, new_counter_cpu);
+               if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+                       goto end;
+               }
+       }
+
+end:
+       return status;
+}
+
+static
+enum trigger_error_accounting_status trigger_error_accounting_ust_get_count(
+               const struct lttng_trigger *trigger, uint64_t *count)
+{
+       struct lttng_ht_iter iter;
+       struct error_account_entry *uid_entry;
+       uint64_t error_counter_index, global_sum = 0;
+       enum trigger_error_accounting_status status;
+       size_t dimension_indexes[1];
+
+       /*
+        * Go over all error counters (ignoring uid) as a trigger (and trigger
+        * errors) can be generated from any applications that this session
+        * daemon is managing.
+        */
+
+       status = get_error_counter_index_for_token(
+                       lttng_trigger_get_tracer_token(trigger), &error_counter_index);
+       if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+               goto end;
+       }
+
+       dimension_indexes[0] = error_counter_index;
+
+       rcu_read_lock();
+
+       cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter,
+                       uid_entry, node.node) {
+               int ret;
+               int64_t local_value = 0;;
+               bool overflow = 0, underflow = 0;
+               ret = ustctl_counter_aggregate(uid_entry->daemon_counter,
+                               dimension_indexes, &local_value, &overflow,
+                               &underflow);
+               assert(ret == 0);
+
+               /* should always be zero or above. */
+               assert(local_value >= 0);
+               global_sum += (uint64_t) local_value;
+
+       }
+
+       rcu_read_unlock();
+
+       *count = global_sum;
+       status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+
+end:
+       return status;
+}
+
+static
+enum trigger_error_accounting_status trigger_error_accounting_ust_clear(
+               const struct lttng_trigger *trigger)
+{
+       struct lttng_ht_iter iter;
+       struct error_account_entry *uid_entry;
+       uint64_t error_counter_index;
+       enum trigger_error_accounting_status status;
+       size_t dimension_indexes[1];
+
+       /*
+        * Go over all error counters (ignoring uid) as a trigger (and trigger
+        * errors) can be generated from any applications that this session
+        * daemon is managing.
+        */
+
+       status = get_error_counter_index_for_token(
+                       lttng_trigger_get_tracer_token(trigger),
+                       &error_counter_index);
+       if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+               ERR("Error getting trigger error counter index");
+               goto end;
+       }
+
+       dimension_indexes[0] = error_counter_index;
+
+       rcu_read_lock();
+       cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter,
+                       uid_entry, node.node) {
+               int ret;
+               ret = ustctl_counter_clear(uid_entry->daemon_counter,
+                               dimension_indexes);
+               assert(ret == 0);
+       }
+
+       rcu_read_unlock();
+       status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+end:
+       return status;
+}
+
+#endif /* HAVE_LIBLTTNG_UST_CTL */
+
+void trigger_error_accounting_register_kernel(int kernel_trigger_group_fd)
+{
+       int local_fd = -1, ret;
+       struct lttng_kernel_counter_conf error_counter_conf;
+
+
+       error_counter_conf.arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR;
+       error_counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_64BITS;
+       error_counter_conf.global_sum_step = 0;
+       error_counter_conf.number_dimensions = 1;
+       error_counter_conf.dimensions[0].size = error_counter_size;
+       error_counter_conf.dimensions[0].has_underflow = false;
+       error_counter_conf.dimensions[0].has_overflow = false;
+
+       ret = kernctl_create_trigger_group_error_counter(
+                       kernel_trigger_group_fd, &error_counter_conf);
+       if (ret < 0) {
+               PERROR("ioctl kernel create trigger group error counter");
+               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 trigger error counter fd");
+               goto error;
+       }
+
+       DBG("Kernel trigger group error counter (fd: %d)", local_fd);
+
+       kernel_error_accountant.kernel_trigger_error_counter_fd = local_fd;
+
+error:
+       return;
+}
+
+static
+enum trigger_error_accounting_status create_error_counter_index_for_token(
+               uint64_t tracer_token, uint64_t *error_counter_index)
+{
+       struct index_ht_entry *index_entry;;
+       enum lttng_index_allocator_status index_alloc_status;
+       uint64_t local_error_counter_index;
+       enum trigger_error_accounting_status status;
+
+       /* Allocate a new index for that counter. */
+       index_alloc_status = lttng_index_allocator_alloc(index_allocator,
+                       &local_error_counter_index);
+       switch (index_alloc_status) {
+       case LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY:
+               ERR("No more index available in the configured trigger error counter: number-of-indices=%"PRIu64,
+                               lttng_index_allocator_get_index_count(
+                                       index_allocator));
+               status = TRIGGER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE;
+               goto end;
+       case LTTNG_INDEX_ALLOCATOR_STATUS_OK:
+               break;
+       default:
+               status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR;
+               goto end;
+       }
+
+       index_entry = zmalloc(sizeof(*index_entry));
+       if (index_entry == NULL) {
+               PERROR("Trigger error counter hashtable entry zmalloc");
+               status = TRIGGER_ERROR_ACCOUNTING_STATUS_NOMEM;
+               goto end;
+       }
+
+       index_entry->error_counter_index = local_error_counter_index;
+       lttng_ht_node_init_u64(&index_entry->node, tracer_token);
+
+       lttng_ht_add_unique_u64(error_counter_indexes_ht, &index_entry->node);
+
+       *error_counter_index = local_error_counter_index;
+       status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+end:
+       return status;
+}
+
+enum trigger_error_accounting_status trigger_error_accounting_register_trigger(
+               const struct lttng_trigger *trigger,
+               uint64_t *error_counter_index)
+{
+       enum trigger_error_accounting_status status;
+       uint64_t local_error_counter_index;
+
+       /* Check if this trigger already has a error counter index assigned. */
+       status = get_error_counter_index_for_token(
+                       lttng_trigger_get_tracer_token(trigger),
+                       &local_error_counter_index);
+       switch (status) {
+       case TRIGGER_ERROR_ACCOUNTING_STATUS_NOT_FOUND:
+               DBG("Trigger error counter index for this tracer token not found. Allocating a new one.");
+               status = create_error_counter_index_for_token(
+                               lttng_trigger_get_tracer_token(trigger),
+                               &local_error_counter_index);
+               if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+                       goto end;
+               }
+       case TRIGGER_ERROR_ACCOUNTING_STATUS_OK:
+               *error_counter_index = local_error_counter_index;
+               status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+               break;
+       default:
+               break;
+       }
+
+end:
+       return status;
+}
+
+static
+enum trigger_error_accounting_status trigger_error_accounting_kernel_get_count(
+               const struct lttng_trigger *trigger, uint64_t *count)
+{
+       struct lttng_kernel_counter_value counter_value;
+       enum trigger_error_accounting_status status;
+       uint64_t error_counter_index;
+       int ret;
+
+       status = get_error_counter_index_for_token(
+                       lttng_trigger_get_tracer_token(trigger), &error_counter_index);
+       if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+               goto end;
+       }
+
+       counter_value.number_dimensions = 1;
+       counter_value.dimension_indexes[0] = error_counter_index;
+
+       assert(kernel_error_accountant.kernel_trigger_error_counter_fd);
+
+       ret = kernctl_counter_get_value(
+                       kernel_error_accountant.kernel_trigger_error_counter_fd,
+                       &counter_value);
+       if (ret) {
+               ERR("Error getting trigger error count.");
+               status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR;
+               goto end;
+       }
+
+       if (counter_value.value < 0) {
+               ERR("Trigger error counter less than zero.");
+               status = TRIGGER_ERROR_ACCOUNTING_STATUS_ERR;
+               goto end;
+       }
+
+       /* Error count can't be negative. */
+       assert(counter_value.value >= 0);
+       *count = (uint64_t) counter_value.value;
+
+       status = TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum trigger_error_accounting_status trigger_error_accounting_get_count(
+               const struct lttng_trigger *trigger, uint64_t *count)
+{
+       switch (lttng_trigger_get_underlying_domain_type_restriction(trigger)) {
+       case LTTNG_DOMAIN_KERNEL:
+               return trigger_error_accounting_kernel_get_count(trigger, count);
+#ifdef HAVE_LIBLTTNG_UST_CTL
+       case LTTNG_DOMAIN_UST:
+               return trigger_error_accounting_ust_get_count(trigger, count);
+#endif /* HAVE_LIBLTTNG_UST_CTL */
+       default:
+               abort();
+       }
+}
+
+static
+enum trigger_error_accounting_status trigger_error_accounting_clear(
+               const struct lttng_trigger *trigger)
+{
+       switch (lttng_trigger_get_underlying_domain_type_restriction(trigger)) {
+       case LTTNG_DOMAIN_KERNEL:
+               // FIXME: Should we clear it here? Right now I believe it's clear when
+               // we create a new trigger in the kernel.
+               return TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+#ifdef HAVE_LIBLTTNG_UST_CTL
+       case LTTNG_DOMAIN_UST:
+               return trigger_error_accounting_ust_clear(trigger);
+#endif /* HAVE_LIBLTTNG_UST_CTL */
+       default:
+               abort();
+       }
+}
+
+void trigger_error_accounting_unregister_trigger(
+               const struct lttng_trigger *trigger)
+{
+       struct lttng_ht_iter iter;
+       struct lttng_ht_node_u64 *node;
+       struct index_ht_entry *index_entry;
+       enum trigger_error_accounting_status status;
+       enum lttng_index_allocator_status index_alloc_status;
+       uint64_t tracer_token = lttng_trigger_get_tracer_token(trigger);
+
+
+       status = trigger_error_accounting_clear(trigger);
+       if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+               ERR("Error clearing trigger error counter index");
+       }
+
+       lttng_ht_lookup(error_counter_indexes_ht, &tracer_token, &iter);
+       node = lttng_ht_iter_get_node_u64(&iter);
+       if(node) {
+               index_entry = caa_container_of(node, struct index_ht_entry, node);
+               index_alloc_status = lttng_index_allocator_release(
+                               index_allocator,
+                               index_entry->error_counter_index);
+               if (index_alloc_status != LTTNG_INDEX_ALLOCATOR_STATUS_OK) {
+                       ERR("Error releasing trigger error counter index");
+               }
+
+               lttng_ht_del(error_counter_indexes_ht, &iter);
+               free(index_entry);
+       }
+}
+
+static void free_error_account_entry(struct rcu_head *head)
+{
+       struct error_account_entry *entry = caa_container_of(head,
+                       struct error_account_entry, rcu_head);
+#ifdef HAVE_LIBLTTNG_UST_CTL
+       ustctl_destroy_counter(entry->daemon_counter);
+#endif /* HAVE_LIBLTTNG_UST_CTL */
+
+       free(entry);
+}
+
+static void free_index_ht_entry(struct rcu_head *head)
+{
+       struct index_ht_entry *entry = caa_container_of(head,
+                       struct index_ht_entry, rcu_head);
+       free(entry);
+}
+
+void trigger_error_accounting_fini(void)
+{
+       struct lttng_ht_iter iter;
+       struct index_ht_entry *index_entry;
+       struct error_account_entry *uid_entry;
+
+       if (kernel_error_accountant.kernel_trigger_error_counter_fd) {
+               int ret = close(kernel_error_accountant.kernel_trigger_error_counter_fd);
+               if (ret) {
+                       PERROR("Closing kernel trigger error counter");
+               }
+       }
+
+       rcu_read_lock();
+
+       cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter,
+                       uid_entry, node.node) {
+               cds_lfht_del(error_counter_uid_ht->ht, &uid_entry->node.node);
+               call_rcu(&uid_entry->rcu_head, free_error_account_entry);
+       }
+
+       cds_lfht_for_each_entry(error_counter_indexes_ht->ht, &iter.iter,
+                       index_entry, node.node) {
+               cds_lfht_del(error_counter_indexes_ht->ht, &index_entry->node.node);
+               call_rcu(&index_entry->rcu_head, free_index_ht_entry);
+       }
+
+       rcu_read_unlock();
+
+       lttng_ht_destroy(error_counter_uid_ht);
+       lttng_ht_destroy(error_counter_indexes_ht);
+}
diff --git a/src/bin/lttng-sessiond/trigger-error-accounting.h b/src/bin/lttng-sessiond/trigger-error-accounting.h
new file mode 100644 (file)
index 0000000..442ddf2
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef _TRIGGER_ERROR_ACCOUNTING_H
+#define _TRIGGER_ERROR_ACCOUNTING_H
+
+#include <stdint.h>
+
+#include "ust-app.h"
+
+enum trigger_error_accounting_status {
+       TRIGGER_ERROR_ACCOUNTING_STATUS_OK,
+       TRIGGER_ERROR_ACCOUNTING_STATUS_ERR,
+       TRIGGER_ERROR_ACCOUNTING_STATUS_NOT_FOUND,
+       TRIGGER_ERROR_ACCOUNTING_STATUS_NOMEM,
+       TRIGGER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE,
+       TRIGGER_ERROR_ACCOUNTING_STATUS_APP_DEAD,
+};
+
+void trigger_error_accounting_init(uint64_t nb_bucket);
+
+#ifdef HAVE_LIBLTTNG_UST_CTL
+enum trigger_error_accounting_status trigger_error_accounting_register_app(
+               struct ust_app *app);
+#else /* HAVE_LIBLTTNG_UST_CTL */
+static inline
+enum trigger_error_accounting_status trigger_error_accounting_register_app(
+               struct ust_app *app)
+{
+       return TRIGGER_ERROR_ACCOUNTING_STATUS_OK;
+}
+#endif /* HAVE_LIBLTTNG_UST_CTL */
+
+void trigger_error_accounting_register_kernel(int kernel_fd);
+
+enum trigger_error_accounting_status trigger_error_accounting_register_trigger(
+               const struct lttng_trigger *trigger,
+               uint64_t *error_counter_index);
+
+enum trigger_error_accounting_status trigger_error_accounting_get_count(
+               const struct lttng_trigger *trigger, uint64_t *count);
+
+void trigger_error_accounting_unregister_trigger(
+               const struct lttng_trigger *trigger);
+
+void trigger_error_accounting_fini(void);
+
+#endif /* _TRIGGER_ERROR_ACCOUNTING_H */
index 14f214f67b3a9476beecd8e0f600f6250e120d94..8f388e0832e0feb6f993d1686a7009480c19ca11 100644 (file)
@@ -87,6 +87,31 @@ struct lttng_ust_stream {
         */
 } LTTNG_PACKED;
 
         */
 } 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;
+       uint64_t error_counter_idx;
+       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;
+       uint16_t capture_buf_size;
+       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 {
 #define LTTNG_UST_EVENT_PADDING1       16
 #define LTTNG_UST_EVENT_PADDING2       (LTTNG_UST_SYM_NAME_LEN + 32)
 struct lttng_ust_event {
@@ -251,6 +276,16 @@ struct lttng_ust_filter_bytecode {
        char data[0];
 } LTTNG_PACKED;
 
        char data[0];
 } LTTNG_PACKED;
 
+#define CAPTURE_BYTECODE_MAX_LEN       65536
+#define LTTNG_UST_CAPTURE_PADDING      32
+struct lttng_ust_capture_bytecode {
+       uint32_t len;
+       uint32_t reloc_offset;
+       uint64_t seqnum;
+       char padding[LTTNG_UST_CAPTURE_PADDING];
+       char data[0];
+} LTTNG_PACKED;
+
 #define LTTNG_UST_EXCLUSION_PADDING    32
 struct lttng_ust_event_exclusion {
        uint32_t count;
 #define LTTNG_UST_EXCLUSION_PADDING    32
 struct lttng_ust_event_exclusion {
        uint32_t count;
@@ -306,6 +341,9 @@ struct lttng_ust_event_exclusion {
 #define LTTNG_UST_FILTER                       _UST_CMD(0xA0)
 #define LTTNG_UST_EXCLUSION                    _UST_CMD(0xA1)
 
 #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;
 #define LTTNG_UST_ROOT_HANDLE  0
 
 struct lttng_ust_obj;
index c332f5c75b1df8bb698520b2d14bc67b7f7fe618..a3601c42455ca3070fec1aca067884dfbd24b746 100644 (file)
@@ -8,21 +8,33 @@
 
 #define _LGPL_SOURCE
 #include <errno.h>
 
 #define _LGPL_SOURCE
 #include <errno.h>
+#include <fcntl.h>
 #include <inttypes.h>
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <inttypes.h>
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <urcu/compiler.h>
 #include <signal.h>
 
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <urcu/compiler.h>
 #include <signal.h>
 
+#include <common/bytecode/bytecode.h>
 #include <common/common.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 <lttng/trigger/trigger-internal.h>
 #include <common/sessiond-comm/sessiond-comm.h>
 
 #include "buffer-registry.h"
 #include <common/sessiond-comm/sessiond-comm.h>
 
 #include "buffer-registry.h"
+#include "condition-internal.h"
 #include "fd-limit.h"
 #include "health-sessiond.h"
 #include "ust-app.h"
 #include "fd-limit.h"
 #include "health-sessiond.h"
 #include "ust-app.h"
@@ -34,6 +46,9 @@
 #include "lttng-sessiond.h"
 #include "notification-thread-commands.h"
 #include "rotate.h"
 #include "lttng-sessiond.h"
 #include "notification-thread-commands.h"
 #include "rotate.h"
+#include "event.h"
+#include "trigger-error-accounting.h"
+
 
 struct lttng_ht *ust_app_ht;
 struct lttng_ht *ust_app_ht_by_sock;
 
 struct lttng_ht *ust_app_ht;
 struct lttng_ht *ust_app_ht_by_sock;
@@ -245,7 +260,7 @@ static struct ust_registry_session *get_session_registry(
        {
                struct buffer_reg_uid *reg_uid = buffer_reg_uid_find(
                                ua_sess->tracing_id, ua_sess->bits_per_long,
        {
                struct buffer_reg_uid *reg_uid = buffer_reg_uid_find(
                                ua_sess->tracing_id, ua_sess->bits_per_long,
-                               ua_sess->real_credentials.uid);
+                               lttng_credentials_get_uid(&ua_sess->real_credentials));
                if (!reg_uid) {
                        goto error;
                }
                if (!reg_uid) {
                        goto error;
                }
@@ -313,6 +328,34 @@ void delete_ust_app_event(int sock, struct ust_app_event *ua_event,
        free(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_trigger_put(ua_token->trigger);
+       free(ua_token);
+}
+
 /*
  * Release ust data object of the given stream.
  *
 /*
  * Release ust data object of the given stream.
  *
@@ -898,6 +941,8 @@ void delete_ust_app(struct ust_app *app)
 {
        int ret, sock;
        struct ust_app_session *ua_sess, *tmp_ua_sess;
 {
        int ret, sock;
        struct ust_app_session *ua_sess, *tmp_ua_sess;
+       struct lttng_ht_iter iter;
+       struct ust_app_token_event_rule *token;
 
        /*
         * The session list lock must be held during this function to guarantee
 
        /*
         * The session list lock must be held during this function to guarantee
@@ -917,9 +962,26 @@ void delete_ust_app(struct ust_app *app)
                rcu_read_unlock();
        }
 
                rcu_read_unlock();
        }
 
+       /* Wipe token associated with the app */
+       cds_lfht_for_each_entry(app->tokens_ht->ht, &iter.iter, token,
+                       node.node) {
+               ret = lttng_ht_del(app->tokens_ht, &iter);
+               assert(!ret);
+               delete_ust_app_token_event_rule(app->sock, token, app);
+       }
+
        ht_cleanup_push(app->sessions);
        ht_cleanup_push(app->ust_sessions_objd);
        ht_cleanup_push(app->ust_objd);
        ht_cleanup_push(app->sessions);
        ht_cleanup_push(app->ust_sessions_objd);
        ht_cleanup_push(app->ust_objd);
+       ht_cleanup_push(app->tokens_ht);
+
+       /* This can happen if trigger setup failed. e.g killed app */
+       if (app->token_communication.handle) {
+               ustctl_release_object(sock, app->token_communication.handle);
+               free(app->token_communication.handle);
+       }
+
+       lttng_pipe_destroy(app->token_communication.trigger_event_pipe);
 
        /*
         * Wait until we have deleted the application from the sock hash table
 
        /*
         * Wait until we have deleted the application from the sock hash table
@@ -1125,6 +1187,52 @@ error:
        return NULL;
 }
 
        return NULL;
 }
 
+/*
+ * Alloc new UST app token event rule.
+ */
+static struct ust_app_token_event_rule *alloc_ust_app_token_event_rule(
+               struct lttng_trigger *trigger)
+{
+       struct ust_app_token_event_rule *ua_token;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+
+       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;
+       }
+
+       /* Get reference of the trigger */
+       /* TODO should this be like lttng_event_rule_get with a returned bool? */
+       lttng_trigger_get(trigger);
+
+       ua_token->enabled = 1;
+       ua_token->token = lttng_trigger_get_tracer_token(trigger);
+       lttng_ht_node_init_u64(&ua_token->node, ua_token->token);
+
+       condition = lttng_trigger_get_condition(trigger);
+       assert(condition);
+       assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+       assert(LTTNG_CONDITION_STATUS_OK == lttng_condition_event_rule_get_rule_mutable(condition, &event_rule));
+       assert(event_rule);
+
+       ua_token->trigger = trigger;
+       ua_token->filter = lttng_event_rule_get_filter_bytecode(event_rule);
+       ua_token->exclusion = lttng_event_rule_generate_exclusions(event_rule);
+       ua_token->error_counter_index = lttng_trigger_get_error_counter_index(trigger);
+
+       /* TODO put capture here? or later*/
+
+       DBG3("UST app token event rule %" PRIu64 " allocated", ua_token->token);
+
+       return ua_token;
+
+error:
+       return NULL;
+}
+
 /*
  * Alloc new UST app context.
  */
 /*
  * Alloc new UST app context.
  */
@@ -1166,50 +1274,51 @@ error:
 }
 
 /*
 }
 
 /*
- * Allocate a filter and copy the given original filter.
+ * Create a liblttng-ust filter bytecode from given bytecode.
  *
  * Return allocated filter or NULL on error.
  */
  *
  * Return allocated filter or NULL on error.
  */
-static struct lttng_filter_bytecode *copy_filter_bytecode(
-               struct lttng_filter_bytecode *orig_f)
+static struct lttng_ust_filter_bytecode *
+create_ust_filter_bytecode_from_bytecode(const struct lttng_bytecode *orig_f)
 {
 {
-       struct lttng_filter_bytecode *filter = NULL;
+       struct lttng_ust_filter_bytecode *filter = NULL;
 
        /* Copy filter bytecode */
        filter = zmalloc(sizeof(*filter) + orig_f->len);
        if (!filter) {
 
        /* Copy filter bytecode */
        filter = zmalloc(sizeof(*filter) + orig_f->len);
        if (!filter) {
-               PERROR("zmalloc alloc filter bytecode");
+               PERROR("zmalloc alloc ust filter bytecode");
                goto error;
        }
 
                goto error;
        }
 
+       assert(sizeof(struct lttng_bytecode) ==
+                       sizeof(struct lttng_ust_filter_bytecode));
        memcpy(filter, orig_f, sizeof(*filter) + orig_f->len);
        memcpy(filter, orig_f, sizeof(*filter) + orig_f->len);
-
 error:
        return filter;
 }
 
 /*
 error:
        return filter;
 }
 
 /*
- * Create a liblttng-ust filter bytecode from given bytecode.
+ * Create a liblttng-ust capture bytecode from given bytecode.
  *
  * Return allocated filter or NULL on error.
  */
  *
  * Return allocated filter or NULL on error.
  */
-static struct lttng_ust_filter_bytecode *create_ust_bytecode_from_bytecode(
-               struct lttng_filter_bytecode *orig_f)
+static struct lttng_ust_capture_bytecode *
+create_ust_capture_bytecode_from_bytecode(const struct lttng_bytecode *orig_f)
 {
 {
-       struct lttng_ust_filter_bytecode *filter = NULL;
+       struct lttng_ust_capture_bytecode *capture = NULL;
 
 
-       /* Copy filter bytecode */
-       filter = zmalloc(sizeof(*filter) + orig_f->len);
-       if (!filter) {
-               PERROR("zmalloc alloc ust filter bytecode");
+       /* Copy capture bytecode */
+       capture = zmalloc(sizeof(*capture) + orig_f->len);
+       if (!capture) {
+               PERROR("zmalloc alloc ust capture bytecode");
                goto error;
        }
 
                goto error;
        }
 
-       assert(sizeof(struct lttng_filter_bytecode) ==
-                       sizeof(struct lttng_ust_filter_bytecode));
-       memcpy(filter, orig_f, sizeof(*filter) + orig_f->len);
+       assert(sizeof(struct lttng_bytecode) ==
+                       sizeof(struct lttng_ust_capture_bytecode));
+       memcpy(capture, orig_f, sizeof(*capture) + orig_f->len);
 error:
 error:
-       return filter;
+       return capture;
 }
 
 /*
 }
 
 /*
@@ -1264,7 +1373,7 @@ error:
  * Return an ust_app_event object or NULL on error.
  */
 static struct ust_app_event *find_ust_app_event(struct lttng_ht *ht,
  * Return an ust_app_event object or NULL on error.
  */
 static struct ust_app_event *find_ust_app_event(struct lttng_ht *ht,
-               const char *name, const struct lttng_filter_bytecode *filter,
+               const char *name, const struct lttng_bytecode *filter,
                int loglevel_value,
                const struct lttng_event_exclusion *exclusion)
 {
                int loglevel_value,
                const struct lttng_event_exclusion *exclusion)
 {
@@ -1297,6 +1406,32 @@ end:
        return event;
 }
 
        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.
  *
 /*
  * Create the channel context on the tracer.
  *
@@ -1343,33 +1478,79 @@ error:
 /*
  * Set the filter on the tracer.
  */
 /*
  * 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_bytecode *bytecode,
+               struct lttng_ust_object_data *ust_object)
 {
        int ret;
        struct lttng_ust_filter_bytecode *ust_bytecode = NULL;
 
        health_code_update();
 
 {
        int ret;
        struct lttng_ust_filter_bytecode *ust_bytecode = NULL;
 
        health_code_update();
 
-       if (!ua_event->filter) {
-               ret = 0;
+       ust_bytecode = create_ust_filter_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,
+                       ust_object);
+       pthread_mutex_unlock(&app->sock_lock);
+       if (ret < 0) {
+               if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+                       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
+                        * creation process. Don't report an error so the execution can
+                        * continue normally.
+                        */
+                       ret = 0;
+                       DBG3("UST app set filter. Application is dead.");
+               }
                goto error;
        }
 
                goto error;
        }
 
-       ust_bytecode = create_ust_bytecode_from_bytecode(ua_event->filter);
+       DBG2("UST filter set for object %p successfully", ust_object);
+
+error:
+       health_code_update();
+       free(ust_bytecode);
+       return ret;
+}
+
+/*
+ * Set a capture bytecode for the passed object.
+ * The seqnum enforce the ordering at runtime and on reception.
+ */
+static int set_ust_capture(struct ust_app *app,
+               const struct lttng_bytecode *bytecode,
+               unsigned int seqnum,
+               struct lttng_ust_object_data *ust_object)
+{
+       int ret;
+       struct lttng_ust_capture_bytecode *ust_bytecode = NULL;
+
+       health_code_update();
+
+       ust_bytecode = create_ust_capture_bytecode_from_bytecode(bytecode);
        if (!ust_bytecode) {
                ret = -LTTNG_ERR_NOMEM;
                goto error;
        }
        if (!ust_bytecode) {
                ret = -LTTNG_ERR_NOMEM;
                goto error;
        }
+
+       /* Set the seqnum */
+       ust_bytecode->seqnum = seqnum;
+
        pthread_mutex_lock(&app->sock_lock);
        pthread_mutex_lock(&app->sock_lock);
-       ret = ustctl_set_filter(app->sock, ust_bytecode,
-                       ua_event->obj);
+       ret = ustctl_set_capture(app->sock, ust_bytecode,
+                       ust_object);
        pthread_mutex_unlock(&app->sock_lock);
        if (ret < 0) {
                if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
        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 capture 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
                } else {
                        /*
                         * This is normal behavior, an application can die during the
@@ -1377,12 +1558,12 @@ int set_ust_event_filter(struct ust_app_event *ua_event,
                         * continue normally.
                         */
                        ret = 0;
                         * continue normally.
                         */
                        ret = 0;
-                       DBG3("UST app filter event failed. Application is dead.");
+                       DBG3("UST app set capture. Application is dead.");
                }
                goto error;
        }
 
                }
                goto error;
        }
 
-       DBG2("UST filter set successfully for event %s", ua_event->name);
+       DBG2("UST capture set for object %p successfully", ust_object);
 
 error:
        health_code_update();
 
 error:
        health_code_update();
@@ -1414,33 +1595,30 @@ end:
 /*
  * Set event exclusions on the tracer.
  */
 /*
  * 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;
 {
        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 = -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) {
        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
                } else {
                        /*
                         * This is normal behavior, an application can die during the
@@ -1448,37 +1626,36 @@ int set_ust_event_exclusion(struct ust_app_event *ua_event,
                         * continue normally.
                         */
                        ret = 0;
                         * continue normally.
                         */
                        ret = 0;
-                       DBG3("UST app event exclusion failed. Application is dead.");
+                       DBG3("UST app set exclusions failed. Application is dead.");
                }
                goto error;
        }
 
                }
                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();
 
 error:
        health_code_update();
-       free(ust_exclusion);
+       free(ust_exclusions);
        return ret;
 }
 
 /*
  * Disable the specified event on to UST tracer for the UST session.
  */
        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);
 {
        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) {
        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
                } else {
                        /*
                         * This is normal behavior, an application can die during the
@@ -1491,8 +1668,8 @@ static int disable_ust_event(struct ust_app *app,
                goto error;
        }
 
                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();
 
 error:
        health_code_update();
@@ -1580,21 +1757,19 @@ error:
 /*
  * Enable the specified event on to UST tracer for the UST session.
  */
 /*
  * 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);
 {
        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) {
        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
                } else {
                        /*
                         * This is normal behavior, an application can die during the
@@ -1602,13 +1777,13 @@ static int enable_ust_event(struct ust_app *app,
                         * continue normally.
                         */
                        ret = 0;
                         * continue normally.
                         */
                        ret = 0;
-                       DBG3("UST app enable event failed. Application is dead.");
+                       DBG3("UST app enable failed. Application is dead.");
                }
                goto error;
        }
 
                }
                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();
 
 error:
        health_code_update();
@@ -1704,14 +1879,14 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess,
 
        ua_event->handle = ua_event->obj->handle;
 
 
        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) {
 
        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;
                }
                if (ret < 0) {
                        goto error;
                }
@@ -1719,7 +1894,7 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess,
 
        /* Set exclusions for the event */
        if (ua_event->exclusion) {
 
        /* 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;
                }
                if (ret < 0) {
                        goto error;
                }
@@ -1731,7 +1906,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.
                 */
                 * 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
                if (ret < 0) {
                        /*
                         * If we hit an EPERM, something is wrong with our enable call. If
@@ -1758,6 +1933,188 @@ error:
        return ret;
 }
 
        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_log_level_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_log_level(
+                                       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;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+       unsigned int capture_bytecode_count = 0;
+
+       health_code_update();
+       assert(app->token_communication.handle);
+
+       condition = lttng_trigger_get_condition(ua_token->trigger);
+       assert(condition);
+       assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+       lttng_condition_event_rule_get_rule_mutable(condition, &event_rule);
+       assert(event_rule);
+       assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT);
+       /* Should we also test for UST at this point, or do we trust all the
+        * upper level? */
+
+       init_ust_trigger_from_event_rule(event_rule, &trigger);
+
+       trigger.id = ua_token->token;
+       trigger.error_counter_index = ua_token->error_counter_index;
+
+       /* 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;
+               }
+       }
+
+       /* Set the capture bytecode
+        * TODO: do we want to emulate what is done with exclusion and provide
+        * and object with a count of capture bytecode? instead of multiple
+        * call?
+        * */
+       capture_bytecode_count = lttng_trigger_get_capture_bytecode_count(ua_token->trigger);
+       for (unsigned int i = 0; i < capture_bytecode_count; i++) {
+               const struct lttng_bytecode *capture_bytecode = lttng_trigger_get_capture_bytecode_at_index(ua_token->trigger, i);
+               ret = set_ust_capture(app, capture_bytecode, i, 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.
  */
 /*
  * Copy data between an UST app event and a LTT event.
  */
@@ -1776,7 +2133,7 @@ static void shadow_copy_event(struct ust_app_event *ua_event,
 
        /* Copy filter bytecode */
        if (uevent->filter) {
 
        /* Copy filter bytecode */
        if (uevent->filter) {
-               ua_event->filter = copy_filter_bytecode(uevent->filter);
+               ua_event->filter = bytecode_copy(uevent->filter);
                /* Filter might be NULL here in case of ENONEM. */
        }
 
                /* Filter might be NULL here in case of ENONEM. */
        }
 
@@ -1847,10 +2204,10 @@ static void shadow_copy_session(struct ust_app_session *ua_sess,
 
        ua_sess->tracing_id = usess->id;
        ua_sess->id = get_next_session_id();
 
        ua_sess->tracing_id = usess->id;
        ua_sess->id = get_next_session_id();
-       ua_sess->real_credentials.uid = app->uid;
-       ua_sess->real_credentials.gid = app->gid;
-       ua_sess->effective_credentials.uid = usess->uid;
-       ua_sess->effective_credentials.gid = usess->gid;
+       LTTNG_OPTIONAL_SET(&ua_sess->real_credentials.uid, app->uid);
+       LTTNG_OPTIONAL_SET(&ua_sess->real_credentials.gid, app->gid);
+       LTTNG_OPTIONAL_SET(&ua_sess->effective_credentials.uid, usess->uid);
+       LTTNG_OPTIONAL_SET(&ua_sess->effective_credentials.gid, usess->gid);
        ua_sess->buffer_type = usess->buffer_type;
        ua_sess->bits_per_long = app->bits_per_long;
 
        ua_sess->buffer_type = usess->buffer_type;
        ua_sess->bits_per_long = app->bits_per_long;
 
@@ -1872,7 +2229,7 @@ static void shadow_copy_session(struct ust_app_session *ua_sess,
        case LTTNG_BUFFER_PER_UID:
                ret = snprintf(ua_sess->path, sizeof(ua_sess->path),
                                DEFAULT_UST_TRACE_UID_PATH,
        case LTTNG_BUFFER_PER_UID:
                ret = snprintf(ua_sess->path, sizeof(ua_sess->path),
                                DEFAULT_UST_TRACE_UID_PATH,
-                               ua_sess->real_credentials.uid,
+                               lttng_credentials_get_uid(&ua_sess->real_credentials),
                                app->bits_per_long);
                break;
        default:
                                app->bits_per_long);
                break;
        default:
@@ -1995,8 +2352,9 @@ static int setup_buffer_reg_pid(struct ust_app_session *ua_sess,
                        app->uint64_t_alignment, app->long_alignment,
                        app->byte_order, app->version.major, app->version.minor,
                        reg_pid->root_shm_path, reg_pid->shm_path,
                        app->uint64_t_alignment, app->long_alignment,
                        app->byte_order, app->version.major, app->version.minor,
                        reg_pid->root_shm_path, reg_pid->shm_path,
-                       ua_sess->effective_credentials.uid,
-                       ua_sess->effective_credentials.gid, ua_sess->tracing_id,
+                       lttng_credentials_get_uid(&ua_sess->effective_credentials),
+                       lttng_credentials_get_gid(&ua_sess->effective_credentials),
+                       ua_sess->tracing_id,
                        app->uid);
        if (ret < 0) {
                /*
                        app->uid);
        if (ret < 0) {
                /*
@@ -2341,7 +2699,7 @@ int enable_ust_app_event(struct ust_app_session *ua_sess,
 {
        int ret;
 
 {
        int ret;
 
-       ret = enable_ust_event(app, ua_sess, ua_event);
+       ret = enable_ust_object(app, ua_event->obj);
        if (ret < 0) {
                goto error;
        }
        if (ret < 0) {
                goto error;
        }
@@ -2360,7 +2718,7 @@ static int disable_ust_app_event(struct ust_app_session *ua_sess,
 {
        int ret;
 
 {
        int ret;
 
-       ret = disable_ust_event(app, ua_sess, ua_event);
+       ret = disable_ust_object(app, ua_event->obj);
        if (ret < 0) {
                goto error;
        }
        if (ret < 0) {
                goto error;
        }
@@ -2887,8 +3245,9 @@ static int create_channel_per_uid(struct ust_app *app,
 
        notification_ret = notification_thread_command_add_channel(
                        notification_thread_handle, session->name,
 
        notification_ret = notification_thread_command_add_channel(
                        notification_thread_handle, session->name,
-                       ua_sess->effective_credentials.uid,
-                       ua_sess->effective_credentials.gid, ua_chan->name,
+                       lttng_credentials_get_uid(&ua_sess->effective_credentials),
+                       lttng_credentials_get_gid(&ua_sess->effective_credentials),
+                       ua_chan->name,
                        ua_chan->key, LTTNG_DOMAIN_UST,
                        ua_chan->attr.subbuf_size * ua_chan->attr.num_subbuf);
        if (notification_ret != LTTNG_OK) {
                        ua_chan->key, LTTNG_DOMAIN_UST,
                        ua_chan->attr.subbuf_size * ua_chan->attr.num_subbuf);
        if (notification_ret != LTTNG_OK) {
@@ -2987,8 +3346,9 @@ static int create_channel_per_pid(struct ust_app *app,
 
        cmd_ret = notification_thread_command_add_channel(
                        notification_thread_handle, session->name,
 
        cmd_ret = notification_thread_command_add_channel(
                        notification_thread_handle, session->name,
-                       ua_sess->effective_credentials.uid,
-                       ua_sess->effective_credentials.gid, ua_chan->name,
+                       lttng_credentials_get_uid(&ua_sess->effective_credentials),
+                       lttng_credentials_get_gid(&ua_sess->effective_credentials),
+                       ua_chan->name,
                        ua_chan->key, LTTNG_DOMAIN_UST,
                        ua_chan->attr.subbuf_size * ua_chan->attr.num_subbuf);
        if (cmd_ret != LTTNG_OK) {
                        ua_chan->key, LTTNG_DOMAIN_UST,
                        ua_chan->attr.subbuf_size * ua_chan->attr.num_subbuf);
        if (cmd_ret != LTTNG_OK) {
@@ -3175,6 +3535,57 @@ error:
        return ret;
 }
 
        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_trigger *trigger,
+               struct ust_app *app)
+{
+       int ret = 0;
+       struct ust_app_token_event_rule *ua_token;
+
+       ua_token = alloc_ust_app_token_event_rule(trigger);
+       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",
+                                       lttng_trigger_get_tracer_token(trigger),
+                                       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", lttng_trigger_get_tracer_token(trigger),
+                       app->pid);
+
+       goto end;
+
+error:
+       /* Valid. Calling here is already in a read side lock */
+       delete_ust_app_token_event_rule(-1, ua_token, app);
+end:
+       return ret;
+}
+
 /*
  * Create UST metadata and open it on the tracer side.
  *
 /*
  * Create UST metadata and open it on the tracer side.
  *
@@ -3319,6 +3730,7 @@ error:
 struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock)
 {
        struct ust_app *lta = NULL;
 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);
 
        assert(msg);
        assert(sock >= 0);
@@ -3335,12 +3747,20 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock)
                goto error;
        }
 
                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 = 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;
        lta->ppid = msg->ppid;
        lta->uid = msg->uid;
        lta->gid = msg->gid;
@@ -3359,6 +3779,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->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));
 
        /* Copy name and make sure it's NULL terminated. */
        strncpy(lta->name, msg->name, sizeof(lta->name));
@@ -3445,6 +3866,61 @@ int ust_app_version(struct ust_app *app)
        return ret;
 }
 
        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;
+       enum trigger_error_accounting_status trigger_error_accounting_status;
+
+       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) {
+               if (ret != -LTTNG_UST_ERR_EXITING && ret != -EPIPE) {
+                       ERR("UST app %d create_trigger_group failed with ret %d, trigger pipe %d", app->sock, ret, writefd);
+               } else {
+                       DBG("UST app %d create trigger group failed. Application is dead", app->sock);
+               }
+               goto end;
+       }
+
+       lttng_ret = notification_thread_command_add_application(
+                       notification_thread_handle, lttng_pipe_get_readfd(app->token_communication.trigger_event_pipe), LTTNG_DOMAIN_UST);
+       if (lttng_ret != LTTNG_OK) {
+               /* TODO: error */
+               ret = - 1;
+               ERR("Failed to add channel to notification thread");
+               goto end;
+       }
+
+       /* Assign handle only when the complete setup is valid */
+       app->token_communication.handle = group;
+
+       trigger_error_accounting_status = trigger_error_accounting_register_app(app);
+       if (trigger_error_accounting_status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+               ERR("Failed to setup trigger error accouting for app");
+               ret = -1;
+               goto end;
+       }
+
+
+end:
+       return ret;
+}
+
 /*
  * Unregister app by removing it from the global traceable app list and freeing
  * the data struct.
 /*
  * Unregister app by removing it from the global traceable app list and freeing
  * the data struct.
@@ -3453,6 +3929,7 @@ int ust_app_version(struct ust_app *app)
  */
 void ust_app_unregister(int sock)
 {
  */
 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;
        struct ust_app *lta;
        struct lttng_ht_node_ulong *node;
        struct lttng_ht_iter ust_app_sock_iter;
@@ -3558,6 +4035,19 @@ void ust_app_unregister(int sock)
                                lta->pid);
        }
 
                                lta->pid);
        }
 
+       /* trigger handle can be null in certain scenario such as a dead app */
+       if (lta->token_communication.handle) {
+               int fd = lttng_pipe_get_readfd(
+                               lta->token_communication.trigger_event_pipe);
+
+               ret_code = notification_thread_command_remove_application(
+                               notification_thread_handle,
+                               fd);
+               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);
 
        /* Free memory */
        call_rcu(&lta->pid_n.head, delete_ust_app_rcu);
 
@@ -4999,6 +5489,124 @@ end:
        return ret;
 }
 
        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_tracer_token(trigger);
+               condition = lttng_trigger_get_condition(trigger);
+               (void) lttng_condition_event_rule_get_rule_mutable(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(trigger, app);
+                       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_tracer_token(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:
+       lttng_triggers_destroy(triggers);
+       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 process attribute trackers.
 /*
  * The caller must ensure that the application is compatible and is tracked
  * by the process attribute trackers.
@@ -5140,6 +5748,21 @@ 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;
+       }
+       if (app->token_communication.handle == NULL) {
+               WARN("UST app global update token for app sock %d skipped since communcation handle is null", app->sock);
+               return;
+       }
+
+       ust_app_synchronize_tokens(app);
+}
+
 /*
  * Called with session lock held.
  */
 /*
  * Called with session lock held.
  */
@@ -5155,6 +5778,31 @@ void ust_app_global_update_all(struct ltt_ust_session *usess)
        rcu_read_unlock();
 }
 
        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();
+}
+
+void ust_app_update_trigger_error_count(struct lttng_trigger *trigger)
+{
+       uint64_t error_count = 0;
+       enum trigger_error_accounting_status status;
+
+       status = trigger_error_accounting_get_count(trigger, &error_count);
+       if (status != TRIGGER_ERROR_ACCOUNTING_STATUS_OK) {
+               ERR("Error getting trigger error count");
+       }
+
+       lttng_trigger_set_error_count(trigger, error_count);
+}
+
 /*
  * Add context to a specific channel for global UST domain.
  */
 /*
  * Add context to a specific channel for global UST domain.
  */
@@ -5965,10 +6613,8 @@ enum lttng_error_code ust_app_snapshot_record(
                                        ua_chan, node.node) {
                                status = consumer_snapshot_channel(socket,
                                                ua_chan->key, output, 0,
                                        ua_chan, node.node) {
                                status = consumer_snapshot_channel(socket,
                                                ua_chan->key, output, 0,
-                                               ua_sess->effective_credentials
-                                                               .uid,
-                                               ua_sess->effective_credentials
-                                                               .gid,
+                                               lttng_credentials_get_uid(&ua_sess->effective_credentials),
+                                               lttng_credentials_get_gid(&ua_sess->effective_credentials),
                                                &trace_path[consumer_path_offset], wait,
                                                nb_packets_per_stream);
                                switch (status) {
                                                &trace_path[consumer_path_offset], wait,
                                                nb_packets_per_stream);
                                switch (status) {
@@ -5988,8 +6634,8 @@ enum lttng_error_code ust_app_snapshot_record(
                        }
                        status = consumer_snapshot_channel(socket,
                                        registry->metadata_key, output, 1,
                        }
                        status = consumer_snapshot_channel(socket,
                                        registry->metadata_key, output, 1,
-                                       ua_sess->effective_credentials.uid,
-                                       ua_sess->effective_credentials.gid,
+                                       lttng_credentials_get_uid(&ua_sess->effective_credentials),
+                                       lttng_credentials_get_gid(&ua_sess->effective_credentials),
                                        &trace_path[consumer_path_offset], wait, 0);
                        switch (status) {
                        case LTTNG_OK:
                                        &trace_path[consumer_path_offset], wait, 0);
                        switch (status) {
                        case LTTNG_OK:
@@ -6345,10 +6991,8 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session)
                                        ua_chan, node.node) {
                                ret = consumer_rotate_channel(socket,
                                                ua_chan->key,
                                        ua_chan, node.node) {
                                ret = consumer_rotate_channel(socket,
                                                ua_chan->key,
-                                               ua_sess->effective_credentials
-                                                               .uid,
-                                               ua_sess->effective_credentials
-                                                               .gid,
+                                               lttng_credentials_get_uid(&ua_sess->effective_credentials),
+                                               lttng_credentials_get_gid(&ua_sess->effective_credentials),
                                                ua_sess->consumer,
                                                /* is_metadata_channel */ false);
                                if (ret < 0) {
                                                ua_sess->consumer,
                                                /* is_metadata_channel */ false);
                                if (ret < 0) {
@@ -6364,8 +7008,8 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session)
                        (void) push_metadata(registry, usess->consumer);
                        ret = consumer_rotate_channel(socket,
                                        registry->metadata_key,
                        (void) push_metadata(registry, usess->consumer);
                        ret = consumer_rotate_channel(socket,
                                        registry->metadata_key,
-                                       ua_sess->effective_credentials.uid,
-                                       ua_sess->effective_credentials.gid,
+                                       lttng_credentials_get_uid(&ua_sess->effective_credentials),
+                                       lttng_credentials_get_gid(&ua_sess->effective_credentials),
                                        ua_sess->consumer,
                                        /* is_metadata_channel */ true);
                        if (ret < 0) {
                                        ua_sess->consumer,
                                        /* is_metadata_channel */ true);
                        if (ret < 0) {
index 164bc2c6f2c95acf59d91e2a339cd55f3b338fef..3b65a67e42121a1dbe1874a140222179dcb0b476 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <stdint.h>
 
 
 #include <stdint.h>
 
+#include <common/index-allocator.h>
 #include <common/uuid.h>
 
 #include "trace-ust.h"
 #include <common/uuid.h>
 
 #include "trace-ust.h"
@@ -22,7 +23,7 @@
 /* Process name (short). */
 #define UST_APP_PROCNAME_LEN   16
 
 /* Process name (short). */
 #define UST_APP_PROCNAME_LEN   16
 
-struct lttng_filter_bytecode;
+struct lttng_bytecode;
 struct lttng_ust_filter_bytecode;
 
 extern int ust_consumerd64_fd, ust_consumerd32_fd;
 struct lttng_ust_filter_bytecode;
 
 extern int ust_consumerd64_fd, ust_consumerd32_fd;
@@ -39,7 +40,7 @@ struct ust_app_notify_sock_obj {
 
 struct ust_app_ht_key {
        const char *name;
 
 struct ust_app_ht_key {
        const char *name;
-       const struct lttng_filter_bytecode *filter;
+       const struct lttng_bytecode *filter;
        enum lttng_ust_loglevel_type loglevel_type;
        const struct lttng_event_exclusion *exclusion;
 };
        enum lttng_ust_loglevel_type loglevel_type;
        const struct lttng_event_exclusion *exclusion;
 };
@@ -108,7 +109,21 @@ struct ust_app_event {
        struct lttng_ust_event attr;
        char name[LTTNG_UST_SYM_NAME_LEN];
        struct lttng_ht_node_str node;
        struct lttng_ust_event attr;
        char name[LTTNG_UST_SYM_NAME_LEN];
        struct lttng_ht_node_str node;
-       struct lttng_filter_bytecode *filter;
+       struct lttng_bytecode *filter;
+       struct lttng_event_exclusion *exclusion;
+};
+
+struct ust_app_token_event_rule {
+       int enabled;
+       uint64_t error_counter_index;
+       int handle;
+       struct lttng_ust_object_data *obj;
+       struct lttng_trigger *trigger;
+       uint64_t token;
+       struct lttng_ht_node_u64 node;
+       /* The event_rule object own this pointer */
+       const struct lttng_bytecode *filter;
+       /* The event_rule object own this pointer */
        struct lttng_event_exclusion *exclusion;
 };
 
        struct lttng_event_exclusion *exclusion;
 };
 
@@ -292,6 +307,14 @@ struct ust_app {
         * Used for path creation
         */
        time_t registration_time;
         * 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
 };
 
 #ifdef HAVE_LIBLTTNG_UST_CTL
@@ -319,6 +342,10 @@ 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);
                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_update_trigger_error_count(struct lttng_trigger *trigger);
 
 void ust_app_clean_list(void);
 int ust_app_ht_alloc(void);
 
 void ust_app_clean_list(void);
 int ust_app_ht_alloc(void);
@@ -356,6 +383,8 @@ int ust_app_release_object(struct ust_app *app,
 enum lttng_error_code ust_app_clear_session(struct ltt_session *session);
 enum lttng_error_code ust_app_open_packets(struct ltt_session *session);
 
 enum lttng_error_code ust_app_clear_session(struct ltt_session *session);
 enum lttng_error_code ust_app_open_packets(struct ltt_session *session);
 
+int ust_app_setup_trigger_group(struct ust_app *app);
+
 static inline
 int ust_app_supported(void)
 {
 static inline
 int ust_app_supported(void)
 {
@@ -444,6 +473,17 @@ static inline
 void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app)
 {}
 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)
 {
 int ust_app_disable_channel_glb(struct ltt_ust_session *usess,
                struct ltt_ust_channel *uchan)
 {
@@ -531,7 +571,11 @@ unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess)
 {
        return 0;
 }
 {
        return 0;
 }
-
+static inline
+void ust_app_update_trigger_error_count(struct lttng_trigger *trigger)
+{
+       return;
+}
 static inline
 int ust_app_supported(void)
 {
 static inline
 int ust_app_supported(void)
 {
index 25dc7ed1c6da96c5fa8cfba14e95d78dab0e9ac1..70e3d165835974479ebad8e08e5460adaa1fbeb2 100644 (file)
@@ -151,7 +151,7 @@ static int ask_channel_creation(struct ust_app_session *ua_sess,
                        ua_chan->tracefile_count,
                        ua_sess->id,
                        ua_sess->output_traces,
                        ua_chan->tracefile_count,
                        ua_sess->id,
                        ua_sess->output_traces,
-                       ua_sess->real_credentials.uid,
+                       lttng_credentials_get_uid(&ua_sess->real_credentials),
                        ua_chan->attr.blocking_timeout,
                        root_shm_path, shm_path,
                        trace_chunk,
                        ua_chan->attr.blocking_timeout,
                        root_shm_path, shm_path,
                        trace_chunk,
index 7c245bdce266489e54b8258c4de06fbc343ae0a6..51aca37a9b526070a885eaec1cb4e95b332ca198 100644 (file)
@@ -79,12 +79,22 @@ int ustctl_set_filter(int sock, struct lttng_ust_filter_bytecode *bytecode,
                struct lttng_ust_object_data *obj_data);
 int ustctl_set_exclusion(int sock, struct lttng_ust_event_exclusion *exclusion,
                struct lttng_ust_object_data *obj_data);
                struct lttng_ust_object_data *obj_data);
 int ustctl_set_exclusion(int sock, struct lttng_ust_event_exclusion *exclusion,
                struct lttng_ust_object_data *obj_data);
+int ustctl_set_capture(int sock, struct lttng_ust_capture_bytecode *bytecode,
+               struct lttng_ust_object_data *obj_data);
 
 int ustctl_enable(int sock, struct lttng_ust_object_data *object);
 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_enable(int sock, struct lttng_ust_object_data *object);
 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.
 /*
  * ustctl_tracepoint_list returns a tracepoint list handle, or negative
  * error value.
@@ -533,4 +543,82 @@ int ustctl_reply_register_channel(int sock,
        enum ustctl_channel_header header_type,
        int ret_code);                  /* return code. 0 ok, negative error */
 
        enum ustctl_channel_header header_type,
        int ret_code);                  /* return code. 0 ok, negative error */
 
+/*
+ * Counter API.
+ */
+
+enum ustctl_counter_bitness {
+       USTCTL_COUNTER_BITNESS_32 = 4,
+       USTCTL_COUNTER_BITNESS_64 = 8,
+};
+
+enum ustctl_counter_arithmetic {
+       USTCTL_COUNTER_ARITHMETIC_MODULAR       = 0,
+       USTCTL_COUNTER_ARITHMETIC_SATURATION    = 1,
+};
+
+/* Used as alloc flags. */
+enum ustctl_counter_alloc {
+       USTCTL_COUNTER_ALLOC_PER_CPU = (1 << 0),
+       USTCTL_COUNTER_ALLOC_GLOBAL = (1 << 1),
+};
+
+struct ustctl_daemon_counter;
+
+int ustctl_get_nr_cpu_per_counter(void);
+
+struct ustctl_counter_dimension {
+       uint64_t size;
+       uint64_t underflow_index;
+       uint64_t overflow_index;
+       uint8_t has_underflow;
+       uint8_t has_overflow;
+};
+
+struct ustctl_daemon_counter *
+       ustctl_create_counter(size_t nr_dimensions,
+               const struct ustctl_counter_dimension *dimensions,
+               int64_t global_sum_step,
+               int global_counter_fd,
+               int nr_counter_cpu_fds,
+               const int *counter_cpu_fds,
+               enum ustctl_counter_bitness bitness,
+               enum ustctl_counter_arithmetic arithmetic,
+               uint32_t alloc_flags);
+
+int ustctl_create_counter_data(struct ustctl_daemon_counter *counter,
+               struct lttng_ust_object_data **counter_data);
+
+int ustctl_create_counter_global_data(struct ustctl_daemon_counter *counter,
+               struct lttng_ust_object_data **counter_global_data);
+int ustctl_create_counter_cpu_data(struct ustctl_daemon_counter *counter, int cpu,
+               struct lttng_ust_object_data **counter_cpu_data);
+
+/*
+ * Each counter data and counter cpu data created need to be destroyed
+ * before calling ustctl_destroy_counter().
+ */
+void ustctl_destroy_counter(struct ustctl_daemon_counter *counter);
+
+int ustctl_send_counter_data_to_ust(int sock, int parent_handle,
+               struct lttng_ust_object_data *counter_data);
+int ustctl_send_counter_global_data_to_ust(int sock,
+               struct lttng_ust_object_data *counter_data,
+               struct lttng_ust_object_data *counter_global_data);
+int ustctl_send_counter_cpu_data_to_ust(int sock,
+               struct lttng_ust_object_data *counter_data,
+               struct lttng_ust_object_data *counter_cpu_data);
+
+int ustctl_counter_read(struct ustctl_daemon_counter *counter,
+               const size_t *dimension_indexes,
+               int cpu, int64_t *value,
+               bool *overflow, bool *underflow);
+int ustctl_counter_aggregate(struct ustctl_daemon_counter *counter,
+               const size_t *dimension_indexes,
+               int64_t *value,
+               bool *overflow, bool *underflow);
+int ustctl_counter_clear(struct ustctl_daemon_counter *counter,
+               const size_t *dimension_indexes);
+
+
 #endif /* LTTNG_UST_CTL_INTERNAL_H */
 #endif /* LTTNG_UST_CTL_INTERNAL_H */
index a6bd7a9d738372bc5b4f86b5a24add072d24b7d4..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 \
                                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)
 
 
 lttng_CFLAGS = $(AM_CFLAGS) $(POPT_CFLAGS)
 
@@ -38,4 +42,5 @@ lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.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/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)
                        $(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(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[]);
 
 extern int cmd_help(int argc, const char **argv,
                const struct cmd_struct commands[]);
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..277a7e3
--- /dev/null
@@ -0,0 +1,2161 @@
+#include <stdio.h>
+#include <ctype.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-expr.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"
+#include "lttng/kernel-probe.h"
+#include "common/filter/filter-ast.h"
+#include "common/filter/filter-ir.h"
+#include "common/dynamic-array.h"
+
+
+#if (LTTNG_SYMBOL_NAME_LEN == 256)
+#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API    "255"
+#endif
+
+#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_USER_ID,
+
+       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,
+
+       OPT_CAPTURE,
+};
+
+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" },
+
+       /* Capture descriptor */
+       { OPT_CAPTURE, '\0', "capture", true },
+
+       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 {
+               ERR("Multiple domains specified.");
+               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 {
+               ERR("Multiple event type not supported.");
+               ret = false;
+       }
+
+       return ret;
+}
+
+static
+bool assign_string(char **dest, const char *src, const char *opt_name)
+{
+       bool ret;
+
+       if (*dest) {
+               ERR(
+                       "Duplicate %s given.", opt_name);
+               goto error;
+       }
+
+       *dest = strdup(src);
+       if (!*dest) {
+               ERR("Failed to allocate %s string.", 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 int parse_kernel_probe_opts(const char *source,
+               struct lttng_kernel_probe_location **location)
+{
+       int ret = 0;
+       int match;
+       char s_hex[19];
+       char name[LTTNG_SYMBOL_NAME_LEN];
+       char *symbol_name = NULL;
+       uint64_t offset;
+
+       /* Check for symbol+offset */
+       match = sscanf(source, "%255[^'+']+%18s", name, s_hex);
+       if (match == 2) {
+               if (*s_hex == '\0') {
+                       ERR("Kernel probe symbol offset is missing.");
+                       goto error;
+               }
+               symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN);
+               if (!symbol_name) {
+                       ERR("Strndup kernel probe location symbol name.");
+                       goto error;
+               }
+               offset = strtoul(s_hex, NULL, 0);
+
+               *location = lttng_kernel_probe_location_symbol_create(
+                               symbol_name, offset);
+               if (!location) {
+                       ERR("Symbol kernel probe location creation failed.");
+                       goto error;
+               }
+
+               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) {
+                       symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN);
+                       if (!symbol_name) {
+                               ERR("Strndup kernel probe location symbol name.");
+                               goto error;
+                       }
+
+                       *location = lttng_kernel_probe_location_symbol_create(
+                                       symbol_name, 0);
+                       if (!location) {
+                               ERR("Symbol kernel probe location creation failed.");
+                               goto error;
+                       }
+
+                       goto end;
+               }
+       }
+
+       /* Check for address */
+       match = sscanf(source, "%18s", s_hex);
+       if (match > 0) {
+               uint64_t address;
+               if (*s_hex == '\0') {
+                       ERR("Invalid kernel probe location address.");
+                       goto error;
+               }
+               address = strtoul(s_hex, NULL, 0);
+               *location = lttng_kernel_probe_location_address_create(address);
+               if (!location) {
+                       ERR("Symbol kernel probe location creation failed.");
+                       goto error;
+               }
+
+               goto end;
+       }
+
+error:
+       /* No match */
+       ret = -1;
+       *location = NULL;
+
+end:
+       free(symbol_name);
+       return ret;
+}
+
+static
+struct lttng_event_expr *ir_op_load_expr_to_event_expr(
+               struct ir_load_expression *load_exp, const char *capture_str)
+{
+       struct ir_load_expression_op *load_expr_op = load_exp->child;
+       struct lttng_event_expr *event_expr = NULL;
+       char *provider_name = NULL;
+
+       switch (load_expr_op->type) {
+       case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+       {
+               const char *field_name;
+
+               load_expr_op = load_expr_op->next;
+               assert(load_expr_op);
+               assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
+               field_name = load_expr_op->u.symbol;
+               assert(field_name);
+
+               event_expr = lttng_event_expr_event_payload_field_create(field_name);
+               if (!event_expr) {
+                       fprintf(stderr, "Failed to create payload field event expression.\n");
+                       goto error;
+               }
+
+               break;
+       }
+
+       case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+       {
+               const char *field_name;
+
+               load_expr_op = load_expr_op->next;
+               assert(load_expr_op);
+               assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
+               field_name = load_expr_op->u.symbol;
+               assert(field_name);
+
+               event_expr = lttng_event_expr_channel_context_field_create(field_name);
+               if (!event_expr) {
+                       fprintf(stderr, "Failed to create channel context field event expression.\n");
+                       goto error;
+               }
+
+               break;
+       }
+
+       case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+       {
+               const char *field_name;
+               const char *colon;
+               const char *type_name;
+
+               load_expr_op = load_expr_op->next;
+               assert(load_expr_op);
+               assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
+               field_name = load_expr_op->u.symbol;
+               assert(field_name);
+
+               /*
+                * The field name needs to be of the form PROVIDER:TYPE.  We
+                * split it here.
+                */
+               colon = strchr(field_name, ':');
+               if (!colon) {
+                       fprintf(stderr, "Invalid app-specific context field name: missing colon in `%s`.\n",
+                               field_name);
+                       goto error;
+               }
+
+               type_name = colon + 1;
+               if (*type_name == '\0') {
+                       fprintf(stderr,
+                               "Invalid app-specific context field name: missing type name after colon in `%s`.\n",
+                               field_name);
+                       goto error;
+               }
+
+               provider_name = strndup(field_name, colon - field_name);
+               if (!provider_name) {
+                       fprintf(stderr, "Failed to allocate string.\n");
+                       goto error;
+               }
+
+               event_expr = lttng_event_expr_app_specific_context_field_create(
+                       provider_name, type_name);
+               if (!event_expr) {
+                       fprintf(stderr,
+                               "Failed to create app-specific context field event expression.\n");
+                       goto error;
+               }
+
+               break;
+       }
+
+       default:
+               fprintf(stderr, "%s: unexpected load expr type %d.\n",
+                       __func__, load_expr_op->type);
+               abort();
+       }
+
+       load_expr_op = load_expr_op->next;
+
+       /* There may be a single array index after that.  */
+       if (load_expr_op->type == IR_LOAD_EXPRESSION_GET_INDEX) {
+               uint64_t index = load_expr_op->u.index;
+               struct lttng_event_expr *index_event_expr;
+
+               index_event_expr = lttng_event_expr_array_field_element_create(event_expr, index);
+               if (!index_event_expr) {
+                       fprintf(stderr, "Failed to create array field element event expression.\n");
+                       goto error;
+               }
+
+               event_expr = index_event_expr;
+               load_expr_op = load_expr_op->next;
+       }
+
+       switch (load_expr_op->type) {
+       case IR_LOAD_EXPRESSION_LOAD_FIELD:
+               /*
+                * This is what we expect, IR_LOAD_EXPRESSION_LOAD_FIELD is
+                * always found at the end of the chain.
+                */
+               break;
+       case IR_LOAD_EXPRESSION_GET_SYMBOL:
+               fprintf(stderr, "Error: While parsing expression `%s`: Capturing subfields is not supported.\n",
+                       capture_str);
+               goto error;
+
+       default:
+               fprintf(stderr, "%s: unexpected load expression operator %s.\n",
+                       __func__, ir_load_expression_type_str(load_expr_op->type));
+               abort();
+       }
+
+       goto end;
+
+error:
+       lttng_event_expr_destroy(event_expr);
+       event_expr = NULL;
+
+end:
+       free(provider_name);
+
+       return event_expr;
+}
+
+static
+struct lttng_event_expr *ir_op_load_to_event_expr(struct ir_op *ir,
+               const char *capture_str)
+{
+       struct lttng_event_expr *event_expr = NULL;
+
+       assert(ir->op == IR_OP_LOAD);
+
+       switch (ir->data_type) {
+       case IR_DATA_EXPRESSION:
+       {
+               struct ir_load_expression *ir_load_expr = ir->u.load.u.expression;
+               event_expr = ir_op_load_expr_to_event_expr(ir_load_expr, capture_str);
+               break;
+       }
+
+       default:
+               fprintf(stderr, "%s: unexpected data type: %s.\n", __func__,
+                       ir_data_type_str(ir->data_type));
+               abort();
+       }
+
+       return event_expr;
+}
+
+static
+struct lttng_event_expr *ir_op_root_to_event_expr(struct ir_op *ir,
+               const char *capture_str)
+{
+       struct lttng_event_expr *event_expr = NULL;
+
+       assert(ir->op == IR_OP_ROOT);
+       ir = ir->u.root.child;
+
+       switch (ir->op) {
+       case IR_OP_LOAD:
+               event_expr = ir_op_load_to_event_expr(ir, capture_str);
+               break;
+
+       case IR_OP_BINARY:
+               fprintf(stderr, "Error: While parsing expression `%s`: Binary operators are not allowed in capture expressions.\n",
+                       capture_str);
+               break;
+
+       case IR_OP_UNARY:
+               fprintf(stderr, "Error: While parsing expression `%s`: Unary operators are not allowed in capture expressions.\n",
+                       capture_str);
+               break;
+
+       case IR_OP_LOGICAL:
+               fprintf(stderr, "Error: While parsing expression `%s`: Logical operators are not allowed in capture expressions.\n",
+                       capture_str);
+               break;
+
+       default:
+               fprintf(stderr, "%s: unexpected IR op type: %s.\n", __func__,
+                       ir_op_type_str(ir->op));
+               abort();
+       }
+
+       return event_expr;
+}
+
+static
+void destroy_event_expr(void *ptr)
+{
+       lttng_event_expr_destroy(ptr);
+}
+
+struct parse_event_rule_res {
+       /* Owned by this */
+       struct lttng_event_rule *er;
+
+       /* Array of `struct lttng_event_expr *` */
+       struct lttng_dynamic_pointer_array capture_descriptors;
+};
+
+static
+struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv)
+{
+       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_kernel_probe_location *kernel_probe_location = NULL;
+       struct lttng_userspace_probe_location *userspace_probe_location = NULL;
+       struct parse_event_rule_res res = { 0 };
+       struct lttng_event_expr *event_expr = NULL;
+       struct filter_parser_ctx *parser_ctx = 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;
+
+       lttng_dynamic_pointer_array_init(&res.capture_descriptors,
+                               destroy_event_expr);
+       state = argpar_state_create(*argc, *argv, event_rule_opt_descrs);
+       if (!state) {
+               ERR("Failed to allocate an argpar state.");
+               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) {
+                       ERR("%s", 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;
+
+                       case OPT_CAPTURE:
+                       {
+                               const char *capture_str = item_opt->arg;
+                               int ret;
+
+                               ret = filter_parser_ctx_create_from_filter_expression(
+                                       capture_str, &parser_ctx);
+                               if (ret) {
+                                       fprintf(stderr, "Failed to parse capture expression `%s`.\n", capture_str);
+                                       goto error;
+                               }
+
+                               event_expr = ir_op_root_to_event_expr(parser_ctx->ir_root,
+                                       capture_str);
+                               if (!event_expr) {
+                                       /* ir_op_root_to_event_expr has printed an error message. */
+                                       goto error;
+                               }
+
+                               ret = lttng_dynamic_pointer_array_add_pointer(
+                                               &res.capture_descriptors,
+                                               event_expr);
+                               if (ret) {
+                                       goto error;
+                               }
+
+                               /* The ownership of event expression was transferred to the dynamic array. */
+                               event_expr = NULL;
+
+                               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) {
+                               ERR(
+                                       "Unexpected argument: %s",
+                                       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:
+                       ERR("Can't use -a/--all with event rule of type %s.",
+                               lttng_event_rule_type_str(event_rule_type));
+                       goto error;
+               }
+
+               if (tracepoint_name) {
+                       ERR("Can't provide a tracepoint name with -a/--all.");
+                       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) {
+               ERR("Need to provide either a tracepoint name or -a/--all.");
+               goto error;
+       }
+
+       /*
+        * We don't support multiple tracepoint names for now.
+        */
+       if (strchr(tracepoint_name, ',')) {
+               ERR("multiple tracepoint names are not supported at the moment.");
+               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) {
+               ERR("Please specify a domain (-k/-u/-j).");
+               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) {
+                       ERR("Event type not available for user-space tracing");
+                       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) {
+                       ERR("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) {
+                       ERR("Failed to create exclusion list.");
+                       goto error;
+               }
+       }
+
+       if (loglevel_str && event_rule_type != LTTNG_EVENT_RULE_TYPE_TRACEPOINT) {
+               ERR("Log levels are only application to tracepoint event rules.");
+               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;
+
+               res.er = lttng_event_rule_tracepoint_create(domain_type);
+               if (!res.er) {
+                       ERR("Failed to create tracepoint event rule.");
+                       goto error;
+               }
+
+               /* Set pattern. */
+               event_rule_status =
+                       lttng_event_rule_tracepoint_set_pattern(res.er, tracepoint_name);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set tracepoint pattern.");
+                       goto error;
+               }
+
+               /* Set filter. */
+               if (filter) {
+                       event_rule_status =
+                               lttng_event_rule_tracepoint_set_filter(
+                                       res.er, filter);
+                       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                               ERR("Failed to set tracepoint filter expression.");
+                               goto error;
+                       }
+               }
+
+               /* Set exclusion list. */
+               if (exclusion_list) {
+                       int n;
+
+                       for (n = 0; exclusion_list[n]; n++) {
+                               event_rule_status = lttng_event_rule_tracepoint_add_exclusion(
+                                               res.er,
+                                               exclusion_list[n]);
+                               if (event_rule_status !=
+                                               LTTNG_EVENT_RULE_STATUS_OK) {
+                                       ERR("Failed to set tracepoint exclusion list.");
+                                       goto error;
+                               }
+                       }
+               }
+
+               if (loglevel_str) {
+                       int loglevel;
+
+                       if (domain_type == LTTNG_DOMAIN_KERNEL) {
+                               ERR("Log levels are not supported by the kernel tracer.");
+                               goto error;
+                       }
+
+                       loglevel = parse_loglevel_string(
+                               loglevel_str, domain_type);
+                       if (loglevel < 0) {
+                               ERR("Failed to parse `%s` as a log level.", loglevel_str);
+                               goto error;
+                       }
+
+                       if (loglevel_only) {
+                               event_rule_status =
+                                       lttng_event_rule_tracepoint_set_log_level(
+                                               res.er, loglevel);
+                       } else {
+                               event_rule_status =
+                                       lttng_event_rule_tracepoint_set_log_level_range_lower_bound(
+                                               res.er, loglevel);
+                       }
+
+                       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                               ERR("Failed to set log level.");
+                               goto error;
+                       }
+               }
+
+               break;
+       }
+
+       case LTTNG_EVENT_RULE_TYPE_KPROBE:
+       {
+               int ret;
+               enum lttng_event_rule_status event_rule_status;
+
+               res.er = lttng_event_rule_kprobe_create();
+               if (!res.er) {
+                       ERR("Failed to create kprobe event rule.");
+                       goto error;
+               }
+
+               ret = parse_kernel_probe_opts(source, &kernel_probe_location);
+               if (ret) {
+                       ERR("Failed to parse kernel probe location.");
+                       goto error;
+               }
+
+               event_rule_status = lttng_event_rule_kprobe_set_name(res.er, tracepoint_name);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set kprobe event rule's name.");
+                       goto error;
+               }
+
+               assert(kernel_probe_location);
+               event_rule_status = lttng_event_rule_kprobe_set_location(res.er, kernel_probe_location);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set kprobe event rule's location.");
+                       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) {
+                       ERR("Failed to parse userspace probe location.");
+                       goto error;
+               }
+
+               res.er = lttng_event_rule_uprobe_create();
+               if (!res.er) {
+                       ERR("Failed to create userspace probe event rule.");
+                       goto error;
+               }
+
+               event_rule_status = lttng_event_rule_uprobe_set_location(res.er, userspace_probe_location);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set userspace probe event rule's location.");
+                       goto error;
+               }
+
+               event_rule_status = lttng_event_rule_uprobe_set_name(res.er, tracepoint_name);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set userspace probe event rule's name.");
+                       goto error;
+               }
+
+               break;
+       }
+
+       case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+       {
+               enum lttng_event_rule_status event_rule_status;
+
+               res.er = lttng_event_rule_syscall_create();
+               if (!res.er) {
+                       ERR("Failed to create syscall event rule.");
+                       goto error;
+               }
+
+               event_rule_status = lttng_event_rule_syscall_set_pattern(res.er, tracepoint_name);
+               if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set syscall event rule's pattern.");
+                       goto error;
+               }
+
+               if (filter) {
+                       event_rule_status = lttng_event_rule_syscall_set_filter(
+                                       res.er, filter);
+                       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                               ERR("Failed to set syscall event rule's filter expression.");
+                               goto error;
+                       }
+               }
+
+               break;
+       }
+
+       default:
+               ERR("%s: I don't support event rules of type `%s` at the moment.", __func__,
+                       lttng_event_rule_type_str(event_rule_type));
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_event_rule_destroy(res.er);
+       res.er = NULL;
+       lttng_dynamic_pointer_array_reset(&res.capture_descriptors);
+
+end:
+       if (parser_ctx) {
+               filter_parser_ctx_free(parser_ctx);
+       }
+
+       lttng_event_expr_destroy(event_expr);
+       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_kernel_probe_location_destroy(kernel_probe_location);
+       lttng_userspace_probe_location_destroy(userspace_probe_location);
+       return res;
+}
+
+static
+struct lttng_condition *handle_condition_event(int *argc, const char ***argv)
+{
+       struct parse_event_rule_res res;
+       struct lttng_condition *c;
+       size_t i;
+
+       res = parse_event_rule(argc, argv);
+       if (!res.er) {
+               c = NULL;
+               goto error;
+       }
+
+       c = lttng_condition_event_rule_create(res.er);
+       if (!c) {
+               goto error;
+       }
+
+       for (i = 0; i < lttng_dynamic_pointer_array_get_count(&res.capture_descriptors);
+                       i++) {
+               enum lttng_condition_status status;
+               struct lttng_event_expr **expr =
+                               lttng_dynamic_array_get_element(
+                                       &res.capture_descriptors.array, i);
+
+               assert(expr);
+               assert(*expr);
+               status = lttng_condition_event_rule_append_capture_descriptor(
+                               c, *expr);
+               if (status != LTTNG_CONDITION_STATUS_OK) {
+                       if (status == LTTNG_CONDITION_STATUS_UNSUPPORTED) {
+                               ERR("The capture feature is unsupported by the event-rule type");
+                       }
+                       goto error;
+               }
+
+               /* Ownership of event expression moved to `c` */
+               *expr = NULL;
+       }
+
+       goto end;
+
+error:
+       lttng_condition_destroy(c);
+       c = NULL;
+
+end:
+       lttng_dynamic_pointer_array_reset(&res.capture_descriptors);
+       lttng_event_rule_destroy(res.er);
+       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) {
+               ERR("Failed to allocate an argpar state.");
+               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) {
+                       ERR("%s", 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:
+                               ERR("Unexpected argument `%s`.",
+                                       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) {
+               ERR("Missing session name argument.");
+               goto error;
+       }
+
+       if (!threshold_arg) {
+               ERR("Missing threshold argument.");
+               goto error;
+       }
+
+       if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) {
+               ERR("Failed to parse `%s` as a size.", threshold_arg);
+               goto error;
+       }
+
+       cond = lttng_condition_session_consumed_size_create();
+       if (!cond) {
+               ERR("Failed to allocate a session consumed size condition.");
+               goto error;
+       }
+
+       condition_status = lttng_condition_session_consumed_size_set_session_name(
+               cond, session_name_arg);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set session consumed size condition session name.");
+               goto error;
+       }
+
+
+       condition_status = lttng_condition_session_consumed_size_set_threshold(
+               cond, threshold);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set session consumed size condition threshold.");
+               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) {
+               ERR("Failed to allocate an argpar state.");
+               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) {
+                       ERR("%s", 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:
+                               ERR("Unexpected argument `%s`.",
+                                       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) {
+               ERR("Missing session name argument.");
+               goto error;
+       }
+
+       if (!threshold_arg) {
+               ERR("Missing threshold argument.");
+               goto error;
+       }
+
+       if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) {
+               ERR("Failed to parse `%s` as a size.", threshold_arg);
+               goto error;
+       }
+
+       cond = lttng_condition_session_consumed_size_create();
+       if (!cond) {
+               ERR("Failed to allocate a session consumed size condition.");
+               goto error;
+       }
+
+       condition_status = lttng_condition_session_consumed_size_set_session_name(
+               cond, session_name_arg);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set session consumed size condition session name.");
+               goto error;
+       }
+
+       condition_status = lttng_condition_session_consumed_size_set_threshold(
+               cond, threshold);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set session consumed size condition threshold.");
+               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) {
+               ERR("Missing condition name.");
+               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) {
+               ERR("Unknown condition name: %s", 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) {
+               ERR("Failed to allocate an argpar state.");
+               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) {
+                       ERR("%s", 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:
+                       ERR("Unexpected argument `%s`.",
+                               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) {
+               ERR("Missing session name.");
+               goto error;
+       }
+
+       action = create_action_cb();
+       if (!action) {
+               ERR(
+                       "Failed to allocate %s session action.", action_name);
+               goto error;
+       }
+
+       action_status = set_session_name_cb(action, session_name_arg);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR(
+                       "Failed to set action %s session's session name.",
+                       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) {
+               ERR("Failed to allocate an argpar state.");
+               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) {
+                       ERR("%s", 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:
+                               ERR("Unexpected argument `%s`.",
+                                       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) {
+               ERR("Missing session name.");
+               goto error;
+       }
+
+       /* --ctrl-url and --data-url must come in pair. */
+       if (ctrl_url_arg && !data_url_arg) {
+               ERR("--ctrl-url is specified, but --data-url is missing.");
+               goto error;
+       }
+
+       if (!ctrl_url_arg && data_url_arg) {
+               ERR("--data-url is specified, but --ctrl-url is missing.");
+               goto error;
+       }
+
+       /* --ctrl-url/--data-url and the non-option URL are mutually exclusive. */
+       if (ctrl_url_arg && url_arg) {
+               ERR("Both --ctrl-url/--data-url and the non-option URL argument "
+                               "can't be used together.");
+               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) {
+                       ERR("Failed to allocate a snapshot output.");
+                       goto error;
+               }
+       }
+
+       action = lttng_action_snapshot_session_create();
+       if (!action) {
+               ERR(
+                       "Failed to allocate snapshot session action.");
+               goto error;
+       }
+
+       action_status = lttng_action_snapshot_session_set_session_name(
+               action, session_name_arg);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR(
+                       "Failed to set action snapshot session's session name.");
+               goto error;
+       }
+
+       if (snapshot_name_arg) {
+               if (!snapshot_output) {
+                       ERR("Can't provide a snapshot output name without a snapshot output destination.");
+                       goto error;
+               }
+
+               ret = lttng_snapshot_output_set_name(snapshot_name_arg, snapshot_output);
+               if (ret != 0) {
+                       ERR("Failed to set name of snapshot output.");
+                       goto error;
+               }
+       }
+
+       if (max_size_arg) {
+               uint64_t max_size;
+
+               if (!snapshot_output) {
+                       ERR("Can't provide a snapshot output max size without a snapshot output destination.");
+                       goto error;
+               }
+
+               ret = utils_parse_size_suffix(max_size_arg, &max_size);
+               if (ret != 0) {
+                       ERR("Failed to parse `%s` as a size.", max_size_arg);
+                       goto error;
+               }
+
+               ret = lttng_snapshot_output_set_size(max_size, snapshot_output);
+               if (ret != 0) {
+                       ERR("Failed to set snapshot output's max size.");
+                       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) {
+                               ERR("Failed to parse %s as a network URL.", url_arg);
+                               goto error;
+                       }
+               } else {
+                       ret = lttng_snapshot_output_set_local_path(
+                               url_arg, snapshot_output);
+                       if (ret != 0) {
+                               ERR("Failed to parse %s as a local path.", 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) {
+                       ERR("Failed to parse `%s` and `%s` as control and data URLs.",
+                               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) {
+                       ERR("Failed to set snapshot session action's output.");
+                       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) {
+               ERR("Missing action name.");
+               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) {
+               ERR("Unknown action name: %s", 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 },
+       { OPT_USER_ID, '\0', "user-id", 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;
+       char *user_id = 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) {
+                       ERR("Failed to create argpar state.");
+                       goto error;
+               }
+
+               status = argpar_state_parse_next(argpar_state, &argpar_item, &error);
+
+               if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+                       ERR("%s", error);
+                       goto error;
+               } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+                       ERR("%s", 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;
+
+                       ERR("Unexpected argument `%s`.",
+                               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) {
+                               ERR("A --condition was already given.");
+                               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) {
+                               ERR("Failed to add pointer to pointer array.");
+                               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;
+               }
+
+               case OPT_USER_ID:
+               {
+                       if (!assign_string(&user_id, item_opt->arg,
+                                       "--user-id")) {
+                               goto error;
+                       }
+                       break;
+               }
+
+               default:
+                       abort();
+               }
+       }
+
+       if (!condition) {
+               ERR("Missing --condition.");
+               goto error;
+       }
+
+       if (lttng_dynamic_pointer_array_get_count(&actions) == 0) {
+               ERR("Need at least one --action.");
+               goto error;
+       }
+
+       if (fire_every_str && fire_once_after_str) {
+               ERR("Can't specify both --fire-once-after and --fire-every.");
+               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;
+       }
+
+       if (id) {
+               enum lttng_trigger_status trigger_status =
+                       lttng_trigger_set_name(trigger, id);
+               if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+                       ERR("Failed to set trigger id.");
+                       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) {
+                       ERR("Failed to parse `%s` as an integer.", 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) {
+                       ERR("Failed to set trigger's firing policy.");
+                       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) {
+                       ERR("Failed to parse `%s` as an integer.", 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) {
+                       ERR("Failed to set trigger's firing policy.");
+                       goto error;
+               }
+       }
+
+       if (user_id) {
+               enum lttng_trigger_status trigger_status;
+               char *end;
+               long long uid;
+
+               errno = 0;
+               uid = strtol(user_id, &end, 10);
+               if (end == user_id || *end != '\0' || errno != 0) {
+                       ERR("Failed to parse `%s` as an integer.", user_id);
+               }
+
+               trigger_status = lttng_trigger_set_user_identity(trigger,
+                       uid);
+               if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+                       ERR("Failed to set trigger's user identity.");
+                       goto error;
+               }
+       }
+
+       ret = lttng_register_trigger(trigger);
+       if (ret) {
+               ERR("Failed to register trigger: %s.",
+                       lttng_strerror(ret));
+               goto error;
+       }
+
+       MSG("Trigger registered successfully.");
+
+       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);
+       free(user_id);
+       // TODO: check what else to free
+
+       return ret;
+}
index f41dc9ecac7424e0a4edc458a982f00353646c95..2187f702954c857518e91ed2fc21a4ed7df80dd1 100644 (file)
 /* Mi dependancy */
 #include <common/mi-lttng.h>
 
 /* Mi dependancy */
 #include <common/mi-lttng.h>
 
+#include <lttng/event-internal.h>
+
 #include "../command.h"
 #include "../command.h"
+#include "../uprobe.h"
 
 #if (LTTNG_SYMBOL_NAME_LEN == 256)
 #define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API    "255"
 
 #if (LTTNG_SYMBOL_NAME_LEN == 256)
 #define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API    "255"
@@ -174,397 +177,11 @@ end:
        return ret;
 }
 
        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
  */
 /*
  * 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];
 {
        int i = 0;
        char str[LTTNG_SYMBOL_NAME_LEN];
@@ -607,7 +224,8 @@ static int loglevel_log4j_str_to_value(const char *inputstr)
 /*
  * Maps JUL loglevel from string to value
  */
 /*
  * 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];
 {
        int i = 0;
        char str[LTTNG_SYMBOL_NAME_LEN];
@@ -652,7 +270,8 @@ static int loglevel_jul_str_to_value(const char *inputstr)
 /*
  * Maps Python loglevel from string to value
  */
 /*
  * 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];
 {
        int i = 0;
        char str[LTTNG_SYMBOL_NAME_LEN];
@@ -691,7 +310,7 @@ static int loglevel_python_str_to_value(const char *inputstr)
 /*
  * Maps loglevel from string to value
  */
 /*
  * Maps loglevel from string to value
  */
-static
+LTTNG_HIDDEN
 int loglevel_str_to_value(const char *inputstr)
 {
        int i = 0;
 int loglevel_str_to_value(const char *inputstr)
 {
        int i = 0;
@@ -891,7 +510,16 @@ end:
        return ret;
 }
 
        return ret;
 }
 
-static
+/*
+ * FIXME: find a good place to declare this since add trigger also uses it
+ */
+LTTNG_HIDDEN
+int create_exclusion_list_and_validate(const char *event_name,
+               const char *exclusions_arg,
+               char ***exclusion_list);
+
+
+LTTNG_HIDDEN
 int create_exclusion_list_and_validate(const char *event_name,
                const char *exclusions_arg,
                char ***exclusion_list)
 int create_exclusion_list_and_validate(const char *event_name,
                const char *exclusions_arg,
                char ***exclusion_list)
@@ -969,6 +597,7 @@ static int enable_events(char *session_name)
        struct lttng_event *ev;
        struct lttng_domain dom;
        char **exclusion_list = NULL;
        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));
 
 
        memset(&dom, 0, sizeof(dom));
 
@@ -1356,7 +985,9 @@ static int enable_events(char *session_name)
                                }
                                break;
                        case LTTNG_EVENT_USERSPACE_PROBE:
                                }
                                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:
                                if (ret) {
                                        switch (ret) {
                                        case CMD_UNSUPPORTED:
@@ -1373,6 +1004,16 @@ static int enable_events(char *session_name)
                                        }
                                        goto error;
                                }
                                        }
                                        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);
                                break;
                        case LTTNG_EVENT_FUNCTION:
                                ret = parse_probe_opts(ev, opt_function);
@@ -1689,6 +1330,7 @@ error:
        }
        lttng_destroy_handle(handle);
        strutils_free_null_terminated_array_of_strings(exclusion_list);
        }
        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.
 
        /* 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..2c6559b
--- /dev/null
@@ -0,0 +1,692 @@
+#include <stdio.h>
+
+#include "../command.h"
+
+#include "common/argpar/argpar.h"
+#include "common/mi-lttng.h"
+#include "lttng/action/action.h"
+#include "lttng/action/action-internal.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"
+#include "lttng/kernel-probe.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 log_level;
+       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);
+
+       _MSG("    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) {
+               _MSG(", filter: %s", filter);
+       } else {
+               assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET);
+       }
+
+       event_rule_status = lttng_event_rule_tracepoint_get_log_level(
+               event_rule, &log_level);
+       if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) {
+               enum lttng_loglevel_type log_level_type;
+               const char *log_level_op;
+
+               event_rule_status = lttng_event_rule_tracepoint_get_log_level_type(
+                       event_rule, &log_level_type);
+               assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+               assert(log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE ||
+                       log_level_type == LTTNG_EVENT_LOGLEVEL_SINGLE);
+
+               log_level_op = (log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE ? "<=" : "==");
+
+               _MSG(", log level %s %s", log_level_op,
+                       mi_lttng_loglevel_string(log_level, 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) {
+               _MSG(", 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);
+
+                       _MSG("%s%s", i > 0 ? "," : "", exclusion);
+               }
+       }
+
+       MSG(")");
+}
+
+static void print_kernel_probe_location(
+               const struct lttng_kernel_probe_location *location)
+{
+       enum lttng_kernel_probe_location_status status;
+       switch (lttng_kernel_probe_location_get_type(location)) {
+       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS:
+       {
+               uint64_t address;
+
+               status = lttng_kernel_probe_location_address_get_address(location, &address);
+               if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) {
+                       ERR("Getting kernel probe location address failed.");
+                       goto end;
+               }
+
+               _MSG("0x%" PRIx64, address);
+
+               break;
+       }
+       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET:
+       {
+               uint64_t offset;
+               const char *symbol_name;
+
+               symbol_name = lttng_kernel_probe_location_symbol_get_name(location);
+               if (!symbol_name) {
+                       ERR("Getting kernel probe location symbol name failed.");
+                       goto end;
+               }
+
+               status = lttng_kernel_probe_location_symbol_get_offset(location, &offset);
+               if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) {
+                       ERR("Getting kernel probe location address failed.");
+                       goto end;
+               }
+
+               if (offset == 0) {
+                       _MSG("%s", symbol_name);
+               } else {
+                       _MSG("%s+0x%" PRIx64, symbol_name, offset);
+               }
+
+
+               break;
+       }
+       default:
+               abort();
+       };
+end:
+       return;
+}
+
+static
+void print_event_rule_kprobe(const struct lttng_event_rule *event_rule)
+{
+       enum lttng_event_rule_status event_rule_status;
+       const char *name;
+       const struct lttng_kernel_probe_location *location;
+
+       assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KPROBE);
+
+       event_rule_status = lttng_event_rule_kprobe_get_name(event_rule, &name);
+       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to get kprobe event rule's name.");
+               goto end;
+       }
+
+       event_rule_status = lttng_event_rule_kprobe_get_location(event_rule, &location);
+       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to get kprobe event rule's location.");
+               goto end;
+       }
+
+       _MSG("    rule: %s (type: probe, location: ", name);
+
+       print_kernel_probe_location(location);
+
+       MSG(")");
+
+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) {
+               ERR("Failed to get uprobe event rule's name.");
+               goto end;
+       }
+
+       event_rule_status = lttng_event_rule_uprobe_get_location(event_rule, &location);
+       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to get uprobe event rule's location.");
+               goto end;
+       }
+
+       _MSG("    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);
+
+               _MSG("%s:%s", binary_path, function_name);
+               break;
+       }
+
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+               _MSG("SDT not implemented yet");
+               break;
+
+       default:
+               abort();
+       }
+
+       MSG(")");
+
+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);
+
+       _MSG("  - 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) {
+               _MSG(", filter: %s", filter);
+       } else {
+               assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET);
+       }
+
+       MSG(")");
+}
+
+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_one_event_expr(const struct lttng_event_expr *event_expr)
+{
+       enum lttng_event_expr_type type;
+
+       type = lttng_event_expr_get_type(event_expr);
+
+       switch (type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: {
+               const char *name;
+
+               name = lttng_event_expr_event_payload_field_get_name(event_expr);
+               _MSG("%s", name);
+
+               break;
+       }
+
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: {
+               const char *name;
+
+               name = lttng_event_expr_channel_context_field_get_name(event_expr);
+               _MSG("$ctx.%s", name);
+
+               break;
+       }
+
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: {
+               const char *provider_name;
+               const char *type_name;
+
+               provider_name =
+                       lttng_event_expr_app_specific_context_field_get_provider_name(
+                               event_expr);
+               type_name =
+                       lttng_event_expr_app_specific_context_field_get_type_name(
+                               event_expr);
+
+               _MSG("$app.%s:%s", provider_name, type_name);
+
+               break;
+       }
+
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: {
+               unsigned int index;
+               const struct lttng_event_expr *parent_expr;
+               enum lttng_event_expr_status status;
+
+               parent_expr = lttng_event_expr_array_field_element_get_parent_expr(
+                       event_expr);
+               assert(parent_expr != NULL);
+
+               print_one_event_expr(parent_expr);
+
+               status = lttng_event_expr_array_field_element_get_index(
+                       event_expr, &index);
+               assert(status == LTTNG_EVENT_EXPR_STATUS_OK);
+
+               _MSG("[%u]", index);
+
+               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;
+       unsigned int cap_desc_count, i;
+
+       condition_status =
+               lttng_condition_event_rule_get_rule(condition, &event_rule);
+       assert(condition_status == LTTNG_CONDITION_STATUS_OK);
+
+       print_event_rule(event_rule);
+
+       condition_status
+               = lttng_condition_event_rule_get_capture_descriptor_count(
+                       condition, &cap_desc_count);
+       assert(condition_status == LTTNG_CONDITION_STATUS_OK);
+
+       if (cap_desc_count > 0) {
+               MSG("    captures:");
+
+               for (i = 0; i < cap_desc_count; i++) {
+                       const struct lttng_event_expr *cap_desc =
+                               lttng_condition_event_rule_get_capture_descriptor_at_index(
+                                       condition, i);
+
+                       _MSG("      - ");
+                       print_one_event_expr(cap_desc);
+                       MSG("");
+               }
+       }
+}
+
+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:
+               MSG("notify");
+               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);
+               MSG("start session `%s`", 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);
+               MSG("stop session `%s`", 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);
+               MSG("rotate session `%s`", 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);
+               _MSG("snapshot session `%s`", value);
+
+               action_status = lttng_action_snapshot_session_get_output(
+                       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://");
+                               }
+
+                               _MSG(", path: %s", ctrl_url);
+                       } else if (starts_with_net || starts_with_net6) {
+                               _MSG(", url: %s", ctrl_url);
+                       } else {
+                               assert(strlen(data_url) > 0);
+
+                               _MSG(", control url: %s, data url: %s", ctrl_url, data_url);
+                       }
+
+                       name = lttng_snapshot_output_get_name(output);
+                       assert(name);
+                       if (strlen(name) > 0) {
+                               _MSG(", name: %s", name);
+                       }
+
+                       max_size = lttng_snapshot_output_get_maxsize(output);
+                       if (max_size != -1ULL) {
+                               _MSG(", max size: %" PRIu64, max_size);
+                       }
+               }
+
+               MSG("");
+               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;
+       uint64_t threshold;
+       uid_t trigger_uid;
+       long error_counter;
+
+       trigger_status = lttng_trigger_get_name(trigger, &name);
+       assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       trigger_status = lttng_trigger_get_user_identity(trigger, &trigger_uid);
+       assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       MSG("- id: %s", name);
+       MSG("  user id: %d", trigger_uid);
+
+       trigger_status = lttng_trigger_get_firing_policy(trigger,
+                       &policy_type, &threshold);
+       if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+               ERR("Failed to get trigger's firing policy.");
+               goto end;
+       }
+
+       switch (policy_type) {
+       case LTTNG_TRIGGER_FIRE_EVERY_N:
+               if (threshold > 1) {
+                       MSG("  firing policy: after every %" PRIu64 " occurences", threshold);
+               }
+               break;
+
+       case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N:
+               MSG("  firing policy: once after %" PRIu64 " occurences", threshold);
+               break;
+
+       default:
+               abort();
+       }
+
+       // FIXME: This should be printed in the condition printing function
+       error_counter = lttng_trigger_get_error_count(trigger);
+       MSG("  tracer notifications discarded: %ld", error_counter);
+
+       condition = lttng_trigger_get_const_condition(trigger);
+       condition_type = lttng_condition_get_type(condition);
+       MSG("  condition: %s",
+               lttng_condition_type_str(condition_type));
+
+       switch (condition_type) {
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               print_condition_event_rule_hit(condition);
+               break;
+
+       default:
+               MSG("  (condition type not handled in %s)", __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;
+
+               MSG("  actions:");
+
+               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(action, i);
+
+                       _MSG("    ");
+                       print_one_action(subaction);
+               }
+       } else {
+               _MSG(" 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) {
+               ERR("%s", 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;
+
+                       ERR("Unexpected argument: %s", item_non_opt->arg);
+               }
+       }
+
+       ret = lttng_list_triggers(&triggers);
+       if (ret != LTTNG_OK) {
+               ERR("Error listing triggers: %s.",
+                       lttng_strerror(ret));
+               goto error;
+       }
+
+       trigger_status = lttng_triggers_get_count(triggers, &num_triggers);
+       if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+               ERR("Failed to get trigger count.");
+               goto error;
+       }
+
+       sorted_triggers = calloc(num_triggers, sizeof(struct lttng_trigger *));
+       if (!sorted_triggers) {
+               ERR("Failed to allocate array of struct lttng_trigger *.");
+               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]);
+       }
+
+       ret = 0;
+
+       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..e7ef293
--- /dev/null
@@ -0,0 +1,191 @@
+#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,
+       OPT_USER_ID,
+};
+
+static const
+struct argpar_opt_descr remove_trigger_options[] = {
+       { OPT_HELP, 'h', "help", false },
+       { OPT_LIST_OPTIONS, '\0', "list-options", false },
+       { OPT_USER_ID, '\0', "user-id", true },
+       ARGPAR_OPT_DESCR_SENTINEL,
+};
+
+static
+bool assign_string(char **dest, const char *src, const char *opt_name)
+{
+       bool ret;
+
+       if (*dest) {
+               ERR(
+                       "Duplicate %s given.", opt_name);
+               goto error;
+       }
+
+       *dest = strdup(src);
+       if (!*dest) {
+               ERR("Failed to allocate %s string.", opt_name);
+               goto error;
+       }
+
+       ret = true;
+       goto end;
+
+error:
+       ret = false;
+
+end:
+       return ret;
+}
+
+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;
+       char *user_id = NULL;
+       long long uid;
+
+       argpar_parse_ret = argpar_parse(argc - 1, argv + 1,
+               remove_trigger_options, true);
+       if (!argpar_parse_ret.items) {
+               ERR("%s", 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;
+
+                       case OPT_USER_ID:
+                       {
+                               if (!assign_string(&user_id, item_opt->arg,
+                                                   "--user-id")) {
+                                       goto error;
+                               }
+                               break;
+                       }
+
+                       default:
+                               abort();
+                       }
+
+               } else {
+                       struct argpar_item_non_opt *item_non_opt =
+                               (struct argpar_item_non_opt *) item;
+
+                       if (id) {
+                               ERR("Unexpected argument: %s", item_non_opt->arg);
+                               goto error;
+                       }
+
+                       id = item_non_opt->arg;
+               }
+       }
+
+       if (!id) {
+               ERR("Missing `id` argument.");
+               goto error;
+       }
+
+       if (user_id) {
+               char *end;
+
+               errno = 0;
+               uid = strtol(user_id, &end, 10);
+               if (end == user_id || *end != '\0' || errno != 0) {
+                       ERR("Failed to parse `%s` as an integer.", user_id);
+               }
+
+       } else {
+               uid = geteuid();
+       }
+
+       ret = lttng_list_triggers(&triggers);
+       if (ret != LTTNG_OK) {
+               ERR("Failed to get the list of triggers.");
+               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;
+               uid_t trigger_uid;
+
+               trigger = lttng_triggers_get_at_index(triggers, i);
+               trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
+               assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+               trigger_status = lttng_trigger_get_user_identity(
+                               trigger, &trigger_uid);
+               assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+               if (trigger_uid == uid &&
+                               strcmp(trigger_name, id) == 0) {
+                       trigger_to_remove = trigger;
+                       break;
+               }
+       }
+
+       if (!trigger_to_remove) {
+               ERR("Couldn't find trigger with id `%s`.", id);
+               goto error;
+       }
+
+       ret = lttng_unregister_trigger(trigger_to_remove);
+       if (ret != 0) {
+               ERR("Failed to unregister trigger `%s`.", id);
+               goto error;
+       }
+
+       MSG("Removed trigger `%s`.", id);
+
+       ret = 0;
+       goto end;
+
+error:
+       ret = 1;
+
+end:
+       argpar_parse_ret_fini(&argpar_parse_ret);
+       lttng_triggers_destroy(triggers);
+       free(user_id);
+
+       return ret;
+}
index 4907a43ba19595cc1b90906a59e2012088007cf8..437a9cc4c2e00367ec5d10f2d0061ae18f4d5c49 100644 (file)
@@ -65,6 +65,7 @@ static struct option long_options[] = {
 /* First level command */
 static struct cmd_struct commands[] =  {
        { "add-context", cmd_add_context},
 /* 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},
        { "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},
        { "enable-event", cmd_enable_events},
        { "help", NULL},
        { "list", cmd_list},
+       { "list-triggers", cmd_list_triggers},
        { "load", cmd_load},
        { "metadata", cmd_metadata},
        { "regenerate", cmd_regenerate},
        { "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},
        { "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 28b007b2402889d8782473033eee8119258ebc73..d9fd99ad55e11d8a51a52cb9c2cc4eafea861990 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
 /*
  * fls: returns the position of the most significant bit.
  * Returns 0 if no bit is set, else returns the position of the most
index 28ebc51a8fe5c829e37dfcbb033c9fdb3f37cd04..a51c8579a8e42d7de4549f44d0792ae0a5971f25 100644 (file)
@@ -9,6 +9,7 @@
 #define _LTTNG_UTILS_H
 
 #include <popt.h>
 #define _LTTNG_UTILS_H
 
 #include <popt.h>
+#include "common/argpar/argpar.h"
 
 #include <lttng/lttng.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);
 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).
 
 /*
  * Return the minimum order for which x <= (1UL << order).
index f6918b54c79d1d1466f8a34006f4168fc7a31999..31b92a962689c2b3b41767004b874e357a0d54ae 100644 (file)
@@ -2,7 +2,11 @@
 
 AUTOMAKE_OPTIONS = subdir-objects
 
 
 AUTOMAKE_OPTIONS = subdir-objects
 
-SUBDIRS = string-utils filter
+SUBDIRS = \
+       string-utils \
+       bytecode \
+       filter \
+       argpar
 
 # Make sure to always distribute all folders
 # since SUBDIRS is decided at configure time.
 
 # Make sure to always distribute all folders
 # since SUBDIRS is decided at configure time.
@@ -21,7 +25,10 @@ DIST_SUBDIRS = \
        consumer \
        string-utils \
        fd-tracker \
        consumer \
        string-utils \
        fd-tracker \
-       filter
+       bytecode \
+       filter \
+       argpar
+
 # Common library
 noinst_LTLIBRARIES = libcommon.la
 EXTRA_DIST = mi-lttng-4.0.xsd
 # Common library
 noinst_LTLIBRARIES = libcommon.la
 EXTRA_DIST = mi-lttng-4.0.xsd
@@ -34,20 +41,27 @@ libcommon_la_SOURCES = \
        actions/snapshot-session.c \
        actions/start-session.c \
        actions/stop-session.c \
        actions/snapshot-session.c \
        actions/start-session.c \
        actions/stop-session.c \
-       buffer-usage.c \
        buffer-view.h buffer-view.c \
        common.h \
        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.c credentials.h \
        daemonize.c daemonize.h \
        defaults.c \
        context.c context.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 \
        dynamic-array.c dynamic-array.h \
        dynamic-buffer.c dynamic-buffer.h \
        endpoint.c \
        error.c error.h \
        evaluation.c \
        event.c \
+       event-expr.c \
+       event-expr-to-bytecode.c event-expr-to-bytecode.h \
+       event-field-value.c \
        event-rule/event-rule.c \
        event-rule/kprobe.c \
        event-rule/syscall.c \
        event-rule/event-rule.c \
        event-rule/kprobe.c \
        event-rule/syscall.c \
@@ -58,6 +72,7 @@ libcommon_la_SOURCES = \
        fs-handle.c fs-handle.h fs-handle-internal.h \
        futex.c futex.h \
        kernel-probe.c \
        fs-handle.c fs-handle.h fs-handle-internal.h \
        futex.c futex.h \
        kernel-probe.c \
+       index-allocator.c index-allocator.h \
        location.c \
        mi-lttng.c mi-lttng.h \
        notification.c \
        location.c \
        mi-lttng.c mi-lttng.h \
        notification.c \
@@ -67,9 +82,8 @@ libcommon_la_SOURCES = \
        pipe.c pipe.h \
        readwrite.c readwrite.h \
        runas.c runas.h \
        pipe.c pipe.h \
        readwrite.c readwrite.h \
        runas.c runas.h \
-       session-consumed-size.c \
+       shm.c shm.h \
        session-descriptor.c \
        session-descriptor.c \
-       session-rotation.c \
        snapshot.c snapshot.h \
        spawn-viewer.c spawn-viewer.h \
        time.c \
        snapshot.c snapshot.h \
        spawn-viewer.c spawn-viewer.h \
        time.c \
@@ -91,11 +105,13 @@ libcommon_la_SOURCES += \
 endif
 
 libcommon_la_LIBADD = \
 endif
 
 libcommon_la_LIBADD = \
+       $(top_builddir)/src/common/bytecode/libbytecode.la \
        $(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/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/filter/libfilter.la
+       $(top_builddir)/src/common/filter/libfilter.la \
+       $(top_builddir)/src/vendor/msgpack/libmsgpack.la
 
 if BUILD_LIB_COMPAT
 SUBDIRS += compat
 
 if BUILD_LIB_COMPAT
 SUBDIRS += compat
index 11b3c5501dace3d515f5f4d3759a0e2acf61bc93..86628542bc50186c32b20a0bc026530a235672d6 100644 (file)
@@ -38,18 +38,11 @@ const char *lttng_action_type_string(enum lttng_action_type action_type)
        }
 }
 
        }
 }
 
-enum lttng_action_type lttng_action_get_type(struct lttng_action *action)
+enum lttng_action_type lttng_action_get_type(const struct lttng_action *action)
 {
        return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN;
 }
 
 {
        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,
 LTTNG_HIDDEN
 void lttng_action_init(
                struct lttng_action *action,
index f5720741598af8c1288c1a383e38f3bd7c9be6e0..31c6f456ebcdf2c11c3e0da702f5733befd13eac 100644 (file)
@@ -16,7 +16,7 @@
 #include <lttng/action/group.h>
 
 #define IS_GROUP_ACTION(action) \
 #include <lttng/action/group.h>
 
 #define IS_GROUP_ACTION(action) \
-       (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_GROUP)
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP)
 
 struct lttng_action_group {
        struct lttng_action parent;
 
 struct lttng_action_group {
        struct lttng_action parent;
index 9255034da076e23ac2df409c0313929f66021691..a492876607a4e68c53cf640f82cc8f08cd3f16d8 100644 (file)
@@ -13,7 +13,7 @@
 #include <lttng/action/rotate-session.h>
 
 #define IS_ROTATE_SESSION_ACTION(action) \
 #include <lttng/action/rotate-session.h>
 
 #define IS_ROTATE_SESSION_ACTION(action) \
-       (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_ROTATE_SESSION)
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_ROTATE_SESSION)
 
 struct lttng_action_rotate_session {
        struct lttng_action parent;
 
 struct lttng_action_rotate_session {
        struct lttng_action parent;
index 667166770d30839c2ee13c066e70e1deb09223fb..7576838650609b245f25f202ca491b3084797d50 100644 (file)
@@ -19,7 +19,7 @@
 #include <inttypes.h>
 
 #define IS_SNAPSHOT_SESSION_ACTION(action) \
 #include <inttypes.h>
 
 #define IS_SNAPSHOT_SESSION_ACTION(action) \
-       (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_SNAPSHOT_SESSION)
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_SNAPSHOT_SESSION)
 
 struct lttng_action_snapshot_session {
        struct lttng_action parent;
 
 struct lttng_action_snapshot_session {
        struct lttng_action parent;
@@ -141,7 +141,6 @@ static int lttng_action_snapshot_session_serialize(
        assert(payload);
 
        size_before_comm = payload->buffer.size;
        assert(payload);
 
        size_before_comm = payload->buffer.size;
-       size_before_comm = size_before_comm + sizeof(comm);
 
        action_snapshot_session = action_snapshot_session_from_action(action);
        comm.session_name_len =
 
        action_snapshot_session = action_snapshot_session_from_action(action);
        comm.session_name_len =
index eeb00a5daba23f7ed303b5dc916cde15575fed1d..f21aaaae97448f13d3b7c13f0c19b1d1bfeb92f3 100644 (file)
@@ -13,7 +13,7 @@
 #include <lttng/action/start-session.h>
 
 #define IS_START_SESSION_ACTION(action) \
 #include <lttng/action/start-session.h>
 
 #define IS_START_SESSION_ACTION(action) \
-       (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_START_SESSION)
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_START_SESSION)
 
 struct lttng_action_start_session {
        struct lttng_action parent;
 
 struct lttng_action_start_session {
        struct lttng_action parent;
index 33077b6766d0773ca9ed0ddf87b6072e7bf1794c..5bd753f7c56b261c853d73b0bb3669f97a324af9 100644 (file)
@@ -13,7 +13,7 @@
 #include <lttng/action/stop-session.h>
 
 #define IS_STOP_SESSION_ACTION(action) \
 #include <lttng/action/stop-session.h>
 
 #define IS_STOP_SESSION_ACTION(action) \
-       (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_STOP_SESSION)
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_STOP_SESSION)
 
 struct lttng_action_stop_session {
        struct lttng_action parent;
 
 struct lttng_action_stop_session {
        struct lttng_action parent;
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 ad6fb84..0000000
+++ /dev/null
@@ -1,823 +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_payload *payload)
-{
-       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(&payload->buffer, &usage_comm,
-                       sizeof(usage_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, usage->session_name,
-                       session_name_len);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, 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_payload(struct lttng_condition *condition,
-               struct lttng_payload_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->buffer.size < sizeof(*condition_comm)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm = (typeof(condition_comm)) src_view->buffer.data;
-       names_view = lttng_buffer_view_from_view(&src_view->buffer,
-                       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_payload(
-               struct lttng_payload_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_payload(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_payload(
-               struct lttng_payload_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_payload(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_payload(
-               enum lttng_condition_type type,
-               struct lttng_payload_view *view)
-{
-       const struct lttng_evaluation_buffer_usage_comm *comm =
-                       (typeof(comm)) view->buffer.data;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (view->buffer.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_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       evaluation = create_evaluation_from_payload(
-                       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_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       evaluation = create_evaluation_from_payload(
-                       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_payload *payload)
-{
-       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(
-                       &payload->buffer, &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/bytecode/Makefile.am b/src/common/bytecode/Makefile.am
new file mode 100644 (file)
index 0000000..9e1c6dd
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+noinst_LTLIBRARIES = libbytecode.la
+
+libbytecode_la_SOURCES = \
+       bytecode.c bytecode.h
diff --git a/src/common/bytecode/bytecode.c b/src/common/bytecode/bytecode.c
new file mode 100644 (file)
index 0000000..d78cf5a
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "bytecode.h"
+
+#include <errno.h>
+
+#include "common/align.h"
+
+#define INIT_ALLOC_SIZE 4
+
+static inline int get_count_order(unsigned int count)
+{
+       int order;
+
+       order = lttng_fls(count) - 1;
+       if (count & (count - 1))
+               order++;
+       return order;
+}
+
+LTTNG_HIDDEN
+int bytecode_init(struct lttng_bytecode_alloc **fb)
+{
+       uint32_t alloc_len;
+
+       alloc_len = sizeof(struct lttng_bytecode_alloc) + INIT_ALLOC_SIZE;
+       *fb = calloc(alloc_len, 1);
+       if (!*fb) {
+               return -ENOMEM;
+       } else {
+               (*fb)->alloc_len = alloc_len;
+               return 0;
+       }
+}
+
+LTTNG_HIDDEN
+int32_t bytecode_reserve(struct lttng_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_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_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;
+}
+
+LTTNG_HIDDEN
+int bytecode_push(struct lttng_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;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_logical(struct lttng_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;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_payload_root(struct lttng_bytecode_alloc **bytecode)
+{
+       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 = BYTECODE_OP_GET_PAYLOAD_ROOT;
+       ret = bytecode_push(bytecode, insn, 1, insn_len);
+       free(insn);
+
+       return ret;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_context_root(struct lttng_bytecode_alloc **bytecode)
+{
+       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 = BYTECODE_OP_GET_CONTEXT_ROOT;
+       ret = bytecode_push(bytecode, insn, 1, insn_len);
+       free(insn);
+
+       return ret;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_app_context_root(struct lttng_bytecode_alloc **bytecode)
+{
+       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 = BYTECODE_OP_GET_APP_CONTEXT_ROOT;
+       ret = bytecode_push(bytecode, insn, 1, insn_len);
+       free(insn);
+
+       return ret;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_index_u64(struct lttng_bytecode_alloc **bytecode,
+               uint64_t index)
+{
+       struct load_op *insn;
+       uint32_t insn_len = sizeof(struct load_op)
+               + sizeof(struct get_index_u64);
+       struct get_index_u64 index_op_data;
+       int ret;
+
+       insn = calloc(insn_len, 1);
+       if (!insn)
+               return -ENOMEM;
+
+       insn->op = BYTECODE_OP_GET_INDEX_U64;
+       index_op_data.index = index;
+       memcpy(insn->data, &index_op_data, sizeof(index));
+       ret = bytecode_push(bytecode, insn, 1, insn_len);
+
+       free(insn);
+
+       return ret;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_symbol(struct lttng_bytecode_alloc **bytecode,
+               struct lttng_bytecode_alloc **bytecode_reloc,
+               const char *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) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       insn->op = BYTECODE_OP_GET_SYMBOL;
+
+       /*
+        * Get offset in the reloc portion at which the symbol name
+        * will end up at (GET_SYMBOL's operand points there).
+        */
+       bytecode_reloc_offset_u32 =
+                       bytecode_get_len(&(*bytecode_reloc)->b)
+                       + sizeof(reloc_offset);
+       symbol_offset.offset = (uint16_t) bytecode_reloc_offset_u32;
+       memcpy(insn->data, &symbol_offset, sizeof(symbol_offset));
+
+       /*
+        * Get offset in the bytecode where the opcode will end up at,
+        * the reloc offset points to it.
+        */
+       reloc_offset_u32 = bytecode_get_len(&(*bytecode)->b);
+       if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
+               ret = -EINVAL;
+               goto end;
+       }
+       reloc_offset = (uint16_t) reloc_offset_u32;
+
+       /* Append op in bytecode. */
+       ret = bytecode_push(bytecode, insn, 1, insn_len);
+       if (ret) {
+               goto end;
+       }
+
+       /* Append reloc offset. */
+       ret = bytecode_push(bytecode_reloc, &reloc_offset,
+                       1, sizeof(reloc_offset));
+       if (ret) {
+               goto end;
+       }
+
+       /* Append symbol name. */
+       ret = bytecode_push(bytecode_reloc, symbol, 1, strlen(symbol) + 1);
+
+end:
+       free(insn);
+       return ret;
+}
+
+/*
+ * Allocate an lttng_bytecode object and copy the given original bytecode.
+ *
+ * Return allocated bytecode or NULL on error.
+ */
+struct lttng_bytecode *bytecode_copy(
+               const struct lttng_bytecode *orig_f)
+{
+       struct lttng_bytecode *bytecode = NULL;
+
+       bytecode = zmalloc(sizeof(*bytecode) + orig_f->len);
+       if (!bytecode) {
+               goto error;
+       }
+
+       memcpy(bytecode, orig_f, sizeof(*bytecode) + orig_f->len);
+
+error:
+       return bytecode;
+}
diff --git a/src/common/bytecode/bytecode.h b/src/common/bytecode/bytecode.h
new file mode 100644 (file)
index 0000000..b519fe1
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_COMMON_BYTECODE_H
+#define LTTNG_COMMON_BYTECODE_H
+
+#include <stdint.h>
+
+#include "common/macros.h"
+#include "common/sessiond-comm/sessiond-comm.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 bytecode_op {
+       BYTECODE_OP_UNKNOWN                             = 0,
+
+       BYTECODE_OP_RETURN                              = 1,
+
+       /* binary */
+       BYTECODE_OP_MUL                                 = 2,
+       BYTECODE_OP_DIV                                 = 3,
+       BYTECODE_OP_MOD                                 = 4,
+       BYTECODE_OP_PLUS                                = 5,
+       BYTECODE_OP_MINUS                               = 6,
+       BYTECODE_OP_BIT_RSHIFT                          = 7,
+       BYTECODE_OP_BIT_LSHIFT                          = 8,
+       BYTECODE_OP_BIT_AND                             = 9,
+       BYTECODE_OP_BIT_OR                              = 10,
+       BYTECODE_OP_BIT_XOR                             = 11,
+
+       /* binary comparators */
+       BYTECODE_OP_EQ                                  = 12,
+       BYTECODE_OP_NE                                  = 13,
+       BYTECODE_OP_GT                                  = 14,
+       BYTECODE_OP_LT                                  = 15,
+       BYTECODE_OP_GE                                  = 16,
+       BYTECODE_OP_LE                                  = 17,
+
+       /* string binary comparator: apply to  */
+       BYTECODE_OP_EQ_STRING                           = 18,
+       BYTECODE_OP_NE_STRING                           = 19,
+       BYTECODE_OP_GT_STRING                           = 20,
+       BYTECODE_OP_LT_STRING                           = 21,
+       BYTECODE_OP_GE_STRING                           = 22,
+       BYTECODE_OP_LE_STRING                           = 23,
+
+       /* s64 binary comparator */
+       BYTECODE_OP_EQ_S64                              = 24,
+       BYTECODE_OP_NE_S64                              = 25,
+       BYTECODE_OP_GT_S64                              = 26,
+       BYTECODE_OP_LT_S64                              = 27,
+       BYTECODE_OP_GE_S64                              = 28,
+       BYTECODE_OP_LE_S64                              = 29,
+
+       /* double binary comparator */
+       BYTECODE_OP_EQ_DOUBLE                           = 30,
+       BYTECODE_OP_NE_DOUBLE                           = 31,
+       BYTECODE_OP_GT_DOUBLE                           = 32,
+       BYTECODE_OP_LT_DOUBLE                           = 33,
+       BYTECODE_OP_GE_DOUBLE                           = 34,
+       BYTECODE_OP_LE_DOUBLE                           = 35,
+
+       /* Mixed S64-double binary comparators */
+       BYTECODE_OP_EQ_DOUBLE_S64                       = 36,
+       BYTECODE_OP_NE_DOUBLE_S64                       = 37,
+       BYTECODE_OP_GT_DOUBLE_S64                       = 38,
+       BYTECODE_OP_LT_DOUBLE_S64                       = 39,
+       BYTECODE_OP_GE_DOUBLE_S64                       = 40,
+       BYTECODE_OP_LE_DOUBLE_S64                       = 41,
+
+       BYTECODE_OP_EQ_S64_DOUBLE                       = 42,
+       BYTECODE_OP_NE_S64_DOUBLE                       = 43,
+       BYTECODE_OP_GT_S64_DOUBLE                       = 44,
+       BYTECODE_OP_LT_S64_DOUBLE                       = 45,
+       BYTECODE_OP_GE_S64_DOUBLE                       = 46,
+       BYTECODE_OP_LE_S64_DOUBLE                       = 47,
+
+       /* unary */
+       BYTECODE_OP_UNARY_PLUS                          = 48,
+       BYTECODE_OP_UNARY_MINUS                         = 49,
+       BYTECODE_OP_UNARY_NOT                           = 50,
+       BYTECODE_OP_UNARY_PLUS_S64                      = 51,
+       BYTECODE_OP_UNARY_MINUS_S64                     = 52,
+       BYTECODE_OP_UNARY_NOT_S64                       = 53,
+       BYTECODE_OP_UNARY_PLUS_DOUBLE                   = 54,
+       BYTECODE_OP_UNARY_MINUS_DOUBLE                  = 55,
+       BYTECODE_OP_UNARY_NOT_DOUBLE                    = 56,
+
+       /* logical */
+       BYTECODE_OP_AND                                 = 57,
+       BYTECODE_OP_OR                                  = 58,
+
+       /* load field ref */
+       BYTECODE_OP_LOAD_FIELD_REF                      = 59,
+       BYTECODE_OP_LOAD_FIELD_REF_STRING               = 60,
+       BYTECODE_OP_LOAD_FIELD_REF_SEQUENCE             = 61,
+       BYTECODE_OP_LOAD_FIELD_REF_S64                  = 62,
+       BYTECODE_OP_LOAD_FIELD_REF_DOUBLE               = 63,
+
+       /* load immediate from operand */
+       BYTECODE_OP_LOAD_STRING                         = 64,
+       BYTECODE_OP_LOAD_S64                            = 65,
+       BYTECODE_OP_LOAD_DOUBLE                         = 66,
+
+       /* cast */
+       BYTECODE_OP_CAST_TO_S64                         = 67,
+       BYTECODE_OP_CAST_DOUBLE_TO_S64                  = 68,
+       BYTECODE_OP_CAST_NOP                            = 69,
+
+       /* get context ref */
+       BYTECODE_OP_GET_CONTEXT_REF                     = 70,
+       BYTECODE_OP_GET_CONTEXT_REF_STRING              = 71,
+       BYTECODE_OP_GET_CONTEXT_REF_S64                 = 72,
+       BYTECODE_OP_GET_CONTEXT_REF_DOUBLE              = 73,
+
+       /* load userspace field ref */
+       BYTECODE_OP_LOAD_FIELD_REF_USER_STRING          = 74,
+       BYTECODE_OP_LOAD_FIELD_REF_USER_SEQUENCE        = 75,
+
+       /*
+        * load immediate star globbing pattern (literal string)
+        * from immediate
+        */
+       BYTECODE_OP_LOAD_STAR_GLOB_STRING               = 76,
+
+       /* globbing pattern binary operator: apply to */
+       BYTECODE_OP_EQ_STAR_GLOB_STRING                 = 77,
+       BYTECODE_OP_NE_STAR_GLOB_STRING                 = 78,
+
+       /*
+        * Instructions for recursive traversal through composed types.
+        */
+       BYTECODE_OP_GET_CONTEXT_ROOT                    = 79,
+       BYTECODE_OP_GET_APP_CONTEXT_ROOT                = 80,
+       BYTECODE_OP_GET_PAYLOAD_ROOT                    = 81,
+
+       BYTECODE_OP_GET_SYMBOL                          = 82,
+       BYTECODE_OP_GET_SYMBOL_FIELD                    = 83,
+       BYTECODE_OP_GET_INDEX_U16                       = 84,
+       BYTECODE_OP_GET_INDEX_U64                       = 85,
+
+       BYTECODE_OP_LOAD_FIELD                          = 86,
+       BYTECODE_OP_LOAD_FIELD_S8                       = 87,
+       BYTECODE_OP_LOAD_FIELD_S16                      = 88,
+       BYTECODE_OP_LOAD_FIELD_S32                      = 89,
+       BYTECODE_OP_LOAD_FIELD_S64                      = 90,
+       BYTECODE_OP_LOAD_FIELD_U8                       = 91,
+       BYTECODE_OP_LOAD_FIELD_U16                      = 92,
+       BYTECODE_OP_LOAD_FIELD_U32                      = 93,
+       BYTECODE_OP_LOAD_FIELD_U64                      = 94,
+       BYTECODE_OP_LOAD_FIELD_STRING                   = 95,
+       BYTECODE_OP_LOAD_FIELD_SEQUENCE                 = 96,
+       BYTECODE_OP_LOAD_FIELD_DOUBLE                   = 97,
+
+       BYTECODE_OP_UNARY_BIT_NOT                       = 98,
+
+       BYTECODE_OP_RETURN_S64                          = 99,
+
+       NR_BYTECODE_OPS,
+};
+
+typedef uint8_t bytecode_opcode_t;
+
+struct load_op {
+       bytecode_opcode_t op;
+
+       /*
+        * data to load. Size known by enum bytecode_opcode_t and null-term
+        * char.
+        */
+       char data[0];
+} LTTNG_PACKED;
+
+struct binary_op {
+       bytecode_opcode_t op;
+} LTTNG_PACKED;
+
+struct unary_op {
+       bytecode_opcode_t op;
+} LTTNG_PACKED;
+
+/* skip_offset is absolute from start of bytecode */
+struct logical_op {
+       bytecode_opcode_t op;
+       uint16_t skip_offset;   /* bytecode insn, if skip second test */
+} LTTNG_PACKED;
+
+struct cast_op {
+       bytecode_opcode_t op;
+} LTTNG_PACKED;
+
+struct return_op {
+       bytecode_opcode_t op;
+} LTTNG_PACKED;
+
+struct lttng_bytecode_alloc {
+       uint32_t alloc_len;
+       struct lttng_bytecode b;
+};
+
+LTTNG_HIDDEN int bytecode_init(struct lttng_bytecode_alloc **fb);
+LTTNG_HIDDEN int32_t bytecode_reserve(struct lttng_bytecode_alloc **fb,
+               uint32_t align, uint32_t len);
+LTTNG_HIDDEN int bytecode_push(struct lttng_bytecode_alloc **fb,
+               const void *data, uint32_t align, uint32_t len);
+LTTNG_HIDDEN int bytecode_push_logical(struct lttng_bytecode_alloc **fb,
+               struct logical_op *data, uint32_t align, uint32_t len,
+               uint16_t *skip_offset);
+LTTNG_HIDDEN int bytecode_push_get_payload_root(
+               struct lttng_bytecode_alloc **bytecode);
+LTTNG_HIDDEN int bytecode_push_get_context_root(
+               struct lttng_bytecode_alloc **bytecode);
+LTTNG_HIDDEN int bytecode_push_get_app_context_root(
+               struct lttng_bytecode_alloc **bytecode);
+LTTNG_HIDDEN int bytecode_push_get_index_u64(
+               struct lttng_bytecode_alloc **bytecode, uint64_t index);
+LTTNG_HIDDEN int bytecode_push_get_symbol(
+               struct lttng_bytecode_alloc **bytecode,
+               struct lttng_bytecode_alloc **bytecode_reloc,
+               const char *symbol);
+LTTNG_HIDDEN struct lttng_bytecode *bytecode_copy(
+               const struct lttng_bytecode *orig_f);
+
+static inline
+unsigned int bytecode_get_len(struct lttng_bytecode *bytecode)
+{
+       return bytecode->len;
+}
+
+#endif /* LTTNG_COMMON_BYTECODE_H */
index aea4be5fe27ffc54e3c9c873fa36209317cb2ba9..e29d6540b56c648242afafc5c065e1ca0fe7ec9c 100644 (file)
@@ -1019,8 +1019,9 @@ int lttng_directory_handle_create_subdirectory_as_user(
                ret = create_directory_check_exists(handle,
                                subdirectory, mode);
        } else {
                ret = create_directory_check_exists(handle,
                                subdirectory, mode);
        } else {
-               ret = _run_as_mkdir(handle, subdirectory,
-                               mode, creds->uid, creds->gid);
+               ret = _run_as_mkdir(handle, subdirectory, mode,
+                               lttng_credentials_get_uid(creds),
+                               lttng_credentials_get_gid(creds));
        }
 
        return ret;
        }
 
        return ret;
@@ -1040,7 +1041,7 @@ int lttng_directory_handle_create_subdirectory_recursive_as_user(
                                subdirectory_path, mode);
        } else {
                ret = _run_as_mkdir_recursive(handle, subdirectory_path,
                                subdirectory_path, mode);
        } else {
                ret = _run_as_mkdir_recursive(handle, subdirectory_path,
-                               mode, creds->uid, creds->gid);
+                               mode, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds));
        }
 
        return ret;
        }
 
        return ret;
@@ -1081,7 +1082,7 @@ int lttng_directory_handle_open_file_as_user(
                                mode);
        } else {
                ret = _run_as_open(handle, filename, flags, mode,
                                mode);
        } else {
                ret = _run_as_open(handle, filename, flags, mode,
-                               creds->uid, creds->gid);
+                               lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds));
        }
        return ret;
 }
        }
        return ret;
 }
@@ -1108,7 +1109,7 @@ int lttng_directory_handle_unlink_file_as_user(
                /* Run as current user. */
                ret = lttng_directory_handle_unlink(handle, filename);
        } else {
                /* Run as current user. */
                ret = lttng_directory_handle_unlink(handle, filename);
        } else {
-               ret = _run_as_unlink(handle, filename, creds->uid, creds->gid);
+               ret = _run_as_unlink(handle, filename, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds));
        }
        return ret;
 }
        }
        return ret;
 }
@@ -1149,7 +1150,7 @@ int lttng_directory_handle_rename_as_user(
                                old_name, new_handle, new_name);
        } else {
                ret = _run_as_rename(old_handle, old_name, new_handle,
                                old_name, new_handle, new_name);
        } else {
                ret = _run_as_rename(old_handle, old_name, new_handle,
-                               new_name, creds->uid, creds->gid);
+                               new_name, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds));
        }
        return ret;
 }
        }
        return ret;
 }
@@ -1175,7 +1176,7 @@ int lttng_directory_handle_remove_subdirectory_as_user(
                /* Run as current user. */
                ret = lttng_directory_handle_rmdir(handle, name);
        } else {
                /* Run as current user. */
                ret = lttng_directory_handle_rmdir(handle, name);
        } else {
-               ret = _run_as_rmdir(handle, name, creds->uid, creds->gid);
+               ret = _run_as_rmdir(handle, name, lttng_credentials_get_uid(creds), lttng_credentials_get_gid(creds));
        }
        return ret;
 }
        }
        return ret;
 }
@@ -1409,8 +1410,8 @@ int lttng_directory_handle_remove_subdirectory_recursive_as_user(
                /* Run as current user. */
                ret = remove_directory_recursive(handle, name, flags);
        } else {
                /* Run as current user. */
                ret = remove_directory_recursive(handle, name, flags);
        } else {
-               ret = _run_as_rmdir_recursive(handle, name, creds->uid,
-                               creds->gid, flags);
+               ret = _run_as_rmdir_recursive(handle, name, lttng_credentials_get_uid(creds),
+                               lttng_credentials_get_gid(creds), flags);
        }
        return ret;
 }
        }
        return ret;
 }
diff --git a/src/common/condition.c b/src/common/condition.c
deleted file mode 100644 (file)
index 427c49e..0000000
+++ /dev/null
@@ -1,196 +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)
-{
-       lttng_condition_put(condition);
-}
-
-static void condition_destroy_ref(struct urcu_ref *ref)
-{
-       struct lttng_condition *condition =
-               container_of(ref, struct lttng_condition, ref);
-
-       condition->destroy(condition);
-}
-
-LTTNG_HIDDEN
-void lttng_condition_get(struct lttng_condition *condition)
-{
-       urcu_ref_get(&condition->ref);
-}
-
-LTTNG_HIDDEN
-void lttng_condition_put(struct lttng_condition *condition)
-{
-       if (!condition) {
-               return;
-       }
-
-       assert(condition->destroy);
-       urcu_ref_put(&condition->ref, condition_destroy_ref);
-}
-
-
-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_payload *payload)
-{
-       int ret;
-       struct lttng_condition_comm condition_comm = {};
-
-       if (!condition) {
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm.condition_type = (int8_t) condition->type;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &condition_comm,
-                       sizeof(condition_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = condition->serialize(condition, payload);
-       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_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **condition)
-{
-       ssize_t ret, condition_size = 0;
-       const struct lttng_condition_comm *condition_comm;
-       condition_create_from_payload_cb create_from_payload = NULL;
-
-       if (!view || !condition) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Deserializing condition from buffer");
-       condition_comm = (typeof(condition_comm)) view->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_payload = lttng_condition_buffer_usage_low_create_from_payload;
-               break;
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-               create_from_payload = lttng_condition_buffer_usage_high_create_from_payload;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
-               create_from_payload = lttng_condition_session_consumed_size_create_from_payload;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-               create_from_payload = lttng_condition_session_rotation_ongoing_create_from_payload;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               create_from_payload = lttng_condition_session_rotation_completed_create_from_payload;
-               break;
-       default:
-               ERR("Attempted to create condition of unknown type (%i)",
-                               (int) condition_comm->condition_type);
-               ret = -1;
-               goto end;
-       }
-
-       if (create_from_payload) {
-               struct lttng_payload_view condition_view =
-                               lttng_payload_view_from_view(view,
-                                       sizeof(*condition_comm), -1);
-
-               ret = create_from_payload(&condition_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;
-       urcu_ref_init(&condition->ref);
-}
diff --git a/src/common/conditions/buffer-usage.c b/src/common/conditions/buffer-usage.c
new file mode 100644 (file)
index 0000000..ad6fb84
--- /dev/null
@@ -0,0 +1,823 @@
+/*
+ * 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_payload *payload)
+{
+       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(&payload->buffer, &usage_comm,
+                       sizeof(usage_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, usage->session_name,
+                       session_name_len);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, 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_payload(struct lttng_condition *condition,
+               struct lttng_payload_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->buffer.size < sizeof(*condition_comm)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm = (typeof(condition_comm)) src_view->buffer.data;
+       names_view = lttng_buffer_view_from_view(&src_view->buffer,
+                       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_payload(
+               struct lttng_payload_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_payload(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_payload(
+               struct lttng_payload_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_payload(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_payload(
+               enum lttng_condition_type type,
+               struct lttng_payload_view *view)
+{
+       const struct lttng_evaluation_buffer_usage_comm *comm =
+                       (typeof(comm)) view->buffer.data;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (view->buffer.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_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       evaluation = create_evaluation_from_payload(
+                       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_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       evaluation = create_evaluation_from_payload(
+                       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_payload *payload)
+{
+       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(
+                       &payload->buffer, &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..a801bf4
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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)
+{
+       lttng_condition_put(condition);
+}
+
+static void condition_destroy_ref(struct urcu_ref *ref)
+{
+       struct lttng_condition *condition =
+               container_of(ref, struct lttng_condition, ref);
+
+       condition->destroy(condition);
+}
+
+LTTNG_HIDDEN
+void lttng_condition_get(struct lttng_condition *condition)
+{
+       urcu_ref_get(&condition->ref);
+}
+
+LTTNG_HIDDEN
+void lttng_condition_put(struct lttng_condition *condition)
+{
+       if (!condition) {
+               return;
+       }
+
+       assert(condition->destroy);
+       urcu_ref_put(&condition->ref, condition_destroy_ref);
+}
+
+
+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_payload *payload)
+{
+       int ret;
+       struct lttng_condition_comm condition_comm = {};
+
+       if (!condition) {
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm.condition_type = (int8_t) condition->type;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &condition_comm,
+                       sizeof(condition_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = condition->serialize(condition, payload);
+       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_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **condition)
+{
+       ssize_t ret, condition_size = 0;
+       const struct lttng_condition_comm *condition_comm;
+       condition_create_from_payload_cb create_from_payload = NULL;
+
+       if (!view || !condition) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Deserializing condition from buffer");
+       condition_comm = (typeof(condition_comm)) view->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_payload = lttng_condition_buffer_usage_low_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+               create_from_payload = lttng_condition_buffer_usage_high_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+               create_from_payload = lttng_condition_session_consumed_size_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               create_from_payload = lttng_condition_session_rotation_ongoing_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               create_from_payload = lttng_condition_session_rotation_completed_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               create_from_payload = lttng_condition_event_rule_create_from_payload;
+               break;
+       default:
+               ERR("Attempted to create condition of unknown type (%i)",
+                               (int) condition_comm->condition_type);
+               ret = -1;
+               goto end;
+       }
+
+       if (create_from_payload) {
+               struct lttng_payload_view condition_view =
+                               lttng_payload_view_from_view(view,
+                                       sizeof(*condition_comm), -1);
+
+               ret = create_from_payload(&condition_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;
+       urcu_ref_init(&condition->ref);
+}
+
+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..7333a4f
--- /dev/null
@@ -0,0 +1,1567 @@
+/*
+ * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/event-expr-to-bytecode.h>
+#include <common/macros.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/event-expr-internal.h>
+#include <lttng/event-expr.h>
+#include <lttng/event-field-value-internal.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/lttng-error.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <vendor/msgpack/msgpack.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_payload *payload);
+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;
+}
+
+/*
+ * Serializes the C string `str` into `buf`.
+ *
+ * Encoding is the length of `str` plus one (for the null character),
+ * and then the string, including its null character.
+ */
+static
+int serialize_cstr(const char *str, struct lttng_dynamic_buffer *buf)
+{
+       int ret;
+       uint32_t len = strlen(str) + 1;
+
+       /* Serialize the length, including the null character */
+       DBG("Serializing C string's length (including null character): "
+                       "%" PRIu32, len);
+       ret = lttng_dynamic_buffer_append(buf, &len, sizeof(len));
+       if (ret) {
+               goto end;
+       }
+
+       /* Serialize the string */
+       DBG("Serializing C string: \"%s\"", str);
+       ret = lttng_dynamic_buffer_append(buf, str, len);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Serializes the event expression `expr` into `buf`.
+ */
+static
+int serialize_event_expr(const struct lttng_event_expr *expr,
+               struct lttng_payload *payload)
+{
+       uint8_t type;
+       int ret;
+
+       /* Serialize the expression's type */
+       DBG("Serializing event expression's type: %d", expr->type);
+       type = expr->type;
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &type, sizeof(type));
+       if (ret) {
+               goto end;
+       }
+
+       /* Serialize the expression */
+       switch (expr->type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+       {
+               const struct lttng_event_expr_field *field_expr =
+                               container_of(expr,
+                                       const struct lttng_event_expr_field,
+                                       parent);
+
+               /* Serialize the field name */
+               DBG("Serializing field event expression's field name: \"%s\"",
+                               field_expr->name);
+               ret = serialize_cstr(field_expr->name, &payload->buffer);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               const struct lttng_event_expr_app_specific_context_field *field_expr =
+                               container_of(expr,
+                                       const struct lttng_event_expr_app_specific_context_field,
+                                       parent);
+
+               /* Serialize the provider name */
+               DBG("Serializing app-specific context field event expression's "
+                               "provider name: \"%s\"",
+                               field_expr->provider_name);
+               ret = serialize_cstr(field_expr->provider_name, &payload->buffer);
+               if (ret) {
+                       goto end;
+               }
+
+               /* Serialize the type name */
+               DBG("Serializing app-specific context field event expression's "
+                               "type name: \"%s\"",
+                               field_expr->provider_name);
+               ret = serialize_cstr(field_expr->type_name, &payload->buffer);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               const struct lttng_event_expr_array_field_element *elem_expr =
+                               container_of(expr,
+                                       const struct lttng_event_expr_array_field_element,
+                                       parent);
+               uint32_t index = elem_expr->index;
+
+               /* Serialize the index */
+               DBG("Serializing array field element event expression's "
+                               "index: %u", elem_expr->index);
+               ret = lttng_dynamic_buffer_append(&payload->buffer, &index, sizeof(index));
+               if (ret) {
+                       goto end;
+               }
+
+               /* Serialize the parent array field expression */
+               DBG("Serializing array field element event expression's "
+                               "parent array field event expression.");
+               ret = serialize_event_expr(elem_expr->array_field_expr, payload);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+struct lttng_capture_descriptor *
+lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+               const struct lttng_condition *condition, unsigned int index)
+{
+       const struct lttng_condition_event_rule *event_rule_cond =
+                       container_of(condition,
+                               const struct lttng_condition_event_rule,
+                               parent);
+       struct lttng_capture_descriptor *desc = NULL;
+       unsigned int count;
+       enum lttng_condition_status status;
+
+       if (!condition || !IS_EVENT_RULE_CONDITION(condition)) {
+               goto end;
+       }
+
+       status = lttng_condition_event_rule_get_capture_descriptor_count(
+                       condition, &count);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto end;
+       }
+
+       if (index >= count) {
+               goto end;
+       }
+
+       desc = lttng_dynamic_pointer_array_get_pointer(
+                       &event_rule_cond->capture_descriptors, index);
+end:
+       return desc;
+}
+
+static int lttng_condition_event_rule_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_condition_event_rule *event_rule;
+       enum lttng_condition_status status;
+       uint32_t capture_descr_count;
+       uint32_t i;
+
+       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);
+
+       DBG("Serializing event rule condition's event rule");
+       ret = lttng_event_rule_serialize(event_rule->rule, payload);
+       if (ret) {
+               goto end;
+       }
+
+       status = lttng_condition_event_rule_get_capture_descriptor_count(
+                       condition, &capture_descr_count);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ret = -1;
+               goto end;
+       };
+
+       DBG("Serializing event rule condition's capture descriptor count: %" PRIu32,
+                       capture_descr_count);
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_descr_count,
+                       sizeof(capture_descr_count));
+       if (ret) {
+               goto end;
+       }
+
+       for (i = 0; i < capture_descr_count; i++) {
+               const struct lttng_capture_descriptor *desc =
+                               lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+                                               condition, i);
+
+               DBG("Serializing event rule condition's capture descriptor %" PRIu32,
+                               i);
+               ret = serialize_event_expr(desc->event_expression, payload);
+               if (ret) {
+                       goto end;
+               }
+
+               /*
+                * Appending the internal index payload linked with the
+                * descriptor.
+                * TODO: might want to move to an englobing object to describe a
+                * capture descriptor publicly.
+                */
+               ret = lttng_dynamic_buffer_append(&payload->buffer, &desc->capture_index,
+                               sizeof(desc->capture_index));
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static
+bool capture_descriptors_are_equal(
+               const struct lttng_condition *condition_a,
+               const struct lttng_condition *condition_b)
+{
+       bool is_equal = true;
+       unsigned int capture_descr_count_a;
+       unsigned int capture_descr_count_b;
+       size_t i;
+       enum lttng_condition_status status;
+
+       status = lttng_condition_event_rule_get_capture_descriptor_count(
+                       condition_a, &capture_descr_count_a);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto not_equal;
+       }
+
+       status = lttng_condition_event_rule_get_capture_descriptor_count(
+                       condition_b, &capture_descr_count_b);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto not_equal;
+       }
+
+       if (capture_descr_count_a != capture_descr_count_b) {
+               goto not_equal;
+       }
+
+       for (i = 0; i < capture_descr_count_a; i++) {
+               const struct lttng_event_expr *expr_a =
+                               lttng_condition_event_rule_get_capture_descriptor_at_index(
+                                       condition_a,
+                                       i);
+               const struct lttng_event_expr *expr_b =
+                               lttng_condition_event_rule_get_capture_descriptor_at_index(
+                                       condition_b,
+                                       i);
+
+               if (!lttng_event_expr_is_equal(expr_a, expr_b)) {
+                       goto not_equal;
+               }
+       }
+
+       goto end;
+
+not_equal:
+       is_equal = false;
+
+end:
+       return is_equal;
+}
+
+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);
+       if (!is_equal) {
+               goto end;
+       }
+
+       is_equal = capture_descriptors_are_equal(_a, _b);
+
+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);
+       lttng_dynamic_pointer_array_reset(&event_rule->capture_descriptors);
+       free(event_rule);
+}
+
+static
+void destroy_capture_descriptor(void *ptr)
+{
+       struct lttng_capture_descriptor *desc =
+                       (struct lttng_capture_descriptor *) ptr;
+       lttng_event_expr_destroy(desc->event_expression);
+       free(desc);
+}
+
+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,
+
+       lttng_event_rule_get(rule);
+
+       condition->rule = rule;
+       lttng_dynamic_pointer_array_init(&condition->capture_descriptors,
+                       destroy_capture_descriptor);
+
+       parent = &condition->parent;
+end:
+       return parent;
+}
+
+static
+int64_t int_from_buffer(const struct lttng_buffer_view *view, size_t size,
+               size_t *offset)
+{
+       int64_t ret;
+
+       if (*offset + size > view->size) {
+               ret = -1;
+               goto end;
+       }
+
+       switch (size) {
+       case 1:
+               ret = (int64_t) view->data[*offset];
+               break;
+       case sizeof(int32_t):
+       {
+               int32_t s32;
+
+               memcpy(&s32, &view->data[*offset], sizeof(s32));
+               ret = (int64_t) s32;
+               break;
+       }
+       case sizeof(ret):
+               memcpy(&ret, &view->data[*offset], sizeof(ret));
+               break;
+       default:
+               abort();
+       }
+
+       *offset += size;
+
+end:
+       return ret;
+}
+static
+uint64_t uint_from_buffer(const struct lttng_buffer_view *view, size_t size,
+               size_t *offset)
+{
+       uint64_t ret;
+
+       if (*offset + size > view->size) {
+               ret = UINT64_C(-1);
+               goto end;
+       }
+
+       switch (size) {
+       case 1:
+               ret = (uint64_t) view->data[*offset];
+               break;
+       case sizeof(uint32_t):
+       {
+               uint32_t u32;
+
+               memcpy(&u32, &view->data[*offset], sizeof(u32));
+               ret = (uint64_t) u32;
+               break;
+       }
+       case sizeof(ret):
+               memcpy(&ret, &view->data[*offset], sizeof(ret));
+               break;
+       default:
+               abort();
+       }
+
+       *offset += size;
+
+end:
+       return ret;
+}
+
+static
+const char *str_from_buffer(const struct lttng_buffer_view *view,
+               size_t *offset)
+{
+       uint64_t len;
+       const char *ret;
+
+       len = uint_from_buffer(view, sizeof(uint32_t), offset);
+       if (len == UINT64_C(-1)) {
+               goto error;
+       }
+
+       ret = &view->data[*offset];
+
+       if (!lttng_buffer_view_contains_string(view, ret, len)) {
+               goto error;
+       }
+
+       *offset += len;
+       goto end;
+
+error:
+       ret = NULL;
+
+end:
+       return ret;
+}
+
+static
+struct lttng_event_expr *event_expr_from_payload(
+               struct lttng_payload_view *view, size_t *offset)
+{
+       struct lttng_event_expr *expr = NULL;
+       const char *str;
+       uint64_t type;
+
+       type = uint_from_buffer(&view->buffer, sizeof(uint8_t), offset);
+       if (type == UINT64_C(-1)) {
+               goto error;
+       }
+
+       switch (type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+               str = str_from_buffer(&view->buffer, offset);
+               if (!str) {
+                       goto error;
+               }
+
+               expr = lttng_event_expr_event_payload_field_create(str);
+               break;
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+               str = str_from_buffer(&view->buffer, offset);
+               if (!str) {
+                       goto error;
+               }
+
+               expr = lttng_event_expr_channel_context_field_create(str);
+               break;
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               const char *provider_name;
+               const char *type_name;
+
+               provider_name = str_from_buffer(&view->buffer, offset);
+               if (!provider_name) {
+                       goto error;
+               }
+
+               type_name = str_from_buffer(&view->buffer, offset);
+               if (!type_name) {
+                       goto error;
+               }
+
+               expr = lttng_event_expr_app_specific_context_field_create(
+                               provider_name, type_name);
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               struct lttng_event_expr *array_field_expr;
+               uint64_t index;
+
+               index = uint_from_buffer(&view->buffer, sizeof(uint32_t), offset);
+               if (index == UINT64_C(-1)) {
+                       goto error;
+               }
+
+               /* Array field expression is the encoded after this */
+               array_field_expr = event_expr_from_payload(view, offset);
+               if (!array_field_expr) {
+                       goto error;
+               }
+
+               /* Move ownership of `array_field_expr` to new expression */
+               expr = lttng_event_expr_array_field_element_create(
+                               array_field_expr, (unsigned int) index);
+               if (!expr) {
+                       /* `array_field_expr` not moved: destroy it */
+                       lttng_event_expr_destroy(array_field_expr);
+               }
+
+               break;
+       }
+       default:
+               abort();
+       }
+
+       goto end;
+
+error:
+       lttng_event_expr_destroy(expr);
+       expr = NULL;
+
+end:
+       return expr;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_event_rule_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **_condition)
+{
+       ssize_t consumed_length;
+       size_t offset = 0;
+       ssize_t size;
+       uint64_t capture_descr_count;
+       uint64_t i;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+
+       if (!view || !_condition) {
+               goto error;
+       }
+
+       /* Struct lttng_event_rule */
+       {
+               struct lttng_payload_view event_rule_view =
+                               lttng_payload_view_from_view(view, offset, -1);
+               size = lttng_event_rule_create_from_payload(
+                               &event_rule_view, &event_rule);
+       }
+
+       if (size < 0 || !event_rule) {
+               goto error;
+       }
+
+       /* Create condition (no capture descriptors yet) at this point */
+       condition = lttng_condition_event_rule_create(event_rule);
+       if (!condition) {
+               goto error;
+       }
+
+
+       /* Capture descriptor count */
+       assert(size >= 0);
+       offset += (size_t) size;
+       capture_descr_count = uint_from_buffer(&view->buffer, sizeof(uint32_t), &offset);
+       if (capture_descr_count == UINT64_C(-1)) {
+               goto error;
+       }
+
+       /* Capture descriptors */
+       for (i = 0; i < capture_descr_count; i++) {
+               enum lttng_condition_status status;
+               struct lttng_capture_descriptor *desc;
+               struct lttng_event_expr *expr = event_expr_from_payload(
+                               view, &offset);
+               int32_t payload_index = int_from_buffer(&view->buffer, sizeof(int32_t),
+                               &offset);
+
+               if (!expr) {
+                       goto error;
+               }
+
+               /* Move ownership of `expr` to `condition` */
+               status = lttng_condition_event_rule_append_capture_descriptor(
+                               condition, expr);
+               if (status != LTTNG_CONDITION_STATUS_OK) {
+                       /* `expr` not moved: destroy it */
+                       lttng_event_expr_destroy(expr);
+                       goto error;
+               }
+
+               /*
+                * Set the internal payload object for the descriptor. This can
+                * be used by liblttng-ctl to access capture msgpack payload on
+                * the client side.
+                */
+               desc = lttng_condition_event_rule_get_internal_capture_descriptor_at_index(condition, i);
+               if (desc == NULL) {
+                       goto error;
+               }
+               desc->capture_index = payload_index;
+       }
+
+       consumed_length = (ssize_t) offset;
+       *_condition = condition;
+       condition = NULL;
+       goto end;
+
+error:
+       consumed_length = -1;
+
+end:
+       lttng_event_rule_destroy(event_rule);
+       lttng_condition_destroy(condition);
+       return consumed_length;
+}
+
+LTTNG_HIDDEN
+enum lttng_condition_status lttng_condition_event_rule_get_rule_mutable(
+               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 *mutable_rule = NULL;
+       enum lttng_condition_status status;
+
+       status = lttng_condition_event_rule_get_rule_mutable(
+                       condition, &mutable_rule);
+       *rule = mutable_rule;
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_event_rule_append_capture_descriptor(
+               struct lttng_condition *condition,
+               struct lttng_event_expr *expr)
+{
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+       struct lttng_condition_event_rule *event_rule_cond =
+                       container_of(condition,
+                               struct lttng_condition_event_rule, parent);
+       int ret;
+       struct lttng_capture_descriptor *descriptor = NULL;
+       const struct lttng_event_rule *rule = NULL;
+
+       /* Only accept l-values */
+       if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !expr ||
+                       !lttng_event_expr_is_lvalue(expr)) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       status = lttng_condition_event_rule_get_rule(condition, &rule);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto end;
+       }
+
+       switch(lttng_event_rule_get_type(rule)) {
+       case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+       case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+               /* Supported */
+               status = LTTNG_CONDITION_STATUS_OK;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               break;
+       default:
+               status = LTTNG_CONDITION_STATUS_UNSUPPORTED;
+               break;
+       }
+
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto end;
+       }
+
+       descriptor = malloc(sizeof(*descriptor));
+       if (descriptor == NULL) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       descriptor->capture_index = -1;
+       descriptor->event_expression = expr;
+
+       ret = lttng_dynamic_pointer_array_add_pointer(
+                       &event_rule_cond->capture_descriptors, descriptor);
+       if (ret) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Ownership is transfered to the internal capture_descriptors array */
+       descriptor = NULL;
+end:
+       free(descriptor);
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_event_rule_get_capture_descriptor_count(
+               const struct lttng_condition *condition, unsigned int *count)
+{
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+       const struct lttng_condition_event_rule *event_rule_cond =
+                       container_of(condition,
+                               const struct lttng_condition_event_rule,
+                               parent);
+
+       if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !count) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       *count = lttng_dynamic_pointer_array_get_count(
+                       &event_rule_cond->capture_descriptors);
+
+end:
+       return status;
+}
+
+const struct lttng_event_expr *
+lttng_condition_event_rule_get_capture_descriptor_at_index(
+               const struct lttng_condition *condition, unsigned int index)
+{
+       const struct lttng_event_expr *expr = NULL;
+       const struct lttng_capture_descriptor *desc = NULL;
+
+       desc = lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+                       condition, index);
+       if (desc == NULL) {
+               goto end;
+       }
+       expr = desc->event_expression;
+
+end:
+       return expr;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_event_rule_create_from_payload(
+               const struct lttng_condition_event_rule *condition,
+               struct lttng_payload_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->buffer.data;
+       uint32_t capture_payload_size;
+       const char *capture_payload = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       if (view->buffer.size < sizeof(*comm)) {
+               ret = -1;
+               goto error;
+       }
+
+       /* Map the name, view of the payload */
+       offset += sizeof(*comm);
+       {
+               struct lttng_payload_view current_view =
+                               lttng_payload_view_from_view(view, offset,
+                                               comm->trigger_name_length);
+               name = current_view.buffer.data;
+               if (!name) {
+                       ret = -1;
+                       goto error;
+               }
+
+               if (!lttng_buffer_view_contains_string(&current_view.buffer,
+                                   name, comm->trigger_name_length)) {
+                       ret = -1;
+                       goto error;
+               }
+       }
+
+       offset += comm->trigger_name_length;
+       {
+               struct lttng_payload_view current_view = lttng_payload_view_from_view(view, offset, -1);
+
+               if (current_view.buffer.size < sizeof(capture_payload_size)) {
+                       ret = -1;
+                       goto error;
+               }
+
+               memcpy(&capture_payload_size, current_view.buffer.data,
+                               sizeof(capture_payload_size));
+       }
+       offset += sizeof(capture_payload_size);
+
+       if (capture_payload_size > 0) {
+               struct lttng_payload_view current_view = lttng_payload_view_from_view(view, offset, -1);
+
+               if (current_view.buffer.size < capture_payload_size) {
+                       ret = -1;
+                       goto error;
+               }
+
+               capture_payload = current_view.buffer.data;
+       }
+
+       evaluation = lttng_evaluation_event_rule_create(condition, name,
+                       capture_payload, capture_payload_size, true);
+       if (!evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       offset += capture_payload_size;
+       *_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_payload *payload)
+{
+       int ret = 0;
+       struct lttng_evaluation_event_rule *hit;
+       struct lttng_evaluation_event_rule_comm comm;
+       uint32_t capture_payload_size;
+
+       hit = container_of(
+                       evaluation, struct lttng_evaluation_event_rule, parent);
+       comm.trigger_name_length = strlen(hit->name) + 1;
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, hit->name, comm.trigger_name_length);
+       if (ret) {
+               goto end;
+       }
+
+       capture_payload_size = (uint32_t) hit->capture_payload.size;
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_payload_size,
+                       sizeof(capture_payload_size));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, hit->capture_payload.data,
+                       hit->capture_payload.size);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+bool msgpack_str_is_equal(const struct msgpack_object *obj, const char *str)
+{
+       bool is_equal = true;
+
+       assert(obj->type == MSGPACK_OBJECT_STR);
+
+       if (obj->via.str.size != strlen(str)) {
+               is_equal = false;
+               goto end;
+       }
+
+       if (strncmp(obj->via.str.ptr, str, obj->via.str.size) != 0) {
+               is_equal = false;
+               goto end;
+       }
+
+end:
+       return is_equal;
+}
+
+static
+const msgpack_object *get_msgpack_map_obj(const struct msgpack_object *map_obj,
+               const char *name)
+{
+       const msgpack_object *ret = NULL;
+       size_t i;
+
+       assert(map_obj->type == MSGPACK_OBJECT_MAP);
+
+       for (i = 0; i < map_obj->via.map.size; i++) {
+               const struct msgpack_object_kv *kv = &map_obj->via.map.ptr[i];
+
+               assert(kv->key.type == MSGPACK_OBJECT_STR);
+
+               if (msgpack_str_is_equal(&kv->key, name)) {
+                       ret = &kv->val;
+                       goto end;
+               }
+       }
+
+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);
+       lttng_dynamic_buffer_reset(&hit->capture_payload);
+       if (hit->captured_values) {
+               lttng_event_field_value_destroy(hit->captured_values);
+       }
+       free(hit);
+}
+
+static
+int event_field_value_from_obj(const msgpack_object *obj,
+               struct lttng_event_field_value **field_val)
+{
+       assert(obj);
+       assert(field_val);
+       int ret = 0;
+
+       switch (obj->type) {
+       case MSGPACK_OBJECT_NIL:
+               /* Unavailable */
+               *field_val = NULL;
+               goto end;
+       case MSGPACK_OBJECT_POSITIVE_INTEGER:
+               *field_val = lttng_event_field_value_uint_create(
+                               obj->via.u64);
+               break;
+       case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+               *field_val = lttng_event_field_value_int_create(
+                               obj->via.i64);
+               break;
+       case MSGPACK_OBJECT_FLOAT32:
+       case MSGPACK_OBJECT_FLOAT64:
+               *field_val = lttng_event_field_value_real_create(
+                               obj->via.f64);
+               break;
+       case MSGPACK_OBJECT_STR:
+               *field_val = lttng_event_field_value_string_create_with_size(
+                               obj->via.str.ptr, obj->via.str.size);
+               break;
+       case MSGPACK_OBJECT_ARRAY:
+       {
+               size_t i;
+
+               *field_val = lttng_event_field_value_array_create();
+               if (!*field_val) {
+                       goto error;
+               }
+
+               for (i = 0; i < obj->via.array.size; i++) {
+                       const msgpack_object *elem_obj = &obj->via.array.ptr[i];
+                       struct lttng_event_field_value *elem_field_val;
+
+                       ret = event_field_value_from_obj(elem_obj,
+                                       &elem_field_val);
+
+                       if (ret) {
+                               goto error;
+                       }
+
+                       if (elem_field_val) {
+                               ret = lttng_event_field_value_array_append(
+                                               *field_val, elem_field_val);
+                       } else {
+                               ret = lttng_event_field_value_array_append_unavailable(
+                                               *field_val);
+                       }
+
+                       if (ret) {
+                               lttng_event_field_value_destroy(elem_field_val);
+                               goto error;
+                       }
+               }
+
+               break;
+       }
+       case MSGPACK_OBJECT_MAP:
+       {
+               /*
+                * As of this version, the only valid map object is
+                * for an enumeration value, for example:
+                *
+                *     type: enum
+                *     value: 177
+                *     labels:
+                *     - Labatt 50
+                *     - Molson Dry
+                *     - Carling Black Label
+                */
+               const msgpack_object *inner_obj;
+               size_t label_i;
+
+               inner_obj = get_msgpack_map_obj(obj, "type");
+               if (!inner_obj) {
+                       ERR("Missing `type` entry in map object.");
+                       goto error;
+               }
+
+               if (inner_obj->type != MSGPACK_OBJECT_STR) {
+                       ERR("Map object's `type` entry is not a string (it's a %d).",
+                                       inner_obj->type);
+                       goto error;
+               }
+
+               if (!msgpack_str_is_equal(inner_obj, "enum")) {
+                       ERR("Map object's `type` entry: expecting `enum`.");
+                       goto error;
+               }
+
+               inner_obj = get_msgpack_map_obj(obj, "value");
+               if (!inner_obj) {
+                       ERR("Missing `value` entry in map object.");
+                       goto error;
+               }
+
+               if (inner_obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER) {
+                       *field_val = lttng_event_field_value_enum_uint_create(
+                                       inner_obj->via.u64);
+               } else if (inner_obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) {
+                       *field_val = lttng_event_field_value_enum_int_create(
+                                       inner_obj->via.i64);
+               } else {
+                       ERR("Map object's `value` entry is not an integer (it's a %d).",
+                                       inner_obj->type);
+                       goto error;
+               }
+
+               if (!*field_val) {
+                       goto error;
+               }
+
+               inner_obj = get_msgpack_map_obj(obj, "labels");
+               if (!inner_obj) {
+                       /* No labels */
+                       goto end;
+               }
+
+               if (inner_obj->type != MSGPACK_OBJECT_ARRAY) {
+                       ERR("Map object's `labels` entry is not an array (it's a %d).",
+                                       inner_obj->type);
+                       goto error;
+               }
+
+               for (label_i = 0; label_i < inner_obj->via.array.size;
+                               label_i++) {
+                       int iret;
+                       const msgpack_object *elem_obj =
+                                       &inner_obj->via.array.ptr[label_i];
+
+                       if (elem_obj->type != MSGPACK_OBJECT_STR) {
+                               ERR("Map object's `labels` entry's type is not a string (it's a %d).",
+                                               elem_obj->type);
+                               goto error;
+                       }
+
+                       iret = lttng_event_field_value_enum_append_label_with_size(
+                                       *field_val, elem_obj->via.str.ptr,
+                                       elem_obj->via.str.size);
+                       if (iret) {
+                               goto error;
+                       }
+               }
+
+               break;
+       }
+       default:
+               ERR("Unexpected object type %d.", obj->type);
+               goto error;
+       }
+
+       if (!*field_val) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(*field_val);
+       *field_val = NULL;
+       ret = -1;
+
+end:
+       return ret;
+}
+
+static
+struct lttng_event_field_value *event_field_value_from_capture_payload(
+               const struct lttng_condition_event_rule *condition,
+               const char *capture_payload, size_t capture_payload_size)
+{
+       struct lttng_event_field_value *ret = NULL;
+       msgpack_unpacked unpacked;
+       msgpack_unpack_return unpack_return;
+       const msgpack_object *root_obj;
+       const msgpack_object_array *root_array_obj;
+       size_t i;
+       size_t count;
+
+       assert(condition);
+       assert(capture_payload);
+
+       /* Initialize value */
+       msgpack_unpacked_init(&unpacked);
+
+       /* Decode */
+       unpack_return = msgpack_unpack_next(&unpacked, capture_payload,
+                       capture_payload_size, NULL);
+       if (unpack_return != MSGPACK_UNPACK_SUCCESS) {
+               ERR("msgpack_unpack_next() failed to decode the "
+                               "MessagePack-encoded capture payload "
+                               "(size %zu); returned %d.",
+                               capture_payload_size, unpack_return);
+               goto error;
+       }
+
+       /* Get root array */
+       root_obj = &unpacked.data;
+
+       if (root_obj->type != MSGPACK_OBJECT_ARRAY) {
+               ERR("Expecting an array as the root object; got type %d.",
+                               root_obj->type);
+               goto error;
+       }
+
+       root_array_obj = &root_obj->via.array;
+
+       /* Create an empty root array event field value */
+       ret = lttng_event_field_value_array_create();
+       if (!ret) {
+               goto error;
+       }
+
+       /*
+        * For each capture descriptor in the condition object:
+        *
+        * 1. Get its corresponding captured field value MessagePack
+        *    object.
+        *
+        * 2. Create a corresponding event field value.
+        *
+        * 3. Append it to `ret` (the root array event field value).
+        */
+       count = lttng_dynamic_pointer_array_get_count(
+                       &condition->capture_descriptors);
+       assert(count > 0);
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_capture_descriptor *capture_descriptor =
+                               lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+                                               &condition->parent, i);
+               const msgpack_object *elem_obj;
+               struct lttng_event_field_value *elem_field_val;
+               int iret;
+
+               assert(capture_descriptor);
+               assert(capture_descriptor->capture_index >= 0);
+
+               if (capture_descriptor->capture_index >= root_array_obj->size) {
+                       ERR("Root array object of size %u does not have enough "
+                                       "elements for the capture index %u "
+                                       "(for capture descriptor #%zu).",
+                                       (unsigned int) root_array_obj->size,
+                                       (unsigned int) capture_descriptor->capture_index,
+                                       i);
+                       goto error;
+               }
+
+               elem_obj = &root_array_obj->ptr[(size_t) capture_descriptor->capture_index];
+               iret = event_field_value_from_obj(elem_obj,
+                               &elem_field_val);
+               if (iret) {
+                       goto error;
+               }
+
+               if (elem_field_val) {
+                       iret = lttng_event_field_value_array_append(ret,
+                                       elem_field_val);
+               } else {
+                       iret = lttng_event_field_value_array_append_unavailable(
+                                       ret);
+               }
+
+               if (iret) {
+                       lttng_event_field_value_destroy(elem_field_val);
+                       goto error;
+               }
+       }
+
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(ret);
+       ret = NULL;
+
+end:
+       msgpack_unpacked_destroy(&unpacked);
+       return ret;
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_event_rule_create(
+               const struct lttng_condition_event_rule *condition,
+               const char *trigger_name,
+               const char *capture_payload, size_t capture_payload_size,
+               bool decode_capture_payload)
+{
+       struct lttng_evaluation_event_rule *hit;
+       struct lttng_evaluation *evaluation = NULL;
+
+       hit = zmalloc(sizeof(struct lttng_evaluation_event_rule));
+       if (!hit) {
+               goto error;
+       }
+
+       hit->name = strdup(trigger_name);
+       if (!hit->name) {
+               goto error;
+       }
+
+       lttng_dynamic_buffer_init(&hit->capture_payload);
+
+       if (capture_payload) {
+               lttng_dynamic_buffer_append(&hit->capture_payload,
+                               capture_payload, capture_payload_size);
+
+               if (decode_capture_payload) {
+                       hit->captured_values =
+                                       event_field_value_from_capture_payload(
+                                               condition,
+                                               capture_payload,
+                                               capture_payload_size);
+                       if (!hit->captured_values) {
+                               ERR("Failed to decode the capture payload (size %zu).",
+                                               capture_payload_size);
+                               goto error;
+                       }
+               }
+       }
+
+       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;
+
+       evaluation = &hit->parent;
+       hit = NULL;
+
+error:
+       if (hit) {
+               lttng_evaluation_event_rule_destroy(&hit->parent);
+       }
+
+       return evaluation;
+}
+
+enum lttng_evaluation_status lttng_evaluation_get_captured_values(
+               const struct lttng_evaluation *evaluation,
+               const struct lttng_event_field_value **field_val)
+{
+       struct lttng_evaluation_event_rule *hit;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !is_event_rule_evaluation(evaluation) ||
+                       !field_val) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       hit = container_of(evaluation, struct lttng_evaluation_event_rule,
+                       parent);
+       if (!hit->captured_values) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       *field_val = hit->captured_values;
+
+end:
+       return status;
+}
+
+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;
+}
+
+LTTNG_HIDDEN
+enum lttng_error_code
+lttng_condition_event_rule_generate_capture_descriptor_bytecode_set(
+               struct lttng_condition *condition,
+               struct lttng_dynamic_pointer_array *bytecode_set)
+{
+       enum lttng_error_code ret;
+       enum lttng_condition_status status;
+       unsigned int capture_count;
+       const struct lttng_condition_event_rule_capture_bytecode_element *set_element;
+       struct lttng_capture_descriptor *local_capture_desc;
+       ssize_t set_count;
+       struct lttng_condition_event_rule_capture_bytecode_element *set_element_to_append =
+                       NULL;
+       struct lttng_bytecode *bytecode = NULL;
+
+       if (!condition || !IS_EVENT_RULE_CONDITION(condition) ||
+                       !bytecode_set) {
+               ret = LTTNG_ERR_FATAL;
+               goto end;
+       }
+
+       status = lttng_condition_event_rule_get_capture_descriptor_count(
+                       condition, &capture_count);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ret = LTTNG_ERR_FATAL;
+               goto end;
+       }
+
+       /*
+        * O(n^2), don't care. This code path is not hot.
+        * Before inserting into the set, validate that the expression is not
+        * already present in it.
+        */
+       for (unsigned int i = 0; i < capture_count; i++) {
+               int found_in_set = false;
+
+               set_count = lttng_dynamic_pointer_array_get_count(bytecode_set);
+
+               local_capture_desc =
+                               lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+                                               condition, i);
+               if (local_capture_desc == NULL) {
+                       ret = LTTNG_ERR_FATAL;
+                       goto end;
+               }
+
+               /*
+                * Iterate over the set to check if already present in the set.
+                */
+               for (ssize_t j = 0; j < set_count; j++) {
+                       set_element = (struct lttng_condition_event_rule_capture_bytecode_element
+                                                       *)
+                                       lttng_dynamic_pointer_array_get_pointer(
+                                                       bytecode_set, j);
+                       if (set_element == NULL) {
+                               ret = LTTNG_ERR_FATAL;
+                               goto end;
+                       }
+
+                       if (!lttng_event_expr_is_equal(
+                                           local_capture_desc->event_expression,
+                                           set_element->expression)) {
+                               /* Check against next set element */
+                               continue;
+                       }
+
+                       /*
+                        * Already present in the set, assign the
+                        * capture index of the capture descriptor for
+                        * future use.
+                        */
+                       found_in_set = true;
+                       local_capture_desc->capture_index = j;
+                       /* Exit inner loop */
+                       break;
+               }
+
+               if (found_in_set) {
+                       /* Process next local capture descriptor */
+                       continue;
+               }
+
+               /*
+                * Not found in the set.
+                * Insert the capture descriptor in the set.
+                */
+               set_element_to_append = malloc(sizeof(*set_element_to_append));
+               if (set_element_to_append == NULL) {
+                       ret = LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+
+               /* Generate the bytecode */
+               status = lttng_event_expr_to_bytecode(
+                               local_capture_desc->event_expression,
+                               &bytecode);
+               if (status < 0 || bytecode == NULL) {
+                       /* TODO: return pertinent capture related error code */
+                       ret = LTTNG_ERR_FILTER_INVAL;
+                       goto end;
+               }
+
+               set_element_to_append->bytecode = bytecode;
+
+               /* 
+                * Ensure the lifetime of the event expression.
+                * Our reference will be put on condition destroy.
+                */
+               lttng_event_expr_get(local_capture_desc->event_expression);
+               set_element_to_append->expression =
+                               local_capture_desc->event_expression;
+
+               ret = lttng_dynamic_pointer_array_add_pointer(
+                               bytecode_set, set_element_to_append);
+               if (ret < 0) {
+                       ret = LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+
+               /* Ownership tranfered to the bytecode set */
+               set_element_to_append = NULL;
+               bytecode = NULL;
+
+               /* Assign the capture descriptor for future use */
+               local_capture_desc->capture_index = set_count;
+       }
+
+       /* Everything went better than expected */
+       ret = LTTNG_OK;
+
+end:
+       free(set_element_to_append);
+       free(bytecode);
+       return ret;
+}
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..0461073
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * 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_payload *payload)
+{
+       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(&payload->buffer, &consumed_comm,
+                       sizeof(consumed_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, 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_payload(struct lttng_condition *condition,
+               struct lttng_payload_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->buffer.size < sizeof(*condition_comm)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm = (typeof(condition_comm)) src_view->buffer.data;
+       names_view = lttng_buffer_view_from_view(&src_view->buffer,
+                       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_payload(
+               struct lttng_payload_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_payload(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_payload(
+               const struct lttng_payload_view *view)
+{
+       const struct lttng_evaluation_session_consumed_size_comm *comm =
+                       (typeof(comm)) view->buffer.data;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (view->buffer.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_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       evaluation = create_evaluation_from_payload(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_payload *payload)
+{
+       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(
+                       &payload->buffer, &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..f6849d2
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * 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_payload *payload);
+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_payload *payload);
+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_payload *payload)
+{
+       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(&payload->buffer, &rotation_comm,
+                       sizeof(rotation_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       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_payload(struct lttng_condition *condition,
+               struct lttng_payload_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->buffer.size < sizeof(*condition_comm)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm = (typeof(condition_comm)) src_view->buffer.data;
+       name_view = lttng_buffer_view_from_view(&src_view->buffer,
+                       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_payload(
+               struct lttng_payload_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_payload(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_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **condition)
+{
+       return lttng_condition_session_rotation_create_from_payload(view,
+                       condition,
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_session_rotation_completed_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **condition)
+{
+       return lttng_condition_session_rotation_create_from_payload(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_payload(
+               enum lttng_condition_type type,
+               struct lttng_payload_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 =
+                       (typeof(comm)) view->buffer.data;
+       struct lttng_buffer_view location_view;
+
+       if (view->buffer.size < sizeof(*comm)) {
+               goto error;
+       }
+
+       size = sizeof(*comm);
+       if (comm->has_location) {
+               location_view = lttng_buffer_view_from_view(
+                               &view->buffer, 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_payload(
+               enum lttng_condition_type type,
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = create_evaluation_from_payload(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_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **evaluation)
+{
+       return lttng_evaluation_session_rotation_create_from_payload(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING,
+                       view, evaluation);
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_session_rotation_completed_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **evaluation)
+{
+       return lttng_evaluation_session_rotation_create_from_payload(
+                       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_payload *payload)
+{
+       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(
+                       &payload->buffer, &comm, sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+       if (!rotation->location) {
+               goto end;
+       }
+       ret = lttng_trace_archive_location_serialize(rotation->location,
+                       &payload->buffer);
+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 dc1bdd89f1182c984bbdf5ecbba4a8d51cddaf7d..589ffabc26f1ea7ac965ba70f702853fd3cfc2ee 100644 (file)
@@ -9,15 +9,60 @@
 #include <stdbool.h>
 #include "credentials.h"
 
 #include <stdbool.h>
 #include "credentials.h"
 
-bool lttng_credentials_is_equal(const struct lttng_credentials *a,
+uid_t lttng_credentials_get_uid(const struct lttng_credentials *creds)
+{
+       return LTTNG_OPTIONAL_GET(creds->uid);
+}
+
+gid_t lttng_credentials_get_gid(const struct lttng_credentials *creds)
+{
+       return LTTNG_OPTIONAL_GET(creds->gid);
+}
+
+bool lttng_credentials_is_equal_uid(const struct lttng_credentials *a,
                const struct lttng_credentials *b)
 {
        assert(a);
        assert(b);
 
                const struct lttng_credentials *b)
 {
        assert(a);
        assert(b);
 
-       if ((a->uid != b->uid) || (a->gid != b->gid)) {
+       /* XOR on the is_set value */
+       if (!!a->uid.is_set != !!b->uid.is_set) {
                return false;
        }
 
                return false;
        }
 
-       return true;
-};
+       if (!a->uid.is_set && !b->uid.is_set) {
+               return true;
+       }
+
+       /* Both a and b are set. */
+       return a->uid.value == b->uid.value;
+}
+
+bool lttng_credentials_is_equal_gid(const struct lttng_credentials *a,
+               const struct lttng_credentials *b)
+{
+       assert(a);
+       assert(b);
+
+       /* XOR on the is_set value */
+       if (!!a->gid.is_set != !!b->gid.is_set) {
+               return false;
+       }
+
+       if (!a->gid.is_set && !b->gid.is_set) {
+               return true;
+       }
+
+       /* Both a and b are set. */
+       return a->gid.value == b->gid.value;
+}
+
+bool lttng_credentials_is_equal(const struct lttng_credentials *a,
+               const struct lttng_credentials *b)
+{
+       assert(a);
+       assert(b);
+
+       return lttng_credentials_is_equal_uid(a, b) &&
+                       lttng_credentials_is_equal_gid(a, b);
+}
index 9e5fc62608e9f828fadf8f6c37e637a7353c618e..c0dc6d7f14dfc6d7035088d3ad7eeb4ee0eaf59f 100644 (file)
 #include <sys/types.h>
 #include <stdbool.h>
 
 #include <sys/types.h>
 #include <stdbool.h>
 
+#include "optional.h"
+
 struct lttng_credentials {
 struct lttng_credentials {
-       uid_t uid;
-       gid_t gid;
+       LTTNG_OPTIONAL(uid_t) uid;
+       LTTNG_OPTIONAL(gid_t) gid;
 };
 
 };
 
+uid_t lttng_credentials_get_uid(const struct lttng_credentials *creds);
+gid_t lttng_credentials_get_gid(const struct lttng_credentials *creds);
+
+bool lttng_credentials_is_equal_uid(const struct lttng_credentials *a,
+               const struct lttng_credentials *b);
+
+bool lttng_credentials_is_equal_gid(const struct lttng_credentials *a,
+               const struct lttng_credentials *b);
+
 bool lttng_credentials_is_equal(const struct lttng_credentials *a,
                const struct lttng_credentials *b);
 
 bool lttng_credentials_is_equal(const struct lttng_credentials *a,
                const struct lttng_credentials *b);
 
diff --git a/src/common/domain.c b/src/common/domain.c
new file mode 100644 (file)
index 0000000..277fc80
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#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 0b488a39cd52f95b785f2f3a52ee48f6c3e9cd0b..71cf9af189c97a8ef6d30ffabce1f9ba0946928b 100644 (file)
@@ -140,6 +140,23 @@ void *lttng_dynamic_pointer_array_get_pointer(
        return *element;
 }
 
        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
 /*
  * 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 11dbc90289806decc6507f11f94727ab9e0476a1..c64bfd7104d45d2a7174abb31cdd08683a4b82cd 100644 (file)
@@ -239,6 +239,9 @@ static const char *error_string_array[] = {
        [ ERROR_INDEX(LTTNG_ERR_GROUP_NOT_FOUND) ] = "Group not found",
        [ ERROR_INDEX(LTTNG_ERR_UNSUPPORTED_DOMAIN) ] = "Unsupported domain used",
        [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) ] = "Operation does not apply to the process attribute tracker's tracking policy",
        [ ERROR_INDEX(LTTNG_ERR_GROUP_NOT_FOUND) ] = "Group not found",
        [ ERROR_INDEX(LTTNG_ERR_UNSUPPORTED_DOMAIN) ] = "Unsupported domain used",
        [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) ] = "Operation does not apply to the process attribute tracker's tracking policy",
+       [ ERROR_INDEX(LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD) ] = "Failed to create a trigger group notification file descriptor",
+       [ ERROR_INDEX(LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER) ] = "Failed to create a trigger group error counter",
+       [ ERROR_INDEX(LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER_FULL) ] = "Trigger group error counter full",
 
        /* Last element */
        [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code"
 
        /* Last element */
        [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code"
index e936bdd91db3b12f1aeab97603df36649529b4f1..479b8b3e51edab7ddf18f9f3d31847f77710c99a 100644 (file)
@@ -5,10 +5,12 @@
  *
  */
 
  *
  */
 
+#include <lttng/condition/condition-internal.h>
 #include <lttng/condition/evaluation-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/evaluation-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 <common/macros.h>
 #include <common/error.h>
 #include <stdbool.h>
 #include <common/macros.h>
 #include <common/error.h>
 #include <stdbool.h>
@@ -48,6 +50,7 @@ end:
 
 LTTNG_HIDDEN
 ssize_t lttng_evaluation_create_from_payload(
 
 LTTNG_HIDDEN
 ssize_t lttng_evaluation_create_from_payload(
+               const struct lttng_condition *condition,
                struct lttng_payload_view *src_view,
                struct lttng_evaluation **evaluation)
 {
                struct lttng_payload_view *src_view,
                struct lttng_evaluation **evaluation)
 {
@@ -107,6 +110,19 @@ ssize_t lttng_evaluation_create_from_payload(
                }
                evaluation_size += ret;
                break;
                }
                evaluation_size += ret;
                break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+               assert(condition);
+               assert(condition->type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+               ret = lttng_evaluation_event_rule_create_from_payload(
+                               container_of(condition,
+                                               struct lttng_condition_event_rule,
+                                               parent),
+                               &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);
        default:
                ERR("Attempted to create evaluation of unknown type (%i)",
                                (int) evaluation_comm->type);
diff --git a/src/common/event-expr-to-bytecode.c b/src/common/event-expr-to-bytecode.c
new file mode 100644 (file)
index 0000000..05eccb7
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "event-expr-to-bytecode.h"
+
+#include <stdio.h>
+#include <lttng/event-expr.h>
+#include <common/bytecode/bytecode.h>
+
+
+static
+int event_expr_to_bytecode_recursive(const struct lttng_event_expr *expr,
+               struct lttng_bytecode_alloc **bytecode,
+               struct lttng_bytecode_alloc **bytecode_reloc)
+{
+       enum lttng_event_expr_status event_expr_status;
+       int status;
+
+       switch (lttng_event_expr_get_type(expr)) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+       {
+               const char *name;
+
+               status = bytecode_push_get_payload_root(bytecode);
+               if (status) {
+                       goto end;
+               }
+
+               name = lttng_event_expr_event_payload_field_get_name(expr);
+               if (!name) {
+                       status = -1;
+                       goto end;
+               }
+
+               status = bytecode_push_get_symbol(bytecode, bytecode_reloc, name);
+               if (status) {
+                       goto end;
+               }
+
+               break;
+       }
+
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+       {
+               const char *name;
+
+               status = bytecode_push_get_context_root(bytecode);
+               if (status) {
+                       goto end;
+               }
+
+               name = lttng_event_expr_channel_context_field_get_name(expr);
+               if (!name) {
+                       status = -1;
+                       goto end;
+               }
+
+               status = bytecode_push_get_symbol(bytecode, bytecode_reloc, name);
+               if (status) {
+                       goto end;
+               }
+
+               break;
+       }
+
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               const char *provider_name, *type_name;
+               char *name = NULL;
+               int ret;
+
+               status = bytecode_push_get_app_context_root(bytecode);
+               if (status) {
+                       goto end;
+               }
+
+               provider_name = lttng_event_expr_app_specific_context_field_get_provider_name(expr);
+               if (!provider_name) {
+                       status = -1;
+                       goto end;
+               }
+
+               type_name = lttng_event_expr_app_specific_context_field_get_type_name(expr);
+               if (!type_name) {
+                       status = -1;
+                       goto end;
+               }
+
+               /* Reconstitute the app context field name from its two parts.  */
+               ret = asprintf(&name, "%s:%s", provider_name, type_name);
+               if (ret < 0) {
+                       status = -1;
+                       goto end;
+               }
+
+               status = bytecode_push_get_symbol(bytecode, bytecode_reloc, name);
+               free(name);
+               if (status) {
+                       goto end;
+               }
+
+               break;
+       }
+
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               const struct lttng_event_expr *parent;
+               unsigned int index;
+
+               parent = lttng_event_expr_array_field_element_get_parent_expr(expr);
+               if (!parent) {
+                       status = -1;
+                       goto end;
+               }
+
+               status = event_expr_to_bytecode_recursive(parent, bytecode, bytecode_reloc);
+               if (status) {
+                       goto end;
+               }
+
+               event_expr_status = lttng_event_expr_array_field_element_get_index(
+                       expr, &index);
+               if (event_expr_status != LTTNG_EVENT_EXPR_STATUS_OK) {
+                       status = -1;
+                       goto end;
+               }
+
+               status = bytecode_push_get_index_u64(bytecode, index);
+               if (status) {
+                       goto end;
+               }
+
+               break;
+       }
+
+       default:
+               abort();
+       }
+
+       status = 0;
+end:
+       return status;
+}
+
+LTTNG_HIDDEN
+int lttng_event_expr_to_bytecode(const struct lttng_event_expr *expr,
+               struct lttng_bytecode **bytecode_out)
+{
+       struct lttng_bytecode_alloc *bytecode = NULL;
+       struct lttng_bytecode_alloc *bytecode_reloc = NULL;
+       struct return_op ret_insn;
+       int status;
+
+       status = bytecode_init(&bytecode);
+       if (status) {
+               goto end;
+       }
+
+       status = bytecode_init(&bytecode_reloc);
+       if (status) {
+               goto end;
+       }
+
+       status = event_expr_to_bytecode_recursive (expr, &bytecode, &bytecode_reloc);
+       if (status) {
+               goto end;
+       }
+
+       ret_insn.op = BYTECODE_OP_RETURN;
+       bytecode_push(&bytecode, &ret_insn, 1, sizeof(ret_insn));
+
+       /* Append symbol table to bytecode. */
+       bytecode->b.reloc_table_offset = bytecode_get_len(&bytecode->b);
+       status = bytecode_push(&bytecode, bytecode_reloc->b.data,
+               1, bytecode_get_len(&bytecode_reloc->b));
+       if (status) {
+               goto end;
+       }
+
+       /* Copy the `lttng_bytecode` out of the `lttng_bytecode_alloc`.  */
+       *bytecode_out = bytecode_copy(&bytecode->b);
+       if (!*bytecode_out) {
+               status = -1;
+               goto end;
+       }
+
+end:
+       if (bytecode) {
+               free(bytecode);
+       }
+
+       if (bytecode_reloc) {
+               free(bytecode_reloc);
+       }
+
+       return status;
+}
diff --git a/src/common/event-expr-to-bytecode.h b/src/common/event-expr-to-bytecode.h
new file mode 100644 (file)
index 0000000..9d7d57a
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef SRC_COMMON_EVENT_EXPR_TO_BYTECODE_H
+#define SRC_COMMON_EVENT_EXPR_TO_BYTECODE_H
+
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/macros.h>
+
+struct lttng_bytecode;
+struct lttng_event_expr;
+
+LTTNG_HIDDEN
+int lttng_event_expr_to_bytecode (const struct lttng_event_expr *expr,
+               struct lttng_bytecode **bytecode_out);
+
+#endif /* SRC_COMMON_EVENT_EXPR_TO_BYTECODE_H */
diff --git a/src/common/event-expr.c b/src/common/event-expr.c
new file mode 100644 (file)
index 0000000..10f109c
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * event-expr.c
+ *
+ * Linux Trace Toolkit Control Library
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <assert.h>
+#include <stddef.h>
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/event-expr-internal.h>
+
+enum lttng_event_expr_type lttng_event_expr_get_type(
+               const struct lttng_event_expr *expr)
+{
+       enum lttng_event_expr_type type;
+
+       if (!expr) {
+               type = LTTNG_EVENT_EXPR_TYPE_INVALID;
+               goto end;
+       }
+
+       type = expr->type;
+
+end:
+       return type;
+}
+
+static
+struct lttng_event_expr *create_empty_expr(enum lttng_event_expr_type type,
+               size_t size)
+{
+       struct lttng_event_expr *expr;
+
+       expr = zmalloc(size);
+       if (!expr) {
+               goto end;
+       }
+
+       urcu_ref_init(&expr->ref);
+       expr->type = type;
+
+end:
+       return expr;
+}
+
+static
+struct lttng_event_expr_field *create_field_event_expr(
+               enum lttng_event_expr_type type,
+               const char *name)
+{
+       struct lttng_event_expr_field *expr =
+                       container_of(
+                               create_empty_expr(type, sizeof(*expr)),
+                               struct lttng_event_expr_field, parent);
+
+       if (!expr) {
+               goto error;
+       }
+
+       assert(name);
+       expr->name = strdup(name);
+       if (!expr->name) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_event_expr_destroy(&expr->parent);
+
+end:
+       return expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_event_payload_field_create(
+               const char *field_name)
+{
+       struct lttng_event_expr *expr = NULL;
+
+       if (!field_name) {
+               goto end;
+       }
+
+       expr = &create_field_event_expr(
+                       LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD,
+                       field_name)->parent;
+
+end:
+       return expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_channel_context_field_create(
+               const char *field_name)
+{
+       struct lttng_event_expr *expr = NULL;
+
+       if (!field_name) {
+               goto end;
+       }
+
+       expr = &create_field_event_expr(
+                       LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD,
+                       field_name)->parent;
+
+end:
+       return expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_app_specific_context_field_create(
+               const char *provider_name, const char *type_name)
+{
+       struct lttng_event_expr_app_specific_context_field *expr = NULL;
+
+       if (!type_name || !provider_name) {
+               goto error;
+       }
+
+       expr = container_of(create_empty_expr(
+                       LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD,
+                       sizeof(*expr)),
+                       struct lttng_event_expr_app_specific_context_field,
+                       parent);
+       if (!expr) {
+               goto error;
+       }
+
+       expr->provider_name = strdup(provider_name);
+       if (!expr->provider_name) {
+               goto error;
+       }
+
+       expr->type_name = strdup(type_name);
+       if (!expr->type_name) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_event_expr_destroy(&expr->parent);
+
+end:
+       return &expr->parent;
+}
+
+struct lttng_event_expr *lttng_event_expr_array_field_element_create(
+               struct lttng_event_expr *array_field_expr,
+               unsigned int index)
+{
+       struct lttng_event_expr_array_field_element *expr = NULL;
+
+       /* The parent array field expression must be an l-value */
+       if (!array_field_expr ||
+                       !lttng_event_expr_is_lvalue(array_field_expr)) {
+               goto error;
+       }
+
+       expr = container_of(create_empty_expr(
+                       LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT,
+                       sizeof(*expr)),
+                       struct lttng_event_expr_array_field_element,
+                       parent);
+       if (!expr) {
+               goto error;
+       }
+
+       expr->array_field_expr = array_field_expr;
+       expr->index = index;
+       goto end;
+
+error:
+       lttng_event_expr_destroy(&expr->parent);
+
+end:
+       return &expr->parent;
+}
+
+const char *lttng_event_expr_event_payload_field_get_name(
+               const struct lttng_event_expr *expr)
+{
+       const char *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_field, parent)->name;
+
+end:
+       return ret;
+}
+
+const char *lttng_event_expr_channel_context_field_get_name(
+               const struct lttng_event_expr *expr)
+{
+       const char *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_field, parent)->name;
+
+end:
+       return ret;
+}
+
+const char *lttng_event_expr_app_specific_context_field_get_provider_name(
+               const struct lttng_event_expr *expr)
+{
+       const char *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_app_specific_context_field,
+                       parent)->provider_name;
+
+end:
+       return ret;
+}
+
+const char *lttng_event_expr_app_specific_context_field_get_type_name(
+               const struct lttng_event_expr *expr)
+{
+       const char *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_app_specific_context_field,
+                       parent)->type_name;
+
+end:
+       return ret;
+}
+
+const struct lttng_event_expr *
+lttng_event_expr_array_field_element_get_parent_expr(
+               const struct lttng_event_expr *expr)
+{
+       const struct lttng_event_expr *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_array_field_element,
+                       parent)->array_field_expr;
+
+end:
+       return ret;
+}
+
+enum lttng_event_expr_status lttng_event_expr_array_field_element_get_index(
+               const struct lttng_event_expr *expr, unsigned int *index)
+{
+       enum lttng_event_expr_status ret = LTTNG_EVENT_EXPR_STATUS_OK;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT ||
+                       !index) {
+               ret = LTTNG_EVENT_EXPR_STATUS_INVALID;
+               goto end;
+       }
+
+       *index = container_of(expr,
+                       const struct lttng_event_expr_array_field_element,
+                       parent)->index;
+
+end:
+       return ret;
+}
+
+bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a,
+               const struct lttng_event_expr *expr_b)
+{
+       bool is_equal = true;
+
+       if (!expr_a && !expr_b) {
+               /* Both `NULL`: equal */
+               goto end;
+       }
+
+       if (!expr_a || !expr_b) {
+               /* Only one `NULL`: not equal */
+               goto not_equal;
+       }
+
+       if (expr_a->type != expr_b->type) {
+               /* Different types: not equal */
+               goto not_equal;
+       }
+
+       switch (expr_a->type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+       {
+               const struct lttng_event_expr_field *field_expr_a =
+                               container_of(expr_a,
+                                       const struct lttng_event_expr_field,
+                                       parent);
+               const struct lttng_event_expr_field *field_expr_b =
+                               container_of(expr_b,
+                                       const struct lttng_event_expr_field,
+                                       parent);
+
+               if (strcmp(field_expr_a->name, field_expr_b->name) != 0) {
+                       goto not_equal;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               const struct lttng_event_expr_app_specific_context_field *field_expr_a =
+                               container_of(expr_a,
+                                       const struct lttng_event_expr_app_specific_context_field,
+                                       parent);
+               const struct lttng_event_expr_app_specific_context_field *field_expr_b =
+                               container_of(expr_b,
+                                       const struct lttng_event_expr_app_specific_context_field,
+                                       parent);
+
+               if (strcmp(field_expr_a->provider_name,
+                               field_expr_b->provider_name) != 0) {
+                       goto not_equal;
+               }
+
+               if (strcmp(field_expr_a->type_name,
+                               field_expr_b->type_name) != 0) {
+                       goto not_equal;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               const struct lttng_event_expr_array_field_element *elem_expr_a =
+                               container_of(expr_a,
+                                       const struct lttng_event_expr_array_field_element,
+                                       parent);
+               const struct lttng_event_expr_array_field_element *elem_expr_b =
+                               container_of(expr_b,
+                                       const struct lttng_event_expr_array_field_element,
+                                       parent);
+
+               if (!lttng_event_expr_is_equal(elem_expr_a->array_field_expr,
+                               elem_expr_b->array_field_expr)) {
+                       goto not_equal;
+               }
+
+               if (elem_expr_a->index != elem_expr_b->index) {
+                       goto not_equal;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+       goto end;
+
+not_equal:
+       is_equal = false;
+
+end:
+       return is_equal;
+}
+
+static
+void event_expr_destroy_ref(struct urcu_ref *ref)
+{
+       struct lttng_event_expr *expr =
+               container_of(ref, struct lttng_event_expr, ref);
+
+       switch (expr->type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+       {
+               struct lttng_event_expr_field *field_expr =
+                               container_of(expr,
+                                       struct lttng_event_expr_field, parent);
+
+               free(field_expr->name);
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               struct lttng_event_expr_app_specific_context_field *field_expr =
+                               container_of(expr,
+                                       struct lttng_event_expr_app_specific_context_field,
+                                       parent);
+
+               free(field_expr->provider_name);
+               free(field_expr->type_name);
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               struct lttng_event_expr_array_field_element *elem_expr =
+                               container_of(expr,
+                                       struct lttng_event_expr_array_field_element,
+                                       parent);
+
+               lttng_event_expr_destroy(elem_expr->array_field_expr);
+               break;
+       }
+       default:
+               break;
+       }
+
+       free(expr);
+}
+
+LTTNG_HIDDEN
+void lttng_event_expr_get(struct lttng_event_expr *expr)
+{
+       urcu_ref_get(&expr->ref);
+}
+
+LTTNG_HIDDEN
+void lttng_event_expr_put(struct lttng_event_expr *expr) {
+       if(!expr) {
+               return;
+       }
+       urcu_ref_put(&expr->ref, event_expr_destroy_ref);
+}
+
+void lttng_event_expr_destroy(struct lttng_event_expr *expr)
+{
+       lttng_event_expr_put(expr);
+       return;
+}
diff --git a/src/common/event-field-value.c b/src/common/event-field-value.c
new file mode 100644 (file)
index 0000000..de524cc
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * event-field-value.c
+ *
+ * Linux Trace Toolkit Control Library
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <assert.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/event-field-value-internal.h>
+
+static
+struct lttng_event_field_value *create_empty_field_val(
+               enum lttng_event_field_value_type type, size_t size)
+{
+       struct lttng_event_field_value *field_val;
+
+       field_val = zmalloc(size);
+       if (!field_val) {
+               goto end;
+       }
+
+       field_val->type = type;
+
+end:
+       return field_val;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_uint_create(
+               uint64_t val)
+{
+       struct lttng_event_field_value_uint *field_val;
+
+       field_val = container_of(create_empty_field_val(
+                       LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT,
+                       sizeof(*field_val)),
+                       struct lttng_event_field_value_uint, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_int_create(
+               int64_t val)
+{
+       struct lttng_event_field_value_int *field_val;
+
+       field_val = container_of(create_empty_field_val(
+                       LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT,
+                       sizeof(*field_val)),
+                       struct lttng_event_field_value_int, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+static
+struct lttng_event_field_value_enum *create_enum_field_val(
+               enum lttng_event_field_value_type type, size_t size)
+{
+       struct lttng_event_field_value_enum *field_val;
+
+       field_val = container_of(create_empty_field_val(type, size),
+                       struct lttng_event_field_value_enum, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       lttng_dynamic_pointer_array_init(&field_val->labels, free);
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return field_val;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_enum_uint_create(
+               uint64_t val)
+{
+       struct lttng_event_field_value_enum_uint *field_val;
+
+       field_val = container_of(create_enum_field_val(
+                       LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM,
+                       sizeof(*field_val)),
+                       struct lttng_event_field_value_enum_uint, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent.parent);
+
+end:
+       return &field_val->parent.parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_enum_int_create(
+               int64_t val)
+{
+       struct lttng_event_field_value_enum_int *field_val;
+
+       field_val = container_of(create_enum_field_val(
+                       LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM,
+                       sizeof(*field_val)),
+                       struct lttng_event_field_value_enum_int, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent.parent);
+
+end:
+       return &field_val->parent.parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_real_create(double val)
+{
+       struct lttng_event_field_value_real *field_val = container_of(
+                       create_empty_field_val(
+                               LTTNG_EVENT_FIELD_VALUE_TYPE_REAL,
+                               sizeof(*field_val)),
+                       struct lttng_event_field_value_real, parent);
+
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_string_create_with_size(
+               const char *val, size_t size)
+{
+       struct lttng_event_field_value_string *field_val = container_of(
+                       create_empty_field_val(
+                               LTTNG_EVENT_FIELD_VALUE_TYPE_STRING,
+                               sizeof(*field_val)),
+                       struct lttng_event_field_value_string, parent);
+
+       if (!field_val) {
+               goto error;
+       }
+
+       assert(val);
+       field_val->val = strndup(val, size);
+       if (!field_val->val) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_string_create(
+               const char *val)
+{
+       assert(val);
+       return lttng_event_field_value_string_create_with_size(val,
+                       strlen(val));
+}
+
+static
+void destroy_field_val(void *field_val)
+{
+       lttng_event_field_value_destroy(field_val);
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_array_create(void)
+{
+       struct lttng_event_field_value_array *field_val = container_of(
+                       create_empty_field_val(
+                               LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY,
+                               sizeof(*field_val)),
+                       struct lttng_event_field_value_array, parent);
+
+       if (!field_val) {
+               goto error;
+       }
+
+       lttng_dynamic_pointer_array_init(&field_val->elems, destroy_field_val);
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+LTTNG_HIDDEN
+void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val)
+{
+       if (!field_val) {
+               goto end;
+       }
+
+       switch (field_val->type) {
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+       {
+               struct lttng_event_field_value_enum *enum_field_val =
+                               container_of(field_val,
+                                       struct lttng_event_field_value_enum, parent);
+
+               lttng_dynamic_pointer_array_reset(&enum_field_val->labels);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING:
+       {
+               struct lttng_event_field_value_string *str_field_val =
+                               container_of(field_val,
+                                       struct lttng_event_field_value_string, parent);
+
+               free(str_field_val->val);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY:
+       {
+               struct lttng_event_field_value_array *array_field_expr =
+                               container_of(field_val,
+                                       struct lttng_event_field_value_array,
+                                       parent);
+
+               lttng_dynamic_pointer_array_reset(&array_field_expr->elems);
+               break;
+       }
+       default:
+               break;
+       }
+
+       free(field_val);
+
+end:
+       return;
+}
+
+LTTNG_HIDDEN
+int lttng_event_field_value_enum_append_label_with_size(
+               struct lttng_event_field_value *field_val,
+               const char *label, size_t size)
+{
+       int ret;
+       char *mein_label;
+
+       assert(field_val);
+       assert(label);
+       mein_label = strndup(label, size);
+       if (!mein_label) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_pointer_array_add_pointer(
+                       &container_of(field_val,
+                               struct lttng_event_field_value_enum, parent)->labels,
+                       mein_label);
+       if (ret == 0) {
+               mein_label = NULL;
+       }
+
+end:
+       free(mein_label);
+       return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_event_field_value_enum_append_label(
+               struct lttng_event_field_value *field_val,
+               const char *label)
+{
+       assert(label);
+       return lttng_event_field_value_enum_append_label_with_size(field_val,
+                       label, strlen(label));
+}
+
+LTTNG_HIDDEN
+int lttng_event_field_value_array_append(
+               struct lttng_event_field_value *array_field_val,
+               struct lttng_event_field_value *field_val)
+{
+       assert(array_field_val);
+       assert(field_val);
+       return lttng_dynamic_pointer_array_add_pointer(
+                       &container_of(array_field_val,
+                               struct lttng_event_field_value_array, parent)->elems,
+                       field_val);
+}
+
+LTTNG_HIDDEN
+int lttng_event_field_value_array_append_unavailable(
+               struct lttng_event_field_value *array_field_val)
+{
+       assert(array_field_val);
+       return lttng_dynamic_pointer_array_add_pointer(
+                       &container_of(array_field_val,
+                               struct lttng_event_field_value_array, parent)->elems,
+                       NULL);
+}
+
+enum lttng_event_field_value_type lttng_event_field_value_get_type(
+               const struct lttng_event_field_value *field_val)
+{
+       enum lttng_event_field_value_type type;
+
+       if (!field_val) {
+               type = LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID;
+               goto end;
+       }
+
+       type = field_val->type;
+
+end:
+       return type;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_unsigned_int_get_value(
+               const struct lttng_event_field_value *field_val, uint64_t *val)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || !val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       switch (field_val->type) {
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT:
+               *val = container_of(field_val,
+                               const struct lttng_event_field_value_uint,
+                               parent)->val;
+               break;
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+               *val = container_of(
+                               container_of(field_val,
+                                       const struct lttng_event_field_value_enum,
+                                       parent),
+                               const struct lttng_event_field_value_enum_uint,
+                               parent)->val;
+               break;
+       default:
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_signed_int_get_value(
+               const struct lttng_event_field_value *field_val, int64_t *val)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || !val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       switch (field_val->type) {
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT:
+               *val = container_of(field_val,
+                               const struct lttng_event_field_value_int,
+                               parent)->val;
+               break;
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+               *val = container_of(
+                               container_of(field_val,
+                                       const struct lttng_event_field_value_enum,
+                                       parent),
+                               const struct lttng_event_field_value_enum_int,
+                               parent)->val;
+               break;
+       default:
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_real_get_value(
+               const struct lttng_event_field_value *field_val, double *val)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_REAL ||
+                       !val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       *val = container_of(field_val,
+                       const struct lttng_event_field_value_real, parent)->val;
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+static
+bool ist_enum_field_val(const struct lttng_event_field_value *field_val)
+{
+       return field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM ||
+               field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_enum_get_label_count(
+               const struct lttng_event_field_value *field_val,
+               unsigned int *count)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || !ist_enum_field_val(field_val) || !count) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       *count = (unsigned int) lttng_dynamic_pointer_array_get_count(
+                       &container_of(field_val,
+                               const struct lttng_event_field_value_enum,
+                               parent)->labels);
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+const char *lttng_event_field_value_enum_get_label_at_index(
+               const struct lttng_event_field_value *field_val,
+               unsigned int index)
+{
+       const char *ret;
+       const struct lttng_event_field_value_enum *enum_field_val;
+
+       if (!field_val || !ist_enum_field_val(field_val)) {
+               ret = NULL;
+               goto end;
+       }
+
+       enum_field_val = container_of(field_val,
+                       const struct lttng_event_field_value_enum, parent);
+
+       if (index >= lttng_dynamic_pointer_array_get_count(&enum_field_val->labels)) {
+               ret = NULL;
+               goto end;
+       }
+
+       ret = lttng_dynamic_pointer_array_get_pointer(&enum_field_val->labels,
+                       index);
+
+end:
+       return ret;
+}
+
+const char *lttng_event_field_value_string_get_value(
+               const struct lttng_event_field_value *field_val)
+{
+       const char *ret;
+
+       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_STRING) {
+               ret = NULL;
+               goto end;
+       }
+
+       ret = container_of(field_val,
+                       const struct lttng_event_field_value_string, parent)->val;
+
+end:
+       return ret;
+}
+
+enum lttng_event_field_value_status lttng_event_field_value_array_get_length(
+               const struct lttng_event_field_value *field_val,
+               unsigned int *length)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY ||
+                       !length) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       *length = (unsigned int) lttng_dynamic_pointer_array_get_count(
+                       &container_of(field_val,
+                               const struct lttng_event_field_value_array,
+                               parent)->elems);
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_array_get_element_at_index(
+               const struct lttng_event_field_value *field_val,
+               unsigned int index,
+               const struct lttng_event_field_value **elem_field_val)
+{
+       enum lttng_event_field_value_status status;
+       const struct lttng_event_field_value_array *array_field_val;
+
+       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY ||
+                       !elem_field_val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       array_field_val = container_of(field_val,
+                       const struct lttng_event_field_value_array, parent);
+
+       if (index >= lttng_dynamic_pointer_array_get_count(&array_field_val->elems)) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       *elem_field_val = lttng_dynamic_pointer_array_get_pointer(
+                       &array_field_val->elems, index);
+       if (*elem_field_val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+       } else {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE;
+       }
+
+end:
+       return status;
+}
index cdf3e0dedbea19b357fcb68a8342a26cf27c44ef..20731ec65b21e1515d276bb13de12cfd0d1a2bcc 100644 (file)
@@ -248,7 +248,7 @@ const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule)
 }
 
 LTTNG_HIDDEN
 }
 
 LTTNG_HIDDEN
-const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode(
+const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode(
                const struct lttng_event_rule *rule)
 {
        assert(rule->get_filter_bytecode);
                const struct lttng_event_rule *rule)
 {
        assert(rule->get_filter_bytecode);
@@ -264,6 +264,36 @@ struct lttng_event_exclusion *lttng_event_rule_generate_exclusions(
 }
 
 LTTNG_HIDDEN
 }
 
 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;
+}
+
 const char *lttng_event_rule_type_str(enum lttng_event_rule_type type)
 {
        switch (type) {
 const char *lttng_event_rule_type_str(enum lttng_event_rule_type type)
 {
        switch (type) {
index a5c93e653f08784dbb4af8997dc1d14c66ba80c9..415d3b77df9e4c433806e38a21d73e9b53a1204e 100644 (file)
@@ -159,7 +159,7 @@ static const char *lttng_event_rule_kprobe_get_filter(
        return NULL;
 }
 
        return NULL;
 }
 
-static const struct lttng_filter_bytecode *
+static const struct lttng_bytecode *
 lttng_event_rule_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
 {
        /* Not supported. */
 lttng_event_rule_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
 {
        /* Not supported. */
index ef7ccd0f9a038505c4cec79fea5b0f6c7fb4428e..1097fca8fa64d575a6969f095130d7fe2cef6243 100644 (file)
@@ -144,7 +144,7 @@ static enum lttng_error_code lttng_event_rule_syscall_generate_filter_bytecode(
        struct lttng_event_rule_syscall *syscall;
        enum lttng_event_rule_status status;
        const char *filter;
        struct lttng_event_rule_syscall *syscall;
        enum lttng_event_rule_status status;
        const char *filter;
-       struct lttng_filter_bytecode *bytecode = NULL;
+       struct lttng_bytecode *bytecode = NULL;
 
        assert(rule);
 
 
        assert(rule);
 
@@ -201,7 +201,7 @@ static const char *lttng_event_rule_syscall_get_internal_filter(
        return syscall->internal_filter.filter;
 }
 
        return syscall->internal_filter.filter;
 }
 
-static const struct lttng_filter_bytecode *
+static const struct lttng_bytecode *
 lttng_event_rule_syscall_get_internal_filter_bytecode(
                const struct lttng_event_rule *rule)
 {
 lttng_event_rule_syscall_get_internal_filter_bytecode(
                const struct lttng_event_rule *rule)
 {
index 14e4c7b01901e9b7706ab5e3b83162ba82764337..c3b9d0783b49b4febdfe4877649507174ebe7b1b 100644 (file)
@@ -13,6 +13,7 @@
 #include <common/runas.h>
 #include <lttng/event-rule/event-rule-internal.h>
 #include <lttng/event-rule/tracepoint-internal.h>
 #include <common/runas.h>
 #include <lttng/event-rule/event-rule-internal.h>
 #include <lttng/event-rule/tracepoint-internal.h>
+#include <lttng/event.h>
 
 #define IS_TRACEPOINT_EVENT_RULE(rule) \
        (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT)
 
 #define IS_TRACEPOINT_EVENT_RULE(rule) \
        (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT)
@@ -365,7 +366,7 @@ lttng_event_rule_tracepoint_generate_filter_bytecode(
        enum lttng_domain_type domain_type;
        enum lttng_event_rule_status status;
        const char *filter;
        enum lttng_domain_type domain_type;
        enum lttng_event_rule_status status;
        const char *filter;
-       struct lttng_filter_bytecode *bytecode = NULL;
+       struct lttng_bytecode *bytecode = NULL;
 
        assert(rule);
 
 
        assert(rule);
 
@@ -457,7 +458,7 @@ static const char *lttng_event_rule_tracepoint_get_internal_filter(
        return tracepoint->internal_filter.filter;
 }
 
        return tracepoint->internal_filter.filter;
 }
 
-static const struct lttng_filter_bytecode *
+static const struct lttng_bytecode *
 lttng_event_rule_tracepoint_get_internal_filter_bytecode(
                const struct lttng_event_rule *rule)
 {
 lttng_event_rule_tracepoint_get_internal_filter_bytecode(
                const struct lttng_event_rule *rule)
 {
@@ -540,6 +541,35 @@ static void destroy_lttng_exclusions_element(void *ptr)
        free(ptr);
 }
 
        free(ptr);
 }
 
+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 *lttng_event_rule_tracepoint_create(
                enum lttng_domain_type domain_type)
 {
@@ -569,6 +599,8 @@ struct lttng_event_rule *lttng_event_rule_tracepoint_create(
                        lttng_event_rule_tracepoint_get_internal_filter_bytecode;
        tp_rule->parent.generate_exclusions =
                        lttng_event_rule_tracepoint_generate_exclusions;
                        lttng_event_rule_tracepoint_get_internal_filter_bytecode;
        tp_rule->parent.generate_exclusions =
                        lttng_event_rule_tracepoint_generate_exclusions;
+       tp_rule->parent.generate_lttng_event =
+                       lttng_event_rule_tracepoint_generate_lttng_event;
 
        tp_rule->domain = domain_type;
        tp_rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL;
 
        tp_rule->domain = domain_type;
        tp_rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL;
index 8d63975ab0ba644673ce222bfde67d992440f643..a994b0e281d664212d117b67a560f89445863244 100644 (file)
@@ -151,7 +151,7 @@ static const char *lttng_event_rule_uprobe_get_filter(
        return NULL;
 }
 
        return NULL;
 }
 
-static const struct lttng_filter_bytecode *
+static const struct lttng_bytecode *
 lttng_event_rule_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
 {
        /* Unsupported. */
 lttng_event_rule_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
 {
        /* Unsupported. */
index fc16f1d526abaef92f0bb97d3eb4ed19f7e69b2b..5d333375dd2e8c3f3e9ec4091916e74d3878ca7e 100644 (file)
@@ -16,7 +16,7 @@ struct bytecode_symbol_iterator {
 
 LTTNG_HIDDEN
 struct bytecode_symbol_iterator *bytecode_symbol_iterator_create(
 
 LTTNG_HIDDEN
 struct bytecode_symbol_iterator *bytecode_symbol_iterator_create(
-               struct lttng_filter_bytecode *bytecode)
+               struct lttng_bytecode *bytecode)
 {
        struct bytecode_symbol_iterator *it = NULL;
 
 {
        struct bytecode_symbol_iterator *it = NULL;
 
index 2cbdd14a96764f2ed64f7824ca316f82d6418e02..a03b1d9feb02fc38f8665d5cf91f0bd72f8164c0 100644 (file)
@@ -18,7 +18,7 @@ struct bytecode_symbol_iterator;
  */
 LTTNG_HIDDEN
 struct bytecode_symbol_iterator *bytecode_symbol_iterator_create(
  */
 LTTNG_HIDDEN
 struct bytecode_symbol_iterator *bytecode_symbol_iterator_create(
-               struct lttng_filter_bytecode *bytecode);
+               struct lttng_bytecode *bytecode);
 
 /*
  * Advance iterator of one element.
 
 /*
  * Advance iterator of one element.
index cac4cb17575ed820a3031fa1c47c0db59932286f..261acc0861740b82297d342f9183fc4a4446803b 100644 (file)
@@ -19,7 +19,6 @@ libfilter_la_SOURCES = \
        filter-visitor-ir-normalize-glob-patterns.c \
        filter-visitor-generate-bytecode.c \
        filter-ast.h \
        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)
        filter-ir.h \
        memstream.h
 libfilter_la_CFLAGS = -include filter-symbols.h $(AM_CFLAGS)
@@ -63,4 +62,6 @@ all-local: filter-lexer.c
 endif # HAVE_FLEX
 
 filter_grammar_test_SOURCES = filter-grammar-test.c
 endif # HAVE_FLEX
 
 filter_grammar_test_SOURCES = filter-grammar-test.c
-filter_grammar_test_LDADD = libfilter.la
+filter_grammar_test_LDADD = \
+       libfilter.la \
+       ../bytecode/libbytecode.la
index 29fde10f8c126f7cd430b9a8f498987386801d03..93f9b9b25fd746673cc4689ff7ac7f5033779122 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <urcu/list.h>
  */
 
 #include <urcu/list.h>
+#include <stdio.h>
 #include <stdint.h>
 
 #define printf_debug(fmt, args...)                                     \
 #include <stdint.h>
 
 #define printf_debug(fmt, args...)                                     \
@@ -157,8 +158,8 @@ struct filter_parser_ctx {
        struct filter_ast *ast;
        struct cds_list_head allocated_strings;
        struct ir_op *ir_root;
        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 lttng_bytecode_alloc *bytecode;
+       struct lttng_bytecode_alloc *bytecode_reloc;
 };
 
 struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input);
 };
 
 struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input);
diff --git a/src/common/filter/filter-bytecode.h b/src/common/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 */
index 70656e1a84f42f87b1b0a35bb1579f2b15e908c9..9029fa65eef9acfec48bd6dace507af88853e181 100644 (file)
@@ -16,9 +16,9 @@
 #include <assert.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <assert.h>
 #include <errno.h>
 #include <inttypes.h>
+#include "common/bytecode/bytecode.h"
 #include "filter-ast.h"
 #include "filter-parser.h"
 #include "filter-ast.h"
 #include "filter-parser.h"
-#include "filter-bytecode.h"
 
 int main(int argc, char **argv)
 {
 
 int main(int argc, char **argv)
 {
index d62c0ee0c4f4d66b5be2336b6104c4f7ce75d4d7..5775e8004571cf007cda4d2b2050d366f5f06c24 100644 (file)
@@ -31,6 +31,29 @@ enum ir_data_type {
        IR_DATA_EXPRESSION,
 };
 
        IR_DATA_EXPRESSION,
 };
 
+static inline
+const char *ir_data_type_str(enum ir_data_type type)
+{
+       switch (type) {
+       case IR_DATA_UNKNOWN:
+               return "IR_DATA_UNKNOWN";
+       case IR_DATA_STRING:
+               return "IR_DATA_STRING";
+       case IR_DATA_NUMERIC:
+               return "IR_DATA_NUMERIC";
+       case IR_DATA_FLOAT:
+               return "IR_DATA_FLOAT";
+       case IR_DATA_FIELD_REF:
+               return "IR_DATA_FIELD_REF";
+       case IR_DATA_GET_CONTEXT_REF:
+               return "IR_DATA_GET_CONTEXT_REF";
+       case IR_DATA_EXPRESSION:
+               return "IR_DATA_EXPRESSION";
+       default:
+               abort();
+       }
+}
+
 enum ir_op_type {
        IR_OP_UNKNOWN = 0,
        IR_OP_ROOT,
 enum ir_op_type {
        IR_OP_UNKNOWN = 0,
        IR_OP_ROOT,
@@ -40,6 +63,27 @@ enum ir_op_type {
        IR_OP_LOGICAL,
 };
 
        IR_OP_LOGICAL,
 };
 
+static inline
+const char *ir_op_type_str(enum ir_op_type type)
+{
+       switch (type) {
+       case IR_OP_UNKNOWN:
+               return "IR_OP_UNKNOWN";
+       case IR_OP_ROOT:
+               return "IR_OP_ROOT";
+       case IR_OP_LOAD:
+               return "IR_OP_LOAD";
+       case IR_OP_UNARY:
+               return "IR_OP_UNARY";
+       case IR_OP_BINARY:
+               return "IR_OP_BINARY";
+       case IR_OP_LOGICAL:
+               return "IR_OP_LOGICAL";
+       default:
+               abort();
+       }
+}
+
 /* left or right child */
 enum ir_side {
        IR_SIDE_UNKNOWN = 0,
 /* left or right child */
 enum ir_side {
        IR_SIDE_UNKNOWN = 0,
@@ -71,6 +115,27 @@ enum ir_load_expression_type {
        IR_LOAD_EXPRESSION_LOAD_FIELD,
 };
 
        IR_LOAD_EXPRESSION_LOAD_FIELD,
 };
 
+static inline
+const char *ir_load_expression_type_str(enum ir_load_expression_type type)
+{
+       switch (type) {
+       case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+               return "IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT";
+       case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+               return "IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT";
+       case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+               return "IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT";
+       case IR_LOAD_EXPRESSION_GET_SYMBOL:
+               return "IR_LOAD_EXPRESSION_GET_SYMBOL";
+       case IR_LOAD_EXPRESSION_GET_INDEX:
+               return "IR_LOAD_EXPRESSION_GET_INDEX";
+       case IR_LOAD_EXPRESSION_LOAD_FIELD:
+               return "IR_LOAD_EXPRESSION_LOAD_FIELD";
+       default:
+               abort();
+       }
+}
+
 struct ir_load_expression_op {
        struct ir_load_expression_op *next;
        enum ir_load_expression_type type;
 struct ir_load_expression_op {
        struct ir_load_expression_op *next;
        enum ir_load_expression_type type;
index 245d83c26bd99c859c62fc9e59dc5235254f4f9a..dc94a9813c2a15df07590aefecd891d4a398f038 100644 (file)
@@ -18,9 +18,9 @@
 #include <assert.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <assert.h>
 #include <errno.h>
 #include <inttypes.h>
+#include "common/bytecode/bytecode.h"
 #include "filter-ast.h"
 #include "filter-parser.h"
 #include "filter-ast.h"
 #include "filter-parser.h"
-#include "filter-bytecode.h"
 #include "memstream.h"
 
 #include <common/macros.h>
 #include "memstream.h"
 
 #include <common/macros.h>
index 699273c3d984f7e9aba6f4ee51b0d77e2fa666d1..458b9d042e73cd60a40f469c40c5793554f93ff4 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #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 "common/align.h"
+#include "common/bytecode/bytecode.h"
+#include "common/compat/string.h"
+#include "common/macros.h"
 #include "filter-ast.h"
 #include "filter-ast.h"
-
-#include <common/macros.h>
+#include "filter-ir.h"
 
 #ifndef max_t
 #define max_t(type, a, b)      ((type) ((a) > (b) ? (a) : (b)))
 #endif
 
 
 #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
 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
 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,
+int bytecode_patch(struct lttng_bytecode_alloc **fb,
                const void *data,
                uint16_t offset,
                uint32_t len)
                const void *data,
                uint16_t offset,
                uint32_t len)
@@ -143,7 +53,7 @@ int visit_node_root(struct filter_parser_ctx *ctx, struct ir_op *node)
                return ret;
 
        /* Generate end of bytecode instruction */
                return ret;
 
        /* Generate end of bytecode instruction */
-       insn.op = FILTER_OP_RETURN;
+       insn.op = BYTECODE_OP_RETURN;
        return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
 }
 
        return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
 }
 
@@ -175,7 +85,7 @@ int append_str(char **s, const char *append)
  */
 static
 int load_expression_legacy_match(const struct ir_load_expression *exp,
  */
 static
 int load_expression_legacy_match(const struct ir_load_expression *exp,
-               enum filter_op *op_type,
+               enum bytecode_op *op_type,
                char **symbol)
 {
        const struct ir_load_expression_op *op;
                char **symbol)
 {
        const struct ir_load_expression_op *op;
@@ -184,21 +94,21 @@ int load_expression_legacy_match(const struct ir_load_expression *exp,
        op = exp->child;
        switch (op->type) {
        case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
        op = exp->child;
        switch (op->type) {
        case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
-               *op_type = FILTER_OP_GET_CONTEXT_REF;
+               *op_type = BYTECODE_OP_GET_CONTEXT_REF;
                if (append_str(symbol, "$ctx.")) {
                        return -ENOMEM;
                }
                need_dot = false;
                break;
        case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
                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;
+               *op_type = BYTECODE_OP_GET_CONTEXT_REF;
                if (append_str(symbol, "$app.")) {
                        return -ENOMEM;
                }
                need_dot = false;
                break;
        case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
                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;
+               *op_type = BYTECODE_OP_LOAD_FIELD_REF;
                need_dot = false;
                break;
 
                need_dot = false;
                break;
 
@@ -250,7 +160,7 @@ int visit_node_load_expression_legacy(struct filter_parser_ctx *ctx,
        struct field_ref ref_offset;
        uint32_t reloc_offset_u32;
        uint16_t reloc_offset;
        struct field_ref ref_offset;
        uint32_t reloc_offset_u32;
        uint16_t reloc_offset;
-       enum filter_op op_type;
+       enum bytecode_op op_type;
        char *symbol = NULL;
        int ret;
 
        char *symbol = NULL;
        int ret;
 
@@ -328,100 +238,37 @@ int visit_node_load_expression(struct filter_parser_ctx *ctx,
                switch (op->type) {
                case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
                {
                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);
+                       int ret = bytecode_push_get_context_root(&ctx->bytecode);
                        if (ret) {
                                return ret;
                        }
                        if (ret) {
                                return ret;
                        }
+
                        break;
                }
                case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
                {
                        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);
+                       int ret = bytecode_push_get_app_context_root(&ctx->bytecode);
                        if (ret) {
                                return ret;
                        }
                        if (ret) {
                                return ret;
                        }
+
                        break;
                }
                case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
                {
                        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);
+                       int ret = bytecode_push_get_payload_root(&ctx->bytecode);
                        if (ret) {
                                return ret;
                        }
                        if (ret) {
                                return ret;
                        }
+
                        break;
                }
                case IR_LOAD_EXPRESSION_GET_SYMBOL:
                {
                        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);
+                       int ret = bytecode_push_get_symbol(
+                                       &ctx->bytecode,
+                                       &ctx->bytecode_reloc,
+                                       op->u.symbol);
                        if (ret) {
                                return ret;
                        }
                        if (ret) {
                                return ret;
                        }
@@ -429,20 +276,7 @@ int visit_node_load_expression(struct filter_parser_ctx *ctx,
                }
                case IR_LOAD_EXPRESSION_GET_INDEX:
                {
                }
                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);
+                       int ret = bytecode_push_get_index_u64(&ctx->bytecode, op->u.index);
                        if (ret) {
                                return ret;
                        }
                        if (ret) {
                                return ret;
                        }
@@ -457,7 +291,7 @@ int visit_node_load_expression(struct filter_parser_ctx *ctx,
                        insn = calloc(insn_len, 1);
                        if (!insn)
                                return -ENOMEM;
                        insn = calloc(insn_len, 1);
                        if (!insn)
                                return -ENOMEM;
-                       insn->op = FILTER_OP_LOAD_FIELD;
+                       insn->op = BYTECODE_OP_LOAD_FIELD;
                        ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
                        free(insn);
                        if (ret) {
                        ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
                        free(insn);
                        if (ret) {
@@ -500,7 +334,7 @@ int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node)
                         * that the appropriate matching function can be
                         * called. Also, see comment below.
                         */
                         * that the appropriate matching function can be
                         * called. Also, see comment below.
                         */
-                       insn->op = FILTER_OP_LOAD_STAR_GLOB_STRING;
+                       insn->op = BYTECODE_OP_LOAD_STAR_GLOB_STRING;
                        break;
                default:
                        /*
                        break;
                default:
                        /*
@@ -513,7 +347,7 @@ int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node)
                         * can be anywhere in the string) is a special
                         * case.
                         */
                         * can be anywhere in the string) is a special
                         * case.
                         */
-                       insn->op = FILTER_OP_LOAD_STRING;
+                       insn->op = BYTECODE_OP_LOAD_STRING;
                        break;
                }
 
                        break;
                }
 
@@ -531,7 +365,7 @@ int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node)
                insn = calloc(insn_len, 1);
                if (!insn)
                        return -ENOMEM;
                insn = calloc(insn_len, 1);
                if (!insn)
                        return -ENOMEM;
-               insn->op = FILTER_OP_LOAD_S64;
+               insn->op = BYTECODE_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);
                memcpy(insn->data, &node->u.load.u.num, sizeof(int64_t));
                ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
                free(insn);
@@ -546,7 +380,7 @@ int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node)
                insn = calloc(insn_len, 1);
                if (!insn)
                        return -ENOMEM;
                insn = calloc(insn_len, 1);
                if (!insn)
                        return -ENOMEM;
-               insn->op = FILTER_OP_LOAD_DOUBLE;
+               insn->op = BYTECODE_OP_LOAD_DOUBLE;
                memcpy(insn->data, &node->u.load.u.flt, sizeof(double));
                ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
                free(insn);
                memcpy(insn->data, &node->u.load.u.flt, sizeof(double));
                ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
                free(insn);
@@ -579,13 +413,13 @@ int visit_node_unary(struct filter_parser_ctx *ctx, struct ir_op *node)
                /* Nothing to do. */
                return 0;
        case AST_UNARY_MINUS:
                /* Nothing to do. */
                return 0;
        case AST_UNARY_MINUS:
-               insn.op = FILTER_OP_UNARY_MINUS;
+               insn.op = BYTECODE_OP_UNARY_MINUS;
                return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
        case AST_UNARY_NOT:
                return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
        case AST_UNARY_NOT:
-               insn.op = FILTER_OP_UNARY_NOT;
+               insn.op = BYTECODE_OP_UNARY_NOT;
                return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
        case AST_UNARY_BIT_NOT:
                return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
        case AST_UNARY_BIT_NOT:
-               insn.op = FILTER_OP_UNARY_BIT_NOT;
+               insn.op = BYTECODE_OP_UNARY_BIT_NOT;
                return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
        }
 }
                return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
        }
 }
@@ -622,53 +456,53 @@ int visit_node_binary(struct filter_parser_ctx *ctx, struct ir_op *node)
                return -EINVAL;
 
        case AST_OP_MUL:
                return -EINVAL;
 
        case AST_OP_MUL:
-               insn.op = FILTER_OP_MUL;
+               insn.op = BYTECODE_OP_MUL;
                break;
        case AST_OP_DIV:
                break;
        case AST_OP_DIV:
-               insn.op = FILTER_OP_DIV;
+               insn.op = BYTECODE_OP_DIV;
                break;
        case AST_OP_MOD:
                break;
        case AST_OP_MOD:
-               insn.op = FILTER_OP_MOD;
+               insn.op = BYTECODE_OP_MOD;
                break;
        case AST_OP_PLUS:
                break;
        case AST_OP_PLUS:
-               insn.op = FILTER_OP_PLUS;
+               insn.op = BYTECODE_OP_PLUS;
                break;
        case AST_OP_MINUS:
                break;
        case AST_OP_MINUS:
-               insn.op = FILTER_OP_MINUS;
+               insn.op = BYTECODE_OP_MINUS;
                break;
        case AST_OP_BIT_RSHIFT:
                break;
        case AST_OP_BIT_RSHIFT:
-               insn.op = FILTER_OP_BIT_RSHIFT;
+               insn.op = BYTECODE_OP_BIT_RSHIFT;
                break;
        case AST_OP_BIT_LSHIFT:
                break;
        case AST_OP_BIT_LSHIFT:
-               insn.op = FILTER_OP_BIT_LSHIFT;
+               insn.op = BYTECODE_OP_BIT_LSHIFT;
                break;
        case AST_OP_BIT_AND:
                break;
        case AST_OP_BIT_AND:
-               insn.op = FILTER_OP_BIT_AND;
+               insn.op = BYTECODE_OP_BIT_AND;
                break;
        case AST_OP_BIT_OR:
                break;
        case AST_OP_BIT_OR:
-               insn.op = FILTER_OP_BIT_OR;
+               insn.op = BYTECODE_OP_BIT_OR;
                break;
        case AST_OP_BIT_XOR:
                break;
        case AST_OP_BIT_XOR:
-               insn.op = FILTER_OP_BIT_XOR;
+               insn.op = BYTECODE_OP_BIT_XOR;
                break;
 
        case AST_OP_EQ:
                break;
 
        case AST_OP_EQ:
-               insn.op = FILTER_OP_EQ;
+               insn.op = BYTECODE_OP_EQ;
                break;
        case AST_OP_NE:
                break;
        case AST_OP_NE:
-               insn.op = FILTER_OP_NE;
+               insn.op = BYTECODE_OP_NE;
                break;
        case AST_OP_GT:
                break;
        case AST_OP_GT:
-               insn.op = FILTER_OP_GT;
+               insn.op = BYTECODE_OP_GT;
                break;
        case AST_OP_LT:
                break;
        case AST_OP_LT:
-               insn.op = FILTER_OP_LT;
+               insn.op = BYTECODE_OP_LT;
                break;
        case AST_OP_GE:
                break;
        case AST_OP_GE:
-               insn.op = FILTER_OP_GE;
+               insn.op = BYTECODE_OP_GE;
                break;
        case AST_OP_LE:
                break;
        case AST_OP_LE:
-               insn.op = FILTER_OP_LE;
+               insn.op = BYTECODE_OP_LE;
                break;
        }
        return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
                break;
        }
        return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
@@ -699,9 +533,9 @@ int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node)
                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) {
                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;
+                       cast_insn.op = BYTECODE_OP_CAST_TO_S64;
                } else {
                } else {
-                       cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;
+                       cast_insn.op = BYTECODE_OP_CAST_DOUBLE_TO_S64;
                }
                ret = bytecode_push(&ctx->bytecode, &cast_insn,
                                        1, sizeof(cast_insn));
                }
                ret = bytecode_push(&ctx->bytecode, &cast_insn,
                                        1, sizeof(cast_insn));
@@ -715,10 +549,10 @@ int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node)
                return -EINVAL;
 
        case AST_OP_AND:
                return -EINVAL;
 
        case AST_OP_AND:
-               insn.op = FILTER_OP_AND;
+               insn.op = BYTECODE_OP_AND;
                break;
        case AST_OP_OR:
                break;
        case AST_OP_OR:
-               insn.op = FILTER_OP_OR;
+               insn.op = BYTECODE_OP_OR;
                break;
        }
        insn.skip_offset = (uint16_t) -1UL;     /* Temporary */
                break;
        }
        insn.skip_offset = (uint16_t) -1UL;     /* Temporary */
@@ -740,9 +574,9 @@ int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node)
                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) {
                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;
+                       cast_insn.op = BYTECODE_OP_CAST_TO_S64;
                } else {
                } else {
-                       cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;
+                       cast_insn.op = BYTECODE_OP_CAST_DOUBLE_TO_S64;
                }
                ret = bytecode_push(&ctx->bytecode, &cast_insn,
                                        1, sizeof(cast_insn));
                }
                ret = bytecode_push(&ctx->bytecode, &cast_insn,
                                        1, sizeof(cast_insn));
diff --git a/src/common/index-allocator.c b/src/common/index-allocator.c
new file mode 100644 (file)
index 0000000..ff5e2c8
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+
+#include <urcu.h>
+#include <urcu/list.h>
+
+#include "macros.h"
+#include "error.h"
+
+#include "index-allocator.h"
+
+struct lttng_index_allocator {
+       struct cds_list_head unused_list;
+       uint64_t size;
+       uint64_t position;
+};
+
+struct lttng_index {
+       uint64_t index;
+       struct cds_list_head head;
+};
+
+struct lttng_index_allocator *lttng_index_allocator_create(
+               uint64_t index_count)
+{
+       struct lttng_index_allocator *allocator = NULL;
+
+       allocator = zmalloc(sizeof(*allocator));
+       if (!allocator) {
+               PERROR("Failed to allocate free index queue");
+               goto end;
+       }
+       allocator->size = index_count;
+       allocator->position = 0;
+
+       CDS_INIT_LIST_HEAD(&allocator->unused_list);
+
+end:
+       return allocator;
+}
+
+uint64_t lttng_index_allocator_get_index_count(struct lttng_index_allocator *allocator)
+{
+       return allocator->size;
+}
+
+enum lttng_index_allocator_status lttng_index_allocator_alloc(
+               struct lttng_index_allocator *allocator,
+               uint64_t *allocated_index)
+{
+       enum lttng_index_allocator_status status =
+                       LTTNG_INDEX_ALLOCATOR_STATUS_OK;
+
+       if (cds_list_empty(&allocator->unused_list)) {
+               if (allocator->position >= allocator->size) {
+                       /* No indices left. */
+                       status = LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY;
+                       goto end;
+               }
+
+               *allocated_index = allocator->position++;
+       } else {
+               struct lttng_index *index;
+
+               index = cds_list_first_entry(&allocator->unused_list,
+                               typeof(*index), head);
+               cds_list_del(&index->head);
+               *allocated_index = index->index;
+               free(index);
+       }
+
+end:
+       return status;
+}
+
+enum lttng_index_allocator_status lttng_index_allocator_release(
+               struct lttng_index_allocator *allocator, uint64_t idx)
+{
+       struct lttng_index *index = NULL;
+       enum lttng_index_allocator_status status =
+                       LTTNG_INDEX_ALLOCATOR_STATUS_OK;
+
+       assert(idx < allocator->size);
+
+       index = zmalloc(sizeof(*index));
+       if (!index) {
+               PERROR("Failed to allocate free index queue");
+               status = LTTNG_INDEX_ALLOCATOR_STATUS_ERROR;
+               goto end;
+       }
+
+       index->index = idx;
+       cds_list_add_tail(&index->head, &allocator->unused_list);
+
+end:
+       return status;
+}
+
+void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator)
+{
+       struct lttng_index *index = NULL, *tmp_index = NULL;
+
+       if (!allocator) {
+               return;
+       }
+
+       cds_list_for_each_entry_safe(index, tmp_index,
+                       &allocator->unused_list, head) {
+               cds_list_del(&index->head);
+               free(index);
+       }
+
+       free(allocator);
+}
diff --git a/src/common/index-allocator.h b/src/common/index-allocator.h
new file mode 100644 (file)
index 0000000..cdc5750
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef _COMMON_INDEX_ALLOCATOR_H
+#define _COMMON_INDEX_ALLOCATOR_H
+
+#include <inttypes.h>
+
+struct lttng_index_allocator;
+
+enum lttng_index_allocator_status {
+       LTTNG_INDEX_ALLOCATOR_STATUS_OK,
+       LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY,
+       LTTNG_INDEX_ALLOCATOR_STATUS_ERROR,
+};
+
+struct lttng_index_allocator *lttng_index_allocator_create(
+               uint64_t index_count);
+
+uint64_t lttng_index_allocator_get_index_count(
+       struct lttng_index_allocator *allocator);
+
+enum lttng_index_allocator_status lttng_index_allocator_alloc(
+               struct lttng_index_allocator *allocator,
+               uint64_t *index);
+
+enum lttng_index_allocator_status lttng_index_allocator_release(
+               struct lttng_index_allocator *allocator, uint64_t index);
+
+void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator);
+
+#endif /* _COMMON_INDEX_ALLOCATOR_H */
index de1a14c218729bd9e88b0a1c1e9cf8219f85e596..b9d843714550c5821d3b091eb7fb6e390a3c6786 100644 (file)
@@ -1207,8 +1207,8 @@ error_rotate_channel:
        case LTTNG_CONSUMER_CREATE_TRACE_CHUNK:
        {
                const struct lttng_credentials credentials = {
        case LTTNG_CONSUMER_CREATE_TRACE_CHUNK:
        {
                const struct lttng_credentials credentials = {
-                       .uid = msg.u.create_trace_chunk.credentials.value.uid,
-                       .gid = msg.u.create_trace_chunk.credentials.value.gid,
+                       .uid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.create_trace_chunk.credentials.value.uid),
+                       .gid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.create_trace_chunk.credentials.value.gid),
                };
                const bool is_local_trace =
                                !msg.u.create_trace_chunk.relayd_id.is_set;
                };
                const bool is_local_trace =
                                !msg.u.create_trace_chunk.relayd_id.is_set;
index 10e281d69315a638a24b96102b601415eeb18801..386685c2a593947b154bf87f91757a601d565d92 100644 (file)
@@ -418,7 +418,51 @@ int kernctl_stop_session(int fd)
                        LTTNG_KERNEL_SESSION_STOP);
 }
 
                        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_group_error_counter(int group_fd, struct lttng_kernel_counter_conf *error_counter_conf)
+{
+       return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_COUNTER, error_counter_conf);
+}
+
+int kernctl_counter_get_value(int counter_fd, struct lttng_kernel_counter_value *value)
+{
+       return LTTNG_IOCTL_NO_CHECK(counter_fd, LTTNG_KERNEL_COUNTER_VALUE, value);
+}
+
+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_capture(int fd, const struct lttng_bytecode *capture)
+{
+       struct lttng_kernel_capture_bytecode *kb;
+       uint32_t len;
+       int ret;
+
+       /* Translate bytecode to kernel bytecode */
+       kb = zmalloc(sizeof(*kb) + capture->len);
+       if (!kb)
+               return -ENOMEM;
+       kb->len = len = capture->len;
+       kb->reloc_offset = capture->reloc_table_offset;
+       kb->seqnum = capture->seqnum;
+       memcpy(kb->data, capture->data, len);
+       ret = LTTNG_IOCTL_CHECK(fd, LTTNG_KERNEL_CAPTURE, kb);
+       free(kb);
+       return ret;
+}
+
+int kernctl_filter(int fd, const struct lttng_bytecode *filter)
 {
        struct lttng_kernel_filter_bytecode *kb;
        uint32_t len;
 {
        struct lttng_kernel_filter_bytecode *kb;
        uint32_t len;
index 49925ea6c3affe8264b1f4b0a7cd00571c90dd39..eab697730f222731270375495e3ba16df5435161 100644 (file)
@@ -28,9 +28,20 @@ int kernctl_disable(int fd);
 int kernctl_start_session(int fd);
 int kernctl_stop_session(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_group_error_counter(int fd,
+               struct lttng_kernel_counter_conf *error_counter_conf);
+int kernctl_create_trigger(int fd, struct lttng_kernel_trigger *trigger);
+
+int kernctl_counter_get_value(int counter_fd, struct lttng_kernel_counter_value *value);
+
 /* Apply on event FD */
 /* Apply on event FD */
-int kernctl_filter(int fd, struct lttng_filter_bytecode *filter);
+int kernctl_filter(int fd, const struct lttng_bytecode *filter);
 int kernctl_add_callsite(int fd, struct lttng_kernel_event_callsite *callsite);
 int kernctl_add_callsite(int fd, struct lttng_kernel_event_callsite *callsite);
+int kernctl_capture(int fd, const struct lttng_bytecode *capture);
 
 int kernctl_tracepoint_list(int fd);
 int kernctl_syscall_list(int fd);
 
 int kernctl_tracepoint_list(int fd);
 int kernctl_syscall_list(int fd);
index ed6555e10e47d87e59f3b4514a42dcee1b4db55a..4cffd9179176f21633bf74fca514e2ef8a2675b5 100644 (file)
 #define LTTNG_KERNEL_TRACER_ABI_VERSION                \
        _IOR(0xF6, 0x4B, struct lttng_kernel_tracer_abi_version)
 
 #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)
+#define LTTNG_KERNEL_CAPTURE _IO(0xF6, 0x32)
+
+#define LTTNG_KERNEL_COUNTER \
+       _IOW(0xF6, 0x33, struct lttng_kernel_counter_conf)
+#define LTTNG_KERNEL_COUNTER_VALUE \
+       _IOWR(0xF6, 0x34, struct lttng_kernel_counter_value)
+
 /* Session FD ioctl */
 #define LTTNG_KERNEL_METADATA                  \
        _IOW(0xF6, 0x54, struct lttng_kernel_channel)
 /* Session FD ioctl */
 #define LTTNG_KERNEL_METADATA                  \
        _IOW(0xF6, 0x54, struct lttng_kernel_channel)
index fc2a1e25944f26cd8273353d5dd6ce49dce92e9e..cdf6f041bd4d563c52112019fbb4b767fe16d718 100644 (file)
@@ -151,6 +151,72 @@ struct lttng_kernel_event {
        } u;
 } LTTNG_PACKED;
 
        } 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;
+       uint64_t error_counter_idx;
+       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;
+
+enum lttng_kernel_counter_arithmetic {
+       LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR = 1,
+};
+
+enum lttng_kernel_counter_bitness {
+       LTTNG_KERNEL_COUNTER_BITNESS_32BITS = 1,
+       LTTNG_KERNEL_COUNTER_BITNESS_64BITS = 2,
+};
+
+struct lttng_kernel_counter_dimension {
+       uint64_t size;
+       uint64_t underflow_index;
+       uint64_t overflow_index;
+       uint8_t has_underflow;
+       uint8_t has_overflow;
+} LTTNG_PACKED;
+
+#define LTTNG_KERNEL_COUNTER_DIMENSION_MAX 8
+struct lttng_kernel_counter_conf {
+       uint32_t arithmetic;    /* enum lttng_kernel_counter_arithmetic */
+       uint32_t bitness;       /* enum lttng_kernel_counter_bitness */
+       uint32_t number_dimensions;
+       int64_t global_sum_step;
+       struct lttng_kernel_counter_dimension dimensions[LTTNG_KERNEL_COUNTER_DIMENSION_MAX];
+} LTTNG_PACKED;
+
+struct lttng_kernel_counter_value {
+       uint32_t number_dimensions;
+       uint64_t dimension_indexes[LTTNG_KERNEL_COUNTER_DIMENSION_MAX];
+       int64_t value;
+} LTTNG_PACKED;
+
+#define LTTNG_KERNEL_TRIGGER_NOTIFICATION_PADDING 32
+struct lttng_kernel_trigger_notification {
+       uint64_t id;
+       uint16_t capture_buf_size;
+       char padding[LTTNG_KERNEL_TRIGGER_NOTIFICATION_PADDING];
+} LTTNG_PACKED;
+
+#define LTTNG_KERNEL_CAPTURE_BYTECODE_MAX_LEN          65536
+struct lttng_kernel_capture_bytecode {
+       uint32_t len;
+       uint32_t reloc_offset;
+       uint64_t seqnum;
+       char data[0];
+} LTTNG_PACKED;
+
 struct lttng_kernel_tracer_version {
        uint32_t major;
        uint32_t minor;
 struct lttng_kernel_tracer_version {
        uint32_t major;
        uint32_t minor;
index 2544d042fd8192ee309558df6b41e483dc4db3ca..27ba0e541a926899dc51614bc03a70666aa18e53 100644 (file)
@@ -113,7 +113,7 @@ ssize_t lttng_notification_create_from_payload(
                                                notification_size, -1);
 
                evaluation_size = lttng_evaluation_create_from_payload(
                                                notification_size, -1);
 
                evaluation_size = lttng_evaluation_create_from_payload(
-                               &evaluation_view, &evaluation);
+                               condition, &evaluation_view, &evaluation);
        }
 
        if (evaluation_size < 0) {
        }
 
        if (evaluation_size < 0) {
index faa64bf604e424adc2aefcda50b044c028c0a507..ca7d1f043d21bdef1b9ad8dc716452ee49fe7858 100644 (file)
        })
 
 /*
        })
 
 /*
- * Initialize an optional field.
+ * Initialize an optional field as unset.
  *
  * The wrapped field is set to the value it would gave if it had static storage
  * duration.
  */
  *
  * The wrapped field is set to the value it would gave if it had static storage
  * duration.
  */
-#define LTTNG_OPTIONAL_INIT { .is_set = 0 }
+#define LTTNG_OPTIONAL_INIT_UNSET { .is_set = 0 }
+
+/*
+ * Initialize an optional field as 'set' with a given value.
+ */
+#define LTTNG_OPTIONAL_INIT_VALUE(val) { .value = val, .is_set = 1 }
 
 /* Set the value of an optional field. */
 #define LTTNG_OPTIONAL_SET(field_ptr, val)     \
 
 /* Set the value of an optional field. */
 #define LTTNG_OPTIONAL_SET(field_ptr, val)     \
index 3c4c2ae0bb9d360141c3ed5dbb60fbc6a420e8ab..731b450080d4dc11cb25c9b1c6bec4c3955e3c19 100644 (file)
@@ -23,6 +23,7 @@
 #include <assert.h>
 #include <signal.h>
 
 #include <assert.h>
 #include <signal.h>
 
+#include <common/bytecode/bytecode.h>
 #include <common/lttng-kernel.h>
 #include <common/common.h>
 #include <common/utils.h>
 #include <common/lttng-kernel.h>
 #include <common/common.h>
 #include <common/utils.h>
@@ -37,7 +38,6 @@
 
 #include <common/sessiond-comm/sessiond-comm.h>
 #include <common/filter/filter-ast.h>
 
 #include <common/sessiond-comm/sessiond-comm.h>
 #include <common/filter/filter-ast.h>
-#include <common/filter/filter-bytecode.h>
 
 #include "runas.h"
 
 
 #include "runas.h"
 
@@ -1801,13 +1801,13 @@ LTTNG_HIDDEN
 int run_as_generate_filter_bytecode(const char *filter_expression,
                uid_t uid,
                gid_t gid,
 int run_as_generate_filter_bytecode(const char *filter_expression,
                uid_t uid,
                gid_t gid,
-               struct lttng_filter_bytecode **bytecode)
+               struct lttng_bytecode **bytecode)
 {
        int ret;
        struct run_as_data data = {};
        struct run_as_ret run_as_ret = {};
 {
        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;
+       const struct lttng_bytecode *view_bytecode = NULL;
+       struct lttng_bytecode *local_bytecode = NULL;
 
        DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d",
                        filter_expression, (int) uid, (int) gid);
 
        DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d",
                        filter_expression, (int) uid, (int) gid);
@@ -1825,7 +1825,7 @@ int run_as_generate_filter_bytecode(const char *filter_expression,
                goto error;
        }
 
                goto error;
        }
 
-       view_bytecode = (const struct lttng_filter_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode;
+       view_bytecode = (const struct lttng_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode;
 
        local_bytecode = zmalloc(sizeof(*local_bytecode) + view_bytecode->len);
        if (!local_bytecode) {
 
        local_bytecode = zmalloc(sizeof(*local_bytecode) + view_bytecode->len);
        if (!local_bytecode) {
index 398221708e16948dfc62221d7df228cf3fb6f850..06e41cd019b73071424af8ab815f7eebd54869e0 100644 (file)
@@ -74,7 +74,7 @@ LTTNG_HIDDEN
 int run_as_generate_filter_bytecode(const char *filter_expression,
                uid_t uid,
                gid_t gid,
 int run_as_generate_filter_bytecode(const char *filter_expression,
                uid_t uid,
                gid_t gid,
-               struct lttng_filter_bytecode **bytecode);
+               struct lttng_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
 int run_as_create_worker(const char *procname,
                post_fork_cleanup_cb clean_up_func, void *clean_up_user_data);
diff --git a/src/common/session-consumed-size.c b/src/common/session-consumed-size.c
deleted file mode 100644 (file)
index 0461073..0000000
+++ /dev/null
@@ -1,458 +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_payload *payload)
-{
-       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(&payload->buffer, &consumed_comm,
-                       sizeof(consumed_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, 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_payload(struct lttng_condition *condition,
-               struct lttng_payload_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->buffer.size < sizeof(*condition_comm)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm = (typeof(condition_comm)) src_view->buffer.data;
-       names_view = lttng_buffer_view_from_view(&src_view->buffer,
-                       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_payload(
-               struct lttng_payload_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_payload(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_payload(
-               const struct lttng_payload_view *view)
-{
-       const struct lttng_evaluation_session_consumed_size_comm *comm =
-                       (typeof(comm)) view->buffer.data;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (view->buffer.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_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       evaluation = create_evaluation_from_payload(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_payload *payload)
-{
-       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(
-                       &payload->buffer, &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 f6849d2..0000000
+++ /dev/null
@@ -1,582 +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_payload *payload);
-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_payload *payload);
-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_payload *payload)
-{
-       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(&payload->buffer, &rotation_comm,
-                       sizeof(rotation_comm));
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       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_payload(struct lttng_condition *condition,
-               struct lttng_payload_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->buffer.size < sizeof(*condition_comm)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm = (typeof(condition_comm)) src_view->buffer.data;
-       name_view = lttng_buffer_view_from_view(&src_view->buffer,
-                       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_payload(
-               struct lttng_payload_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_payload(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_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **condition)
-{
-       return lttng_condition_session_rotation_create_from_payload(view,
-                       condition,
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_session_rotation_completed_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **condition)
-{
-       return lttng_condition_session_rotation_create_from_payload(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_payload(
-               enum lttng_condition_type type,
-               struct lttng_payload_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 =
-                       (typeof(comm)) view->buffer.data;
-       struct lttng_buffer_view location_view;
-
-       if (view->buffer.size < sizeof(*comm)) {
-               goto error;
-       }
-
-       size = sizeof(*comm);
-       if (comm->has_location) {
-               location_view = lttng_buffer_view_from_view(
-                               &view->buffer, 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_payload(
-               enum lttng_condition_type type,
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = create_evaluation_from_payload(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_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **evaluation)
-{
-       return lttng_evaluation_session_rotation_create_from_payload(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING,
-                       view, evaluation);
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_session_rotation_completed_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **evaluation)
-{
-       return lttng_evaluation_session_rotation_create_from_payload(
-                       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_payload *payload)
-{
-       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(
-                       &payload->buffer, &comm, sizeof(comm));
-       if (ret) {
-               goto end;
-       }
-       if (!rotation->location) {
-               goto end;
-       }
-       ret = lttng_trace_archive_location_serialize(rotation->location,
-                       &payload->buffer);
-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 c15a26f889bc83bcd682103abb4ab079622dab18..e403bfe3e05411632bff0184e3162cd21cf833ad 100644 (file)
@@ -106,6 +106,7 @@ enum lttcomm_sessiond_command {
        LTTNG_SESSION_LIST_ROTATION_SCHEDULES           = 48,
        LTTNG_CREATE_SESSION_EXT                        = 49,
        LTTNG_CLEAR_SESSION                             = 50,
        LTTNG_SESSION_LIST_ROTATION_SCHEDULES           = 48,
        LTTNG_CREATE_SESSION_EXT                        = 49,
        LTTNG_CLEAR_SESSION                             = 50,
+       LTTNG_LIST_TRIGGERS                             = 51,
 };
 
 enum lttcomm_relayd_command {
 };
 
 enum lttcomm_relayd_command {
@@ -429,7 +430,7 @@ struct lttcomm_session_msg {
  * starts at reloc_table_offset.
  */
 #define LTTNG_FILTER_PADDING   32
  * starts at reloc_table_offset.
  */
 #define LTTNG_FILTER_PADDING   32
-struct lttng_filter_bytecode {
+struct lttng_bytecode {
        uint32_t len;   /* len of data */
        uint32_t reloc_table_offset;
        uint64_t seqnum;
        uint32_t len;   /* len of data */
        uint32_t reloc_table_offset;
        uint64_t seqnum;
diff --git a/src/common/shm.c b/src/common/shm.c
new file mode 100644 (file)
index 0000000..74b240c
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include <common/error.h>
+
+#include "shm.h"
+
+/*
+ * Using fork to set umask in the child process (not multi-thread safe). We
+ * deal with the shm_open vs ftruncate race (happening when the sessiond owns
+ * the shm and does not let everybody modify it, to ensure safety against
+ * shm_unlink) by simply letting the mmap fail and retrying after a few
+ * seconds. For global shm, everybody has rw access to it until the sessiond
+ * starts.
+ */
+static int get_wait_shm(char *shm_path, size_t mmap_size, int global)
+{
+       int wait_shm_fd, ret;
+       mode_t mode;
+
+       assert(shm_path);
+
+       /* Default permissions */
+       mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+       /*
+        * Change owner of the shm path.
+        */
+       if (global) {
+               /*
+                * If global session daemon, any application can
+                * register. Make it initially writeable so applications
+                * registering concurrently can do ftruncate() by
+                * themselves.
+                */
+               mode |= S_IROTH | S_IWOTH;
+       }
+
+       /*
+        * We're alone in a child process, so we can modify the process-wide
+        * umask.
+        */
+       umask(~mode);
+
+       /*
+        * Try creating shm (or get rw access). We don't do an exclusive open,
+        * because we allow other processes to create+ftruncate it concurrently.
+        *
+        * A sysctl, fs.protected_regular may prevent the session daemon from
+        * opening a previously created shm when the O_CREAT flag is provided.
+        * Systemd enables this ABI-breaking change by default since v241.
+        *
+        * First, attempt to use the create-or-open semantic that is
+        * desired here. If this fails with EACCES, work around this broken
+        * behaviour and attempt to open the shm without the O_CREAT flag.
+        *
+        * The two attempts are made in this order since applications are
+        * expected to race with the session daemon to create this shm.
+        * Attempting an shm_open() without the O_CREAT flag first could fail
+        * because the file doesn't exist. It could then be created by an
+        * application, which would cause a second try with the O_CREAT flag to
+        * fail with EACCES.
+        *
+        * Note that this introduces a new failure mode where a user could
+        * launch an application (creating the shm) and unlink the shm while
+        * the session daemon is launching, causing the second attempt
+        * to fail. This is not recovered-from as unlinking the shm will
+        * prevent userspace tracing from succeeding anyhow: the sessiond would
+        * use a now-unlinked shm, while the next application would create
+        * a new named shm.
+        */
+       wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode);
+       if (wait_shm_fd < 0) {
+               if (errno == EACCES) {
+                       /* Work around sysctl fs.protected_regular. */
+                       DBG("shm_open of %s returned EACCES, this may be caused "
+                                       "by the fs.protected_regular sysctl. "
+                                       "Attempting to open the shm without "
+                                       "creating it.", shm_path);
+                       wait_shm_fd = shm_open(shm_path, O_RDWR, mode);
+               }
+               if (wait_shm_fd < 0) {
+                       PERROR("Failed to open wait shm at %s", shm_path);
+                       goto error;
+               }
+       }
+
+       ret = ftruncate(wait_shm_fd, mmap_size);
+       if (ret < 0) {
+               PERROR("ftruncate wait shm");
+               exit(EXIT_FAILURE);
+       }
+
+#ifndef __FreeBSD__
+       if (global) {
+               ret = fchown(wait_shm_fd, 0, 0);
+               if (ret < 0) {
+                       PERROR("fchown");
+                       exit(EXIT_FAILURE);
+               }
+               /*
+                * If global session daemon, any application can
+                * register so the shm needs to be set in read-only mode
+                * for others.
+                */
+               mode &= ~S_IWOTH;
+               ret = fchmod(wait_shm_fd, mode);
+               if (ret < 0) {
+                       PERROR("fchmod");
+                       exit(EXIT_FAILURE);
+               }
+       } else {
+               ret = fchown(wait_shm_fd, getuid(), getgid());
+               if (ret < 0) {
+                       PERROR("fchown");
+                       exit(EXIT_FAILURE);
+               }
+       }
+#else
+#warning "FreeBSD does not support setting file mode on shm FD."
+#endif
+
+       DBG("Got the wait shm fd %d", wait_shm_fd);
+
+       return wait_shm_fd;
+
+error:
+       DBG("Failing to get the wait shm fd");
+
+       return -1;
+}
+
+/*
+ * Return the wait shm mmap for UST application notification. The global
+ * variable is used to indicate if the the session daemon is global
+ * (root:tracing) or running with an unprivileged user.
+ *
+ * This returned value is used by futex_wait_update() in futex.c to WAKE all
+ * waiters which are UST application waiting for a session daemon.
+ */
+char *shm_ust_get_mmap(char *shm_path, int global)
+{
+       size_t mmap_size;
+       int wait_shm_fd, ret;
+       char *wait_shm_mmap;
+       long sys_page_size;
+
+       assert(shm_path);
+
+       sys_page_size = sysconf(_SC_PAGE_SIZE);
+       if (sys_page_size < 0) {
+               PERROR("sysconf PAGE_SIZE");
+               goto error;
+       }
+       mmap_size = sys_page_size;
+
+       wait_shm_fd = get_wait_shm(shm_path, mmap_size, global);
+       if (wait_shm_fd < 0) {
+               goto error;
+       }
+
+       wait_shm_mmap = mmap(NULL, mmap_size, PROT_WRITE | PROT_READ,
+                       MAP_SHARED, wait_shm_fd, 0);
+
+       /* close shm fd immediately after taking the mmap reference */
+       ret = close(wait_shm_fd);
+       if (ret) {
+               PERROR("Error closing fd");
+       }
+
+       if (wait_shm_mmap == MAP_FAILED) {
+               DBG("mmap error (can be caused by race with ust).");
+               goto error;
+       }
+
+       return wait_shm_mmap;
+
+error:
+       return NULL;
+}
+
+/*
+ * shm_create_anonymous is never called concurrently within a process.
+ */
+int shm_create_anonymous(const char *owner_name)
+{
+       char tmp_name[NAME_MAX];
+       int shmfd, ret;
+
+       ret = snprintf(tmp_name, NAME_MAX, "/shm-%s-%d", owner_name, getpid());
+       if (ret < 0) {
+               PERROR("snprintf");
+               return -1;
+       }
+       /*
+        * Allocate shm, and immediately unlink its shm oject, keeping only the
+        * file descriptor as a reference to the object.
+        */
+       shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700);
+       if (shmfd < 0) {
+               PERROR("shm_open");
+               goto error_shm_open;
+       }
+       ret = shm_unlink(tmp_name);
+       if (ret < 0 && errno != ENOENT) {
+               PERROR("shm_unlink");
+               goto error_shm_release;
+       }
+       return shmfd;
+
+error_shm_release:
+       ret = close(shmfd);
+       if (ret) {
+               PERROR("close");
+       }
+error_shm_open:
+       return -1;
+}
diff --git a/src/common/shm.h b/src/common/shm.h
new file mode 100644 (file)
index 0000000..d714506
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef _LTT_SHM_H
+#define _LTT_SHM_H
+
+char *shm_ust_get_mmap(char *shm_path, int global);
+
+int shm_create_anonymous(const char *owner_name);
+
+#endif /* _LTT_SHM_H */
index 75d1738d9ee2f4c38e0137f9e6779b4ec350943d..242d0f71ed8e698ccae7a78a86f118346de93f02 100644 (file)
@@ -966,8 +966,8 @@ enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
        pthread_mutex_lock(&chunk->lock);
        if (chunk->credentials.is_set) {
                if (chunk->credentials.value.use_current_user) {
        pthread_mutex_lock(&chunk->lock);
        if (chunk->credentials.is_set) {
                if (chunk->credentials.value.use_current_user) {
-                       credentials->uid = geteuid();
-                       credentials->gid = getegid();
+                       LTTNG_OPTIONAL_SET(&credentials->uid, geteuid());
+                       LTTNG_OPTIONAL_SET(&credentials->gid, getegid());
                } else {
                        *credentials = chunk->credentials.value.user;
                }
                } else {
                        *credentials = chunk->credentials.value.user;
                }
index 2ef77b2360a2bf8ab1ea80cdf513da2b9d46ce2b..77f40af194bbe5fa764a9016ad53a766894e0a17 100644 (file)
@@ -7,16 +7,32 @@
 
 #include <lttng/trigger/trigger-internal.h>
 #include <lttng/condition/condition-internal.h>
 
 #include <lttng/trigger/trigger-internal.h>
 #include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/condition/buffer-usage.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-expr-internal.h>
 #include <lttng/action/action-internal.h>
 #include <common/credentials.h>
 #include <common/payload.h>
 #include <common/payload-view.h>
 #include <lttng/action/action-internal.h>
 #include <common/credentials.h>
 #include <common/payload.h>
 #include <common/payload-view.h>
+#include <lttng/domain.h>
 #include <common/error.h>
 #include <common/error.h>
+#include <common/dynamic-array.h>
 #include <common/optional.h>
 #include <assert.h>
 #include <common/optional.h>
 #include <assert.h>
+#include <inttypes.h>
 
 
+static void destroy_lttng_condition_event_rule_capture_bytecode_element(void *ptr)
+{
+       struct lttng_condition_event_rule_capture_bytecode_element *element =
+                       ptr;
+       lttng_event_expr_destroy(element->expression);
+       free(element->bytecode);
+       free(element);
+}
 LTTNG_HIDDEN
 LTTNG_HIDDEN
-bool lttng_trigger_validate(struct lttng_trigger *trigger)
+bool lttng_trigger_validate(const struct lttng_trigger *trigger)
 {
        bool valid;
 
 {
        bool valid;
 
@@ -25,6 +41,11 @@ bool lttng_trigger_validate(struct lttng_trigger *trigger)
                goto end;
        }
 
                goto end;
        }
 
+       if (!trigger->creds.uid.is_set) {
+               valid = false;
+               goto end;
+       }
+
        valid = lttng_condition_validate(trigger->condition) &&
                        lttng_action_validate(trigger->action);
 end:
        valid = lttng_condition_validate(trigger->condition) &&
                        lttng_action_validate(trigger->action);
 end:
@@ -48,12 +69,19 @@ struct lttng_trigger *lttng_trigger_create(
 
        urcu_ref_init(&trigger->ref);
 
 
        urcu_ref_init(&trigger->ref);
 
+       trigger->firing_policy.type = LTTNG_TRIGGER_FIRE_EVERY_N;
+       trigger->firing_policy.threshold = 1;
+       lttng_trigger_set_error_count(trigger, 0);
+
        lttng_condition_get(condition);
        trigger->condition = condition;
 
        lttng_action_get(action);
        trigger->action = action;
 
        lttng_condition_get(condition);
        trigger->condition = condition;
 
        lttng_action_get(action);
        trigger->action = action;
 
+       lttng_dynamic_pointer_array_init(&trigger->capture_bytecode_set,
+                       destroy_lttng_condition_event_rule_capture_bytecode_element);
+
 end:
        return trigger;
 }
 end:
        return trigger;
 }
@@ -69,11 +97,10 @@ struct lttng_condition *lttng_trigger_get_condition(
        return trigger ? trigger->condition : NULL;
 }
 
        return trigger ? trigger->condition : NULL;
 }
 
-LTTNG_HIDDEN
 const struct lttng_condition *lttng_trigger_get_const_condition(
                const struct lttng_trigger *trigger)
 {
 const struct lttng_condition *lttng_trigger_get_const_condition(
                const struct lttng_trigger *trigger)
 {
-       return trigger->condition;
+       return trigger ? trigger->condition : NULL;
 }
 
 
 }
 
 
@@ -88,11 +115,10 @@ struct lttng_action *lttng_trigger_get_action(
        return trigger ? trigger->action : NULL;
 }
 
        return trigger ? trigger->action : NULL;
 }
 
-LTTNG_HIDDEN
 const struct lttng_action *lttng_trigger_get_const_action(
                const struct lttng_trigger *trigger)
 {
 const struct lttng_action *lttng_trigger_get_const_action(
                const struct lttng_trigger *trigger)
 {
-       return trigger->action;
+       return trigger ? trigger->action : NULL;
 }
 
 static void trigger_destroy_ref(struct urcu_ref *ref)
 }
 
 static void trigger_destroy_ref(struct urcu_ref *ref)
@@ -103,6 +129,8 @@ static void trigger_destroy_ref(struct urcu_ref *ref)
        struct lttng_condition *condition =
                        lttng_trigger_get_condition(trigger);
 
        struct lttng_condition *condition =
                        lttng_trigger_get_condition(trigger);
 
+       lttng_dynamic_pointer_array_reset(&trigger->capture_bytecode_set);
+
        assert(action);
        assert(condition);
 
        assert(action);
        assert(condition);
 
@@ -110,6 +138,7 @@ static void trigger_destroy_ref(struct urcu_ref *ref)
        lttng_action_put(action);
        lttng_condition_put(condition);
 
        lttng_action_put(action);
        lttng_condition_put(condition);
 
+       free(trigger->name);
        free(trigger);
 }
 
        free(trigger);
 }
 
@@ -118,15 +147,41 @@ void lttng_trigger_destroy(struct lttng_trigger *trigger)
        lttng_trigger_put(trigger);
 }
 
        lttng_trigger_put(trigger);
 }
 
+static bool is_firing_policy_valid(enum lttng_trigger_firing_policy_type policy)
+{
+       bool valid = false;
+
+       switch (policy) {
+       case LTTNG_TRIGGER_FIRE_EVERY_N:
+       case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N:
+               valid = true;
+               break;
+       default:
+               valid = false;
+               break;
+       }
+
+       return valid;
+}
+
 LTTNG_HIDDEN
 ssize_t lttng_trigger_create_from_payload(
                struct lttng_payload_view *src_view,
                struct lttng_trigger **trigger)
 {
 LTTNG_HIDDEN
 ssize_t lttng_trigger_create_from_payload(
                struct lttng_payload_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_condition *condition = NULL;
        struct lttng_action *action = NULL;
        const struct lttng_trigger_comm *trigger_comm;
+       const char *name = NULL;
+       uint64_t firing_threshold;
+       int64_t error_count;
+       enum lttng_trigger_firing_policy_type firing_policy;
+       struct lttng_credentials creds = {
+               .uid = LTTNG_OPTIONAL_INIT_UNSET,
+               .gid = LTTNG_OPTIONAL_INIT_UNSET,
+       };
 
        if (!src_view || !trigger) {
                ret = -1;
 
        if (!src_view || !trigger) {
                ret = -1;
@@ -135,7 +190,35 @@ ssize_t lttng_trigger_create_from_payload(
 
        /* lttng_trigger_comm header */
        trigger_comm = (typeof(trigger_comm)) src_view->buffer.data;
 
        /* lttng_trigger_comm header */
        trigger_comm = (typeof(trigger_comm)) src_view->buffer.data;
+
+       /* Get the trigger creds */
+       LTTNG_OPTIONAL_SET(&creds.uid, trigger_comm->uid);
+
        offset += sizeof(*trigger_comm);
        offset += sizeof(*trigger_comm);
+
+       firing_policy = trigger_comm->policy_type;
+       if (!is_firing_policy_valid(firing_policy)) {
+               ret =-1;
+               goto end;
+       }
+
+       firing_threshold = trigger_comm->policy_threshold;
+       error_count = trigger_comm->error_count;
+       if (trigger_comm->name_length != 0) {
+               /* Name */
+               struct lttng_payload_view name_view =
+                               lttng_payload_view_from_view(
+                                               src_view, offset, trigger_comm->name_length);
+
+               name = name_view.buffer.data;
+               if (!lttng_buffer_view_contains_string(&name_view.buffer, name, trigger_comm->name_length)){
+                       ret = -1;
+                       goto end;
+               }
+               offset += trigger_comm->name_length;
+               name_size = trigger_comm->name_length;
+       }
+
        {
                /* struct lttng_condition */
                struct lttng_payload_view condition_view =
        {
                /* struct lttng_condition */
                struct lttng_payload_view condition_view =
@@ -168,7 +251,7 @@ ssize_t lttng_trigger_create_from_payload(
        offset += action_size;
 
        /* Unexpected size of inner-elements; the buffer is corrupted. */
        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;
        }
                ret = -1;
                goto error;
        }
@@ -179,6 +262,8 @@ ssize_t lttng_trigger_create_from_payload(
                goto error;
        }
 
                goto error;
        }
 
+       lttng_trigger_set_credentials(*trigger, &creds);
+
        /*
         * The trigger object owns references to the action and condition
         * objects.
        /*
         * The trigger object owns references to the action and condition
         * objects.
@@ -189,6 +274,22 @@ ssize_t lttng_trigger_create_from_payload(
        lttng_action_put(action);
        action = NULL;
 
        lttng_action_put(action);
        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;
+       }
+
+       lttng_trigger_set_error_count(*trigger, error_count);
+
        ret = offset;
 
 error:
        ret = offset;
 
 error:
@@ -203,13 +304,30 @@ end:
  * for the detailed format.
  */
 LTTNG_HIDDEN
  * for the detailed format.
  */
 LTTNG_HIDDEN
-int lttng_trigger_serialize(struct lttng_trigger *trigger,
+int lttng_trigger_serialize(const struct lttng_trigger *trigger,
                struct lttng_payload *payload)
 {
        int ret;
                struct lttng_payload *payload)
 {
        int ret;
-       size_t header_offset, size_before_payload;
+       size_t header_offset, size_before_payload, size_name;
        struct lttng_trigger_comm trigger_comm = {};
        struct lttng_trigger_comm *header;
        struct lttng_trigger_comm trigger_comm = {};
        struct lttng_trigger_comm *header;
+       const struct lttng_credentials *creds = NULL;
+
+       creds = lttng_trigger_get_credentials(trigger);
+       assert(creds);
+
+       trigger_comm.uid = LTTNG_OPTIONAL_GET(creds->uid);
+
+       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;
+       trigger_comm.error_count = (int64_t) lttng_trigger_get_error_count(trigger);
 
        header_offset = payload->buffer.size;
        ret = lttng_dynamic_buffer_append(&payload->buffer, &trigger_comm,
 
        header_offset = payload->buffer.size;
        ret = lttng_dynamic_buffer_append(&payload->buffer, &trigger_comm,
@@ -219,6 +337,14 @@ int lttng_trigger_serialize(struct lttng_trigger *trigger,
        }
 
        size_before_payload = payload->buffer.size;
        }
 
        size_before_payload = payload->buffer.size;
+
+       /* Trigger name */
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, trigger->name, size_name);
+       if (ret) {
+               goto end;
+       }
+
        ret = lttng_condition_serialize(trigger->condition, payload);
        if (ret) {
                goto end;
        ret = lttng_condition_serialize(trigger->condition, payload);
        if (ret) {
                goto end;
@@ -236,6 +362,118 @@ end:
        return ret;
 }
 
        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;
+       }
+
+       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_name(struct lttng_trigger *dst,
+               const struct lttng_trigger *src)
+{
+       int ret = 0;
+       enum lttng_trigger_status status;
+
+       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_tracer_token(struct lttng_trigger *trigger, uint64_t token)
+{
+       assert(trigger);
+       LTTNG_OPTIONAL_SET(&trigger->tracer_token, token);
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_trigger_get_tracer_token(const struct lttng_trigger *trigger)
+{
+       assert(trigger);
+
+       return LTTNG_OPTIONAL_GET(trigger->tracer_token);
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_set_error_counter_index(struct lttng_trigger *trigger,
+               uint64_t error_counter_index)
+{
+       assert(trigger);
+       LTTNG_OPTIONAL_SET(&trigger->error_counter_index, error_counter_index);
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_trigger_get_error_counter_index(
+               const struct lttng_trigger *trigger)
+{
+       assert(trigger);
+
+       return LTTNG_OPTIONAL_GET(trigger->error_counter_index);
+}
+
+LTTNG_HIDDEN
+int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset)
+{
+       int ret = 0;
+       char *generated_name = NULL;
+
+       ret = asprintf(&generated_name, "T%" PRIu64 "", offset);
+       if (ret < 0) {
+               ERR("Failed to generate trigger name");
+               ret = -1;
+               goto end;
+       }
+
+       free(trigger->name);
+       trigger->name = generated_name;
+end:
+       return ret;
+}
+
 LTTNG_HIDDEN
 void lttng_trigger_get(struct lttng_trigger *trigger)
 {
 LTTNG_HIDDEN
 void lttng_trigger_get(struct lttng_trigger *trigger)
 {
@@ -252,18 +490,516 @@ void lttng_trigger_put(struct lttng_trigger *trigger)
        urcu_ref_put(&trigger->ref , trigger_destroy_ref);
 }
 
        urcu_ref_put(&trigger->ref , trigger_destroy_ref);
 }
 
+static void delete_trigger_array_element(void *ptr)
+{
+       struct lttng_trigger *trigger = ptr;
+       lttng_trigger_put(trigger);
+}
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_equal(
+               const struct lttng_trigger *a, const struct lttng_trigger *b)
+{
+       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;
+       }
+
+       if (!lttng_credentials_is_equal(lttng_trigger_get_credentials(a),
+                       lttng_trigger_get_credentials(b))) {
+               return false;
+       }
+
+       return true;
+}
+
+LTTNG_HIDDEN
+struct lttng_triggers *lttng_triggers_create(void)
+{
+       struct lttng_triggers *triggers = NULL;
+
+       triggers = zmalloc(sizeof(*triggers));
+       if (!triggers) {
+               goto end;
+       }
+
+       lttng_dynamic_pointer_array_init(&triggers->array, delete_trigger_array_element);
+
+end:
+       return triggers;
+}
+
+LTTNG_HIDDEN
+struct lttng_trigger *lttng_triggers_get_pointer_of_index(
+               const struct lttng_triggers *triggers, unsigned int index)
+{
+       struct lttng_trigger *trigger = NULL;
+       assert(triggers);
+       if (index >= lttng_dynamic_pointer_array_get_count(&triggers->array)) {
+               goto end;
+       }
+
+       trigger = (struct lttng_trigger *)
+                       lttng_dynamic_pointer_array_get_pointer(
+                                       &triggers->array, index);
+end:
+       return trigger;
+}
+
+LTTNG_HIDDEN
+int lttng_triggers_add(
+               struct lttng_triggers *triggers, struct lttng_trigger *trigger)
+{
+       int ret;
+
+       assert(triggers);
+       assert(trigger);
+
+       lttng_trigger_get(trigger);
+
+       ret = lttng_dynamic_pointer_array_add_pointer(&triggers->array, trigger);
+       if (ret) {
+               lttng_trigger_put(trigger);
+       }
+
+       return ret;
+}
+
+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_payload *payload)
+{
+       int ret;
+       unsigned int count;
+       size_t header_offset, size_before_payload;
+       struct lttng_triggers_comm triggers_comm = {};
+       struct lttng_triggers_comm *header;
+       enum lttng_trigger_status status;
+
+       header_offset = payload->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(&payload->buffer, &triggers_comm,
+                       sizeof(triggers_comm));
+       if (ret) {
+               goto end;
+       }
+
+       size_before_payload = payload->buffer.size;
+
+       for (int i = 0; i < count; i++) {
+               const struct lttng_trigger *trigger =
+                               lttng_triggers_get_at_index(triggers, i);
+               assert(trigger);
+
+               ret = lttng_trigger_serialize(trigger, payload);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       /* Update payload size. */
+       header = (struct lttng_triggers_comm *) ((char *) payload->buffer.data + header_offset);
+       header->length = payload->buffer.size - size_before_payload;
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_triggers_create_from_payload(
+               struct lttng_payload_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_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->buffer.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;
+               struct lttng_payload_view trigger_view =
+                       lttng_payload_view_from_view(src_view, offset, -1);
+               trigger_size = lttng_trigger_create_from_payload(&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);
+               lttng_trigger_put(trigger);
+               if (ret < 0) {
+                       ret = -1;
+                       goto error;
+               }
+
+               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)
 {
 LTTNG_HIDDEN
 const struct lttng_credentials *lttng_trigger_get_credentials(
                const struct lttng_trigger *trigger)
 {
-       return LTTNG_OPTIONAL_GET_PTR(trigger->creds);
+       return &trigger->creds;
 }
 
 LTTNG_HIDDEN
 }
 
 LTTNG_HIDDEN
-void lttng_trigger_set_credentials(
-               struct lttng_trigger *trigger,
+void lttng_trigger_set_credentials(struct lttng_trigger *trigger,
                const struct lttng_credentials *creds)
 {
        assert(creds);
                const struct lttng_credentials *creds)
 {
        assert(creds);
-       LTTNG_OPTIONAL_SET(&trigger->creds, *creds);
+       trigger->creds = *creds;
+}
+
+enum lttng_trigger_status lttng_trigger_set_user_identity(
+               struct lttng_trigger *trigger, uid_t uid)
+{
+       enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK;
+       struct lttng_credentials creds = {
+               .uid = LTTNG_OPTIONAL_INIT_UNSET,
+               .gid = LTTNG_OPTIONAL_INIT_UNSET,
+       };
+
+       if (!trigger) {
+               ret = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       if (geteuid() != 0) {
+               ret = LTTNG_TRIGGER_STATUS_EPERM;
+               goto end;
+       }
+
+       LTTNG_OPTIONAL_SET(&creds.uid, uid);
+
+       lttng_trigger_set_credentials(trigger, &creds);
+
+end:
+       return ret;
+}
+
+enum lttng_trigger_status lttng_trigger_get_user_identity(
+               const struct lttng_trigger *trigger, uid_t *uid)
+{
+       enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK;
+       const struct lttng_credentials *creds = NULL;
+
+       if (!trigger || !uid ) {
+               ret = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       if (!trigger->creds.uid.is_set ) {
+               ret = LTTNG_TRIGGER_STATUS_UNSET;
+               goto end;
+       }
+
+       creds = lttng_trigger_get_credentials(trigger);
+       *uid = lttng_credentials_get_uid(creds);
+
+end:
+       return ret;
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_trigger_get_error_count(
+               const struct lttng_trigger *trigger)
+{
+       return LTTNG_OPTIONAL_GET(trigger->error_count);
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_set_error_count(
+               struct lttng_trigger *trigger,
+               uint64_t error_count)
+{
+       LTTNG_OPTIONAL_SET(&trigger->error_count, error_count);
+}
+
+enum lttng_trigger_status lttng_trigger_set_firing_policy(
+               struct lttng_trigger *trigger,
+               enum lttng_trigger_firing_policy_type policy_type,
+               uint64_t 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,
+               uint64_t *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_should_fire(const struct lttng_trigger *trigger)
+{
+       assert(trigger);
+       bool ready_to_fire = false;
+
+       switch (trigger->firing_policy.type) {
+       case LTTNG_TRIGGER_FIRE_EVERY_N:
+               if (trigger->firing_policy.current_count < trigger->firing_policy.threshold) {
+                       ready_to_fire = true;
+               }
+               break;
+       case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N:
+               if (trigger->firing_policy.current_count < trigger->firing_policy.threshold) {
+                       ready_to_fire = true;
+               }
+               break;
+       default:
+               abort();
+       };
+
+       return ready_to_fire;
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_fire(struct lttng_trigger *trigger)
+{
+       assert(trigger);
+
+       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;
+               }
+               break;
+       case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N:
+                       /*
+                        * TODO: deactivate the trigger condition on
+                        * remove any work overhead on the
+                        * traced application or kernel since the trigger will
+                        * never fire again.
+                        */
+               break;
+       default:
+               abort();
+       };
+}
+
+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;
+}
+
+LTTNG_HIDDEN
+struct lttng_trigger *lttng_trigger_copy(const struct lttng_trigger *trigger)
+{
+       int ret;
+       struct lttng_payload copy_buffer;
+       struct lttng_trigger *copy = NULL;
+
+       lttng_payload_init(&copy_buffer);
+
+       ret = lttng_trigger_serialize(trigger, &copy_buffer);
+       if (ret < 0) {
+               goto end;
+       }
+
+       {
+               struct lttng_payload_view view =
+                               lttng_payload_view_from_payload(
+                                               &copy_buffer, 0, -1);
+               ret = lttng_trigger_create_from_payload(
+                               &view, &copy);
+               if (ret < 0) {
+                       copy = NULL;
+                       goto end;
+               }
+       }
+
+end:
+       lttng_payload_reset(&copy_buffer);
+       return copy;
+}
+
+LTTNG_HIDDEN
+unsigned int lttng_trigger_get_capture_bytecode_count(
+               const struct lttng_trigger *trigger)
+{
+       unsigned int count = 0;
+       if (!trigger) {
+               goto end;
+       }
+
+       count = lttng_dynamic_pointer_array_get_count(
+                       &trigger->capture_bytecode_set);
+
+end:
+       return count;
+}
+
+LTTNG_HIDDEN
+const struct lttng_bytecode *
+lttng_trigger_get_capture_bytecode_at_index(
+               const struct lttng_trigger *trigger, unsigned int index)
+{
+       struct lttng_condition_event_rule_capture_bytecode_element *element = NULL;
+       struct lttng_bytecode *bytecode = NULL;
+
+       element = lttng_dynamic_pointer_array_get_pointer(
+                       &trigger->capture_bytecode_set, index);
+
+       if (element == NULL) {
+               goto end;
+       }
+       bytecode = element->bytecode;
+end:
+       return bytecode;
 }
 }
index 209931f5644a3d74dc27ed1a8f7dbb3d3863d387..cd9b273c28f32f36be201df68263100a11c7b7bb 100644 (file)
@@ -37,6 +37,7 @@
 #include <common/utils.h>
 #include <common/index/index.h>
 #include <common/consumer/consumer.h>
 #include <common/utils.h>
 #include <common/index/index.h>
 #include <common/consumer/consumer.h>
+#include <common/shm.h>
 #include <common/optional.h>
 
 #include "ust-consumer.h"
 #include <common/optional.h>
 
 #include "ust-consumer.h"
@@ -352,48 +353,6 @@ error_alloc:
        return ret;
 }
 
        return ret;
 }
 
-/*
- * create_posix_shm is never called concurrently within a process.
- */
-static
-int create_posix_shm(void)
-{
-       char tmp_name[NAME_MAX];
-       int shmfd, ret;
-
-       ret = snprintf(tmp_name, NAME_MAX, "/ust-shm-consumer-%d", getpid());
-       if (ret < 0) {
-               PERROR("snprintf");
-               return -1;
-       }
-       /*
-        * Allocate shm, and immediately unlink its shm oject, keeping
-        * only the file descriptor as a reference to the object.
-        * We specifically do _not_ use the / at the beginning of the
-        * pathname so that some OS implementations can keep it local to
-        * the process (POSIX leaves this implementation-defined).
-        */
-       shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700);
-       if (shmfd < 0) {
-               PERROR("shm_open");
-               goto error_shm_open;
-       }
-       ret = shm_unlink(tmp_name);
-       if (ret < 0 && errno != ENOENT) {
-               PERROR("shm_unlink");
-               goto error_shm_release;
-       }
-       return shmfd;
-
-error_shm_release:
-       ret = close(shmfd);
-       if (ret) {
-               PERROR("close");
-       }
-error_shm_open:
-       return -1;
-}
-
 static int open_ust_stream_fd(struct lttng_consumer_channel *channel, int cpu,
                const struct lttng_credentials *session_credentials)
 {
 static int open_ust_stream_fd(struct lttng_consumer_channel *channel, int cpu,
                const struct lttng_credentials *session_credentials)
 {
@@ -401,7 +360,7 @@ static int open_ust_stream_fd(struct lttng_consumer_channel *channel, int cpu,
        int ret;
 
        if (!channel->shm_path[0]) {
        int ret;
 
        if (!channel->shm_path[0]) {
-               return create_posix_shm();
+               return shm_create_anonymous("ust-consumer");
        }
        ret = get_stream_shm_path(shm_path, channel->shm_path, cpu);
        if (ret) {
        }
        ret = get_stream_shm_path(shm_path, channel->shm_path, cpu);
        if (ret) {
@@ -409,7 +368,8 @@ static int open_ust_stream_fd(struct lttng_consumer_channel *channel, int cpu,
        }
        return run_as_open(shm_path,
                O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR,
        }
        return run_as_open(shm_path,
                O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR,
-               session_credentials->uid, session_credentials->gid);
+               lttng_credentials_get_uid(session_credentials),
+               lttng_credentials_get_gid(session_credentials));
 
 error_shm_path:
        return -1;
 
 error_shm_path:
        return -1;
@@ -487,8 +447,10 @@ error_open:
                                ERR("Cannot get stream shm path");
                        }
                        closeret = run_as_unlink(shm_path,
                                ERR("Cannot get stream shm path");
                        }
                        closeret = run_as_unlink(shm_path,
-                                       channel->buffer_credentials.value.uid,
-                                       channel->buffer_credentials.value.gid);
+                                       lttng_credentials_get_uid(LTTNG_OPTIONAL_GET_PTR(
+                                                       channel->buffer_credentials)),
+                                       lttng_credentials_get_gid(LTTNG_OPTIONAL_GET_PTR(
+                                                       channel->buffer_credentials)));
                        if (closeret) {
                                PERROR("unlink %s", shm_path);
                        }
                        if (closeret) {
                                PERROR("unlink %s", shm_path);
                        }
@@ -497,8 +459,10 @@ error_open:
        /* Try to rmdir all directories under shm_path root. */
        if (channel->root_shm_path[0]) {
                (void) run_as_rmdir_recursive(channel->root_shm_path,
        /* Try to rmdir all directories under shm_path root. */
        if (channel->root_shm_path[0]) {
                (void) run_as_rmdir_recursive(channel->root_shm_path,
-                               channel->buffer_credentials.value.uid,
-                               channel->buffer_credentials.value.gid,
+                               lttng_credentials_get_uid(LTTNG_OPTIONAL_GET_PTR(
+                                               channel->buffer_credentials)),
+                               lttng_credentials_get_gid(LTTNG_OPTIONAL_GET_PTR(
+                                               channel->buffer_credentials)),
                                LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
        }
        free(stream_fds);
                                LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
        }
        free(stream_fds);
@@ -1461,8 +1425,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                struct ustctl_consumer_channel_attr attr;
                const uint64_t chunk_id = msg.u.ask_channel.chunk_id.value;
                const struct lttng_credentials buffer_credentials = {
                struct ustctl_consumer_channel_attr attr;
                const uint64_t chunk_id = msg.u.ask_channel.chunk_id.value;
                const struct lttng_credentials buffer_credentials = {
-                       .uid = msg.u.ask_channel.buffer_credentials.uid,
-                       .gid = msg.u.ask_channel.buffer_credentials.gid,
+                       .uid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.ask_channel.buffer_credentials.uid),
+                       .gid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.ask_channel.buffer_credentials.gid),
                };
 
                /* Create a plain object and reserve a channel key. */
                };
 
                /* Create a plain object and reserve a channel key. */
@@ -2072,8 +2036,8 @@ end_rotate_channel_nosignal:
        case LTTNG_CONSUMER_CREATE_TRACE_CHUNK:
        {
                const struct lttng_credentials credentials = {
        case LTTNG_CONSUMER_CREATE_TRACE_CHUNK:
        {
                const struct lttng_credentials credentials = {
-                       .uid = msg.u.create_trace_chunk.credentials.value.uid,
-                       .gid = msg.u.create_trace_chunk.credentials.value.gid,
+                       .uid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.create_trace_chunk.credentials.value.uid),
+                       .gid = LTTNG_OPTIONAL_INIT_VALUE(msg.u.create_trace_chunk.credentials.value.gid),
                };
                const bool is_local_trace =
                                !msg.u.create_trace_chunk.relayd_id.is_set;
                };
                const bool is_local_trace =
                                !msg.u.create_trace_chunk.relayd_id.is_set;
@@ -2402,8 +2366,10 @@ void lttng_ustconsumer_del_channel(struct lttng_consumer_channel *chan)
                                ERR("Cannot get stream shm path");
                        }
                        ret = run_as_unlink(shm_path,
                                ERR("Cannot get stream shm path");
                        }
                        ret = run_as_unlink(shm_path,
-                                       chan->buffer_credentials.value.uid,
-                                       chan->buffer_credentials.value.gid);
+                                       lttng_credentials_get_uid(LTTNG_OPTIONAL_GET_PTR(
+                                                       chan->buffer_credentials)),
+                                       lttng_credentials_get_gid(LTTNG_OPTIONAL_GET_PTR(
+                                                       chan->buffer_credentials)));
                        if (ret) {
                                PERROR("unlink %s", shm_path);
                        }
                        if (ret) {
                                PERROR("unlink %s", shm_path);
                        }
@@ -2422,8 +2388,10 @@ void lttng_ustconsumer_free_channel(struct lttng_consumer_channel *chan)
        /* Try to rmdir all directories under shm_path root. */
        if (chan->root_shm_path[0]) {
                (void) run_as_rmdir_recursive(chan->root_shm_path,
        /* Try to rmdir all directories under shm_path root. */
        if (chan->root_shm_path[0]) {
                (void) run_as_rmdir_recursive(chan->root_shm_path,
-                               chan->buffer_credentials.value.uid,
-                               chan->buffer_credentials.value.gid,
+                               lttng_credentials_get_uid(LTTNG_OPTIONAL_GET_PTR(
+                                               chan->buffer_credentials)),
+                               lttng_credentials_get_gid(LTTNG_OPTIONAL_GET_PTR(
+                                               chan->buffer_credentials)),
                                LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
        }
        free(chan->stream_fds);
                                LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
        }
        free(chan->stream_fds);
index 2a20e5a8c4ab4cca1557ca917cfa2de02defd905..3497f92dbb00aba9c07186c355f9b881b1a0f619 100644 (file)
@@ -679,8 +679,8 @@ int utils_mkdir(const char *path, mode_t mode, int uid, int gid)
        int ret;
        struct lttng_directory_handle *handle;
        const struct lttng_credentials creds = {
        int ret;
        struct lttng_directory_handle *handle;
        const struct lttng_credentials creds = {
-               .uid = (uid_t) uid,
-               .gid = (gid_t) gid,
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(gid),
        };
 
        handle = lttng_directory_handle_create(NULL);
        };
 
        handle = lttng_directory_handle_create(NULL);
@@ -708,8 +708,8 @@ int utils_mkdir_recursive(const char *path, mode_t mode, int uid, int gid)
        int ret;
        struct lttng_directory_handle *handle;
        const struct lttng_credentials creds = {
        int ret;
        struct lttng_directory_handle *handle;
        const struct lttng_credentials creds = {
-               .uid = (uid_t) uid,
-               .gid = (gid_t) gid,
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(gid),
        };
 
        handle = lttng_directory_handle_create(NULL);
        };
 
        handle = lttng_directory_handle_create(NULL);
@@ -1669,3 +1669,40 @@ end:
        free(buf);
        return ret_val;
 }
        free(buf);
        return ret_val;
 }
+
+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 dec4cd8aa2268490eae52adb55795d76ab62a07b..570216d53c9709dddcfaf480f68a465bc0a8f294 100644 (file)
@@ -59,4 +59,16 @@ enum lttng_error_code utils_user_id_from_name(
 enum lttng_error_code utils_group_id_from_name(
                const char *group_name, gid_t *group_id);
 
 enum lttng_error_code utils_group_id_from_name(
                const char *group_name, gid_t *group_id);
 
+/*
+ * 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 */
 #endif /* _COMMON_UTILS_H */
index 3f1ab9fdc4bb8f17abe91345355b11f3263d61e2..3216e5002efb950f00055ffa0f3b4e6f56f0ac6d 100644 (file)
@@ -19,6 +19,7 @@
 #include <string.h>
 #include <unistd.h>
 
 #include <string.h>
 #include <unistd.h>
 
+#include <common/bytecode/bytecode.h>
 #include <common/common.h>
 #include <common/compat/string.h>
 #include <common/defaults.h>
 #include <common/common.h>
 #include <common/compat/string.h>
 #include <common/defaults.h>
@@ -45,7 +46,6 @@
 
 #include <common/filter/filter-ast.h>
 #include <common/filter/filter-parser.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"
 
 #include <common/filter/memstream.h>
 #include "lttng-ctl-helper.h"
 
@@ -2956,6 +2956,13 @@ int lttng_register_trigger(struct lttng_trigger *trigger)
        struct lttcomm_session_msg *message_lsm;
        struct lttng_payload message;
        struct lttng_payload reply;
        struct lttcomm_session_msg *message_lsm;
        struct lttng_payload message;
        struct lttng_payload reply;
+       struct lttng_trigger *reply_trigger = NULL;
+       enum lttng_domain_type domain_type;
+       struct lttng_credentials creds = {
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(geteuid()),
+               .gid = LTTNG_OPTIONAL_INIT_UNSET,
+       };
+
 
        lttng_payload_init(&message);
        lttng_payload_init(&reply);
 
        lttng_payload_init(&message);
        lttng_payload_init(&reply);
@@ -2965,11 +2972,39 @@ int lttng_register_trigger(struct lttng_trigger *trigger)
                goto end;
        }
 
                goto end;
        }
 
+       if (!trigger->creds.uid.is_set) {
+               /* Use the client credentials as the trigger credentials */
+               lttng_trigger_set_credentials(trigger, &creds);
+       } else {
+               /*
+                * Validate that either the current trigger credentials and the
+                * client credentials are identical or that the current user is
+                * root. The root user can register, unregister triggers for
+                * himself and other users.
+                * This check is also present on the sessiond side, using the
+                * credentials passed on the socket. These check are all
+                * "safety" checks.
+                */
+               const struct lttng_credentials *trigger_creds =
+                               lttng_trigger_get_credentials(trigger);
+               if (!lttng_credentials_is_equal_uid(trigger_creds, &creds)) {
+                       if (lttng_credentials_get_uid(&creds) != 0) {
+                               ret = -LTTNG_ERR_EPERM;
+                               goto end;
+                       }
+               }
+       }
+
        if (!lttng_trigger_validate(trigger)) {
                ret = -LTTNG_ERR_INVALID_TRIGGER;
                goto end;
        }
 
        if (!lttng_trigger_validate(trigger)) {
                ret = -LTTNG_ERR_INVALID_TRIGGER;
                goto end;
        }
 
+       domain_type = lttng_trigger_get_underlying_domain_type_restriction(
+                       trigger);
+
+       lsm.domain.type = domain_type;
+
        ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm));
        if (ret) {
                ret = -LTTNG_ERR_NOMEM;
        ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm));
        if (ret) {
                ret = -LTTNG_ERR_NOMEM;
@@ -2990,6 +3025,39 @@ int lttng_register_trigger(struct lttng_trigger *trigger)
 
        message_lsm->u.trigger.length = (uint32_t) message.buffer.size - sizeof(lsm);
 
 
        message_lsm->u.trigger.length = (uint32_t) message.buffer.size - sizeof(lsm);
 
+       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.
+                */
+               ssize_t sz;
+
+               struct lttng_payload_view pv = lttng_payload_view_from_payload(
+                               &message, sizeof(lsm), -1);
+               sz = lttng_trigger_create_from_payload(&pv, &reply_trigger);
+               if (sz != pv.buffer.size - sizeof(lsm)) {
+                       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;
+       }
+
        {
                struct lttng_payload_view message_view =
                                lttng_payload_view_from_payload(
        {
                struct lttng_payload_view message_view =
                                lttng_payload_view_from_payload(
@@ -3003,20 +3071,45 @@ int lttng_register_trigger(struct lttng_trigger *trigger)
                }
        }
 
                }
        }
 
+       {
+               struct lttng_payload_view reply_view =
+                               lttng_payload_view_from_payload(
+                                               &reply, 0, reply.buffer.size);
+
+               ret = lttng_trigger_create_from_payload(
+                               &reply_view, &reply_trigger);
+               if (ret < 0) {
+                       ret = -LTTNG_ERR_FATAL;
+                       goto end;
+               }
+       }
+
+       ret = lttng_trigger_assign_name(trigger, reply_trigger);
+       if (ret < 0) {
+               ret = -LTTNG_ERR_FATAL;
+               goto end;
+       }
+
        ret = 0;
 end:
        lttng_payload_reset(&message);
        lttng_payload_reset(&reply);
        ret = 0;
 end:
        lttng_payload_reset(&message);
        lttng_payload_reset(&reply);
+       lttng_trigger_destroy(reply_trigger);
        return ret;
 }
 
        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;
        struct lttcomm_session_msg *message_lsm;
        struct lttng_payload message;
        struct lttng_payload reply;
 {
        int ret;
        struct lttcomm_session_msg lsm;
        struct lttcomm_session_msg *message_lsm;
        struct lttng_payload message;
        struct lttng_payload reply;
+       struct lttng_trigger *copy = NULL;
+       struct lttng_credentials creds = {
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(geteuid()),
+               .gid = LTTNG_OPTIONAL_INIT_UNSET,
+       };
 
        lttng_payload_init(&message);
        lttng_payload_init(&reply);
 
        lttng_payload_init(&message);
        lttng_payload_init(&reply);
@@ -3026,7 +3119,36 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger)
                goto end;
        }
 
                goto end;
        }
 
-       if (!lttng_trigger_validate(trigger)) {
+       copy = lttng_trigger_copy(trigger);
+       if (!copy) {
+               ret = -LTTNG_ERR_UNK;
+               goto end;
+       }
+
+       if (!copy->creds.uid.is_set) {
+               /* Use the client credentials as the trigger credentials */
+               lttng_trigger_set_credentials(copy, &creds);
+       } else {
+               /*
+                * Validate that either the current trigger credentials and the
+                * client credentials are identical or that the current user is
+                * root. The root user can register, unregister triggers for
+                * himself and other users.
+                * This check is also present on the sessiond side, using the
+                * credentials passed on the socket. These check are all
+                * "safety" checks.
+                */
+               const struct lttng_credentials *trigger_creds =
+                               lttng_trigger_get_credentials(copy);
+               if (!lttng_credentials_is_equal_uid(trigger_creds, &creds)) {
+                       if (lttng_credentials_get_uid(&creds) != 0) {
+                               ret = -LTTNG_ERR_EPERM;
+                               goto end;
+                       }
+               }
+       }
+
+       if (!lttng_trigger_validate(copy)) {
                ret = -LTTNG_ERR_INVALID_TRIGGER;
                goto end;
        }
                ret = -LTTNG_ERR_INVALID_TRIGGER;
                goto end;
        }
@@ -3046,7 +3168,7 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger)
        */
        message_lsm = (struct lttcomm_session_msg *) message.buffer.data;
 
        */
        message_lsm = (struct lttcomm_session_msg *) message.buffer.data;
 
-       ret = lttng_trigger_serialize(trigger, &message);
+       ret = lttng_trigger_serialize(copy, &message);
        if (ret < 0) {
                ret = -LTTNG_ERR_UNK;
                goto end;
        if (ret < 0) {
                ret = -LTTNG_ERR_UNK;
                goto end;
@@ -3074,11 +3196,58 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger)
 
        ret = 0;
 end:
 
        ret = 0;
 end:
+       lttng_trigger_destroy(copy);
        lttng_payload_reset(&message);
        lttng_payload_reset(&reply);
        return ret;
 }
 
        lttng_payload_reset(&message);
        lttng_payload_reset(&reply);
        return ret;
 }
 
+/*
+ * Ask the session daemon for all registered triggers.
+ * Allocate a lttng_triggers collection.
+ * On error, returns a negative value.
+ */
+enum lttng_error_code lttng_list_triggers(struct lttng_triggers **triggers)
+{
+       int ret;
+       struct lttcomm_session_msg lsm;
+       struct lttng_payload_view lsm_view =
+                       lttng_payload_view_init_from_buffer(
+                               (const char *) &lsm, 0, sizeof(lsm));
+       struct lttng_triggers *local_triggers = NULL;
+       struct lttng_payload reply;
+
+       lttng_payload_init(&reply);
+
+       memset(&lsm, 0, sizeof(lsm));
+       lsm.cmd_type = LTTNG_LIST_TRIGGERS;
+
+       ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply);
+       if (ret < 0) {
+               goto end;
+       }
+
+       {
+               struct lttng_payload_view reply_view =
+                               lttng_payload_view_from_payload(
+                                               &reply, 0, reply.buffer.size);
+               ret = lttng_triggers_create_from_payload(
+                               &reply_view, &local_triggers);
+               if (ret < 0) {
+                       ret = -LTTNG_ERR_FATAL;
+                       goto end;
+               }
+       }
+
+       *triggers = local_triggers;
+       local_triggers = NULL;
+       ret = LTTNG_OK;
+end:
+       lttng_payload_reset(&reply);
+       lttng_triggers_destroy(local_triggers);
+       return ret;
+}
+
 /*
  * lib constructor.
  */
 /*
  * lib constructor.
  */
index 2d7725c96b96394247fc8a642be782aa06e6331e..8d3ec201363f595b2520294b9608b16cc3336b2d 100644 (file)
@@ -238,29 +238,29 @@ void lttng_snapshot_output_destroy(struct lttng_snapshot_output *obj)
  * Getter family functions of snapshot output.
  */
 
  * 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(
 {
        return output->id;
 }
 
 const char *lttng_snapshot_output_get_name(
-               struct lttng_snapshot_output *output)
+               const struct lttng_snapshot_output *output)
 {
        return output->name;
 }
 
 {
        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;
 }
 
 {
        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(
 {
        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;
 }
 {
        return output->max_size;
 }
diff --git a/src/vendor/Makefile.am b/src/vendor/Makefile.am
new file mode 100644 (file)
index 0000000..e9c2fcf
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+SUBDIRS = msgpack
diff --git a/src/vendor/msgpack/Makefile.am b/src/vendor/msgpack/Makefile.am
new file mode 100644 (file)
index 0000000..6666b61
--- /dev/null
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+AM_CPPFLAGS = -I$(top_srcdir)/src
+
+noinst_LTLIBRARIES = libmsgpack.la
+
+AM_CFLAGS += -fvisibility=hidden
+
+libmsgpack_la_SOURCES = \
+       fbuffer.h \
+       gcc_atomic.h \
+       lttng-config.h \
+       msgpack.h \
+       objectc.c \
+       object.h \
+       pack_define.h \
+       pack.h \
+       pack_template.h \
+       predef.h \
+       sbuffer.h \
+       sysdep.h \
+       timestamp.h \
+       unpack.c \
+       unpack_define.h \
+       unpack.h \
+       unpack_template.h \
+       util.h \
+       version.c \
+       version.h \
+       version_master.h \
+       vrefbuffer.c \
+       vrefbuffer.h \
+       zbuffer.h \
+       zone.c \
+       zone.h
diff --git a/src/vendor/msgpack/fbuffer.h b/src/vendor/msgpack/fbuffer.h
new file mode 100644 (file)
index 0000000..d478008
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * MessagePack for C FILE* buffer adaptor
+ *
+ * Copyright (C) 2013 Vladimir Volodko
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_FBUFFER_H
+#define MSGPACK_FBUFFER_H
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @defgroup msgpack_fbuffer FILE* buffer
+ * @ingroup msgpack_buffer
+ * @{
+ */
+
+static inline int msgpack_fbuffer_write(void* data, const char* buf, size_t len)
+{
+    return (1 == fwrite(buf, len, 1, (FILE *)data)) ? 0 : -1;
+}
+
+/** @} */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/fbuffer.h */
diff --git a/src/vendor/msgpack/gcc_atomic.h b/src/vendor/msgpack/gcc_atomic.h
new file mode 100644 (file)
index 0000000..6b1b1a7
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#ifndef MSGPACK_GCC_ATOMIC_H
+#define MSGPACK_GCC_ATOMIC_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef int _msgpack_atomic_counter_t;
+
+int _msgpack_sync_decr_and_fetch(volatile _msgpack_atomic_counter_t* ptr);
+int _msgpack_sync_incr_and_fetch(volatile _msgpack_atomic_counter_t* ptr);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+
+#endif // MSGPACK_GCC_ATOMIC_H
diff --git a/src/vendor/msgpack/lttng-config.h b/src/vendor/msgpack/lttng-config.h
new file mode 100644 (file)
index 0000000..073bf8d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 Michael Jeanson <mjeanson@efficios.com>
+ *
+ * SPDX-License-Identifier: BSL-1.0
+ *
+ */
+
+#ifndef MSGPACK_LTTNG_CONFIG_H
+#define MSGPACK_LTTNG_CONFIG_H
+
+#include <common/compat/endian.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define MSGPACK_ENDIAN_LITTLE_BYTE 1
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define MSGPACK_ENDIAN_BIG_BYTE 1
+#endif
+
+#endif
diff --git a/src/vendor/msgpack/msgpack.h b/src/vendor/msgpack/msgpack.h
new file mode 100644 (file)
index 0000000..cf962c9
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * MessagePack for C
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+/**
+ * @defgroup msgpack MessagePack C
+ * @{
+ * @}
+ */
+
+#include "vendor/msgpack/util.h"
+#include "vendor/msgpack/object.h"
+#include "vendor/msgpack/zone.h"
+#include "vendor/msgpack/pack.h"
+#include "vendor/msgpack/unpack.h"
+#include "vendor/msgpack/sbuffer.h"
+#include "vendor/msgpack/vrefbuffer.h"
+#include "vendor/msgpack/version.h"
+
diff --git a/src/vendor/msgpack/object.h b/src/vendor/msgpack/object.h
new file mode 100644 (file)
index 0000000..e943174
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * MessagePack for C dynamic typing routine
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_OBJECT_H
+#define MSGPACK_OBJECT_H
+
+#include "zone.h"
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @defgroup msgpack_object Dynamically typed object
+ * @ingroup msgpack
+ * @{
+ */
+
+typedef enum {
+    MSGPACK_OBJECT_NIL                  = 0x00,
+    MSGPACK_OBJECT_BOOLEAN              = 0x01,
+    MSGPACK_OBJECT_POSITIVE_INTEGER     = 0x02,
+    MSGPACK_OBJECT_NEGATIVE_INTEGER     = 0x03,
+    MSGPACK_OBJECT_FLOAT32              = 0x0a,
+    MSGPACK_OBJECT_FLOAT64              = 0x04,
+    MSGPACK_OBJECT_FLOAT                = 0x04,
+#if defined(MSGPACK_USE_LEGACY_NAME_AS_FLOAT)
+    MSGPACK_OBJECT_DOUBLE               = MSGPACK_OBJECT_FLOAT, /* obsolete */
+#endif /* MSGPACK_USE_LEGACY_NAME_AS_FLOAT */
+    MSGPACK_OBJECT_STR                  = 0x05,
+    MSGPACK_OBJECT_ARRAY                = 0x06,
+    MSGPACK_OBJECT_MAP                  = 0x07,
+    MSGPACK_OBJECT_BIN                  = 0x08,
+    MSGPACK_OBJECT_EXT                  = 0x09
+} msgpack_object_type;
+
+
+struct msgpack_object;
+struct msgpack_object_kv;
+
+typedef struct {
+    uint32_t size;
+    struct msgpack_object* ptr;
+} msgpack_object_array;
+
+typedef struct {
+    uint32_t size;
+    struct msgpack_object_kv* ptr;
+} msgpack_object_map;
+
+typedef struct {
+    uint32_t size;
+    const char* ptr;
+} msgpack_object_str;
+
+typedef struct {
+    uint32_t size;
+    const char* ptr;
+} msgpack_object_bin;
+
+typedef struct {
+    int8_t type;
+    uint32_t size;
+    const char* ptr;
+} msgpack_object_ext;
+
+typedef union {
+    bool boolean;
+    uint64_t u64;
+    int64_t  i64;
+#if defined(MSGPACK_USE_LEGACY_NAME_AS_FLOAT)
+    double   dec; /* obsolete*/
+#endif /* MSGPACK_USE_LEGACY_NAME_AS_FLOAT */
+    double   f64;
+    msgpack_object_array array;
+    msgpack_object_map map;
+    msgpack_object_str str;
+    msgpack_object_bin bin;
+    msgpack_object_ext ext;
+} msgpack_object_union;
+
+typedef struct msgpack_object {
+    msgpack_object_type type;
+    msgpack_object_union via;
+} msgpack_object;
+
+typedef struct msgpack_object_kv {
+    msgpack_object key;
+    msgpack_object val;
+} msgpack_object_kv;
+
+#if !defined(_KERNEL_MODE)
+MSGPACK_DLLEXPORT
+void msgpack_object_print(FILE* out, msgpack_object o);
+#endif
+
+MSGPACK_DLLEXPORT
+int msgpack_object_print_buffer(char *buffer, size_t buffer_size, msgpack_object o);
+
+MSGPACK_DLLEXPORT
+bool msgpack_object_equal(const msgpack_object x, const msgpack_object y);
+
+/** @} */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/object.h */
diff --git a/src/vendor/msgpack/objectc.c b/src/vendor/msgpack/objectc.c
new file mode 100644 (file)
index 0000000..6086bd6
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * MessagePack for C dynamic typing routine
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#if defined(_KERNEL_MODE)
+#  undef  _NO_CRT_STDIO_INLINE
+#  define _NO_CRT_STDIO_INLINE
+#endif
+
+#include "vendor/msgpack/object.h"
+#include "vendor/msgpack/pack.h"
+#include <ctype.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 1800
+#include <inttypes.h>
+#else
+#define PRIu64 "I64u"
+#define PRIi64 "I64i"
+#define PRIi8 "i"
+#endif
+#else
+#include <inttypes.h>
+#endif
+
+#if defined(_KERNEL_MODE)
+#  undef  snprintf
+#  define snprintf _snprintf
+#endif
+
+int msgpack_pack_object(msgpack_packer* pk, msgpack_object d)
+{
+    switch(d.type) {
+    case MSGPACK_OBJECT_NIL:
+        return msgpack_pack_nil(pk);
+
+    case MSGPACK_OBJECT_BOOLEAN:
+        if(d.via.boolean) {
+            return msgpack_pack_true(pk);
+        } else {
+            return msgpack_pack_false(pk);
+        }
+
+    case MSGPACK_OBJECT_POSITIVE_INTEGER:
+        return msgpack_pack_uint64(pk, d.via.u64);
+
+    case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+        return msgpack_pack_int64(pk, d.via.i64);
+
+    case MSGPACK_OBJECT_FLOAT32:
+        return msgpack_pack_float(pk, (float)d.via.f64);
+
+    case MSGPACK_OBJECT_FLOAT64:
+        return msgpack_pack_double(pk, d.via.f64);
+
+    case MSGPACK_OBJECT_STR:
+        {
+            int ret = msgpack_pack_str(pk, d.via.str.size);
+            if(ret < 0) { return ret; }
+            return msgpack_pack_str_body(pk, d.via.str.ptr, d.via.str.size);
+        }
+
+    case MSGPACK_OBJECT_BIN:
+        {
+            int ret = msgpack_pack_bin(pk, d.via.bin.size);
+            if(ret < 0) { return ret; }
+            return msgpack_pack_bin_body(pk, d.via.bin.ptr, d.via.bin.size);
+        }
+
+    case MSGPACK_OBJECT_EXT:
+        {
+            int ret = msgpack_pack_ext(pk, d.via.ext.size, d.via.ext.type);
+            if(ret < 0) { return ret; }
+            return msgpack_pack_ext_body(pk, d.via.ext.ptr, d.via.ext.size);
+        }
+
+    case MSGPACK_OBJECT_ARRAY:
+        {
+            int ret = msgpack_pack_array(pk, d.via.array.size);
+            if(ret < 0) {
+                return ret;
+            }
+            else {
+                msgpack_object* o = d.via.array.ptr;
+                msgpack_object* const oend = d.via.array.ptr + d.via.array.size;
+                for(; o != oend; ++o) {
+                    ret = msgpack_pack_object(pk, *o);
+                    if(ret < 0) { return ret; }
+                }
+
+                return 0;
+            }
+        }
+
+    case MSGPACK_OBJECT_MAP:
+        {
+            int ret = msgpack_pack_map(pk, d.via.map.size);
+            if(ret < 0) {
+                return ret;
+            }
+            else {
+                msgpack_object_kv* kv = d.via.map.ptr;
+                msgpack_object_kv* const kvend = d.via.map.ptr + d.via.map.size;
+                for(; kv != kvend; ++kv) {
+                    ret = msgpack_pack_object(pk, kv->key);
+                    if(ret < 0) { return ret; }
+                    ret = msgpack_pack_object(pk, kv->val);
+                    if(ret < 0) { return ret; }
+                }
+
+                return 0;
+            }
+        }
+
+    default:
+        return -1;
+    }
+}
+
+#if !defined(_KERNEL_MODE)
+
+static void msgpack_object_bin_print(FILE* out, const char *ptr, size_t size)
+{
+    size_t i;
+    for (i = 0; i < size; ++i) {
+        if (ptr[i] == '"') {
+            fputs("\\\"", out);
+        } else if (isprint((unsigned char)ptr[i])) {
+            fputc(ptr[i], out);
+        } else {
+            fprintf(out, "\\x%02x", (unsigned char)ptr[i]);
+        }
+    }
+}
+
+void msgpack_object_print(FILE* out, msgpack_object o)
+{
+    switch(o.type) {
+    case MSGPACK_OBJECT_NIL:
+        fprintf(out, "nil");
+        break;
+
+    case MSGPACK_OBJECT_BOOLEAN:
+        fprintf(out, (o.via.boolean ? "true" : "false"));
+        break;
+
+    case MSGPACK_OBJECT_POSITIVE_INTEGER:
+#if defined(PRIu64)
+        fprintf(out, "%" PRIu64, o.via.u64);
+#else
+        if (o.via.u64 > ULONG_MAX)
+            fprintf(out, "over 4294967295");
+        else
+            fprintf(out, "%lu", (unsigned long)o.via.u64);
+#endif
+        break;
+
+    case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+#if defined(PRIi64)
+        fprintf(out, "%" PRIi64, o.via.i64);
+#else
+        if (o.via.i64 > LONG_MAX)
+            fprintf(out, "over +2147483647");
+        else if (o.via.i64 < LONG_MIN)
+            fprintf(out, "under -2147483648");
+        else
+            fprintf(out, "%ld", (signed long)o.via.i64);
+#endif
+        break;
+
+    case MSGPACK_OBJECT_FLOAT32:
+    case MSGPACK_OBJECT_FLOAT64:
+        fprintf(out, "%f", o.via.f64);
+        break;
+
+    case MSGPACK_OBJECT_STR:
+        fprintf(out, "\"");
+        fwrite(o.via.str.ptr, o.via.str.size, 1, out);
+        fprintf(out, "\"");
+        break;
+
+    case MSGPACK_OBJECT_BIN:
+        fprintf(out, "\"");
+        msgpack_object_bin_print(out, o.via.bin.ptr, o.via.bin.size);
+        fprintf(out, "\"");
+        break;
+
+    case MSGPACK_OBJECT_EXT:
+#if defined(PRIi8)
+        fprintf(out, "(ext: %" PRIi8 ")", o.via.ext.type);
+#else
+        fprintf(out, "(ext: %d)", (int)o.via.ext.type);
+#endif
+        fprintf(out, "\"");
+        msgpack_object_bin_print(out, o.via.ext.ptr, o.via.ext.size);
+        fprintf(out, "\"");
+        break;
+
+    case MSGPACK_OBJECT_ARRAY:
+        fprintf(out, "[");
+        if(o.via.array.size != 0) {
+            msgpack_object* p = o.via.array.ptr;
+            msgpack_object* const pend = o.via.array.ptr + o.via.array.size;
+            msgpack_object_print(out, *p);
+            ++p;
+            for(; p < pend; ++p) {
+                fprintf(out, ", ");
+                msgpack_object_print(out, *p);
+            }
+        }
+        fprintf(out, "]");
+        break;
+
+    case MSGPACK_OBJECT_MAP:
+        fprintf(out, "{");
+        if(o.via.map.size != 0) {
+            msgpack_object_kv* p = o.via.map.ptr;
+            msgpack_object_kv* const pend = o.via.map.ptr + o.via.map.size;
+            msgpack_object_print(out, p->key);
+            fprintf(out, "=>");
+            msgpack_object_print(out, p->val);
+            ++p;
+            for(; p < pend; ++p) {
+                fprintf(out, ", ");
+                msgpack_object_print(out, p->key);
+                fprintf(out, "=>");
+                msgpack_object_print(out, p->val);
+            }
+        }
+        fprintf(out, "}");
+        break;
+
+    default:
+        // FIXME
+#if defined(PRIu64)
+        fprintf(out, "#<UNKNOWN %i %" PRIu64 ">", o.type, o.via.u64);
+#else
+        if (o.via.u64 > ULONG_MAX)
+            fprintf(out, "#<UNKNOWN %i over 4294967295>", o.type);
+        else
+            fprintf(out, "#<UNKNOWN %i %lu>", o.type, (unsigned long)o.via.u64);
+#endif
+
+    }
+}
+
+#endif
+
+#define MSGPACK_CHECKED_CALL(ret, func, aux_buffer, aux_buffer_size, ...) \
+    ret = func(aux_buffer, aux_buffer_size, __VA_ARGS__);                 \
+    if (ret <= 0 || ret >= (int)aux_buffer_size) return 0;                \
+    aux_buffer = aux_buffer + ret;                                        \
+    aux_buffer_size = aux_buffer_size - ret                               \
+
+static int msgpack_object_bin_print_buffer(char *buffer, size_t buffer_size, const char *ptr, size_t size)
+{
+    size_t i;
+    char *aux_buffer = buffer;
+    size_t aux_buffer_size = buffer_size;
+    int ret;
+
+    for (i = 0; i < size; ++i) {
+        if (ptr[i] == '"') {
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\\\"");
+        } else if (isprint((unsigned char)ptr[i])) {
+            if (aux_buffer_size > 0) {
+                memcpy(aux_buffer, ptr + i, 1);
+                aux_buffer = aux_buffer + 1;
+                aux_buffer_size = aux_buffer_size - 1;
+            }
+        } else {
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\\x%02x", (unsigned char)ptr[i]);
+        }
+    }
+
+    return (int)(buffer_size - aux_buffer_size);
+}
+
+int msgpack_object_print_buffer(char *buffer, size_t buffer_size, msgpack_object o)
+{
+    char *aux_buffer = buffer;
+    size_t aux_buffer_size = buffer_size;
+    int ret;
+    switch(o.type) {
+    case MSGPACK_OBJECT_NIL:
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "nil");
+        break;
+
+    case MSGPACK_OBJECT_BOOLEAN:
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, (o.via.boolean ? "true" : "false"));
+        break;
+
+    case MSGPACK_OBJECT_POSITIVE_INTEGER:
+#if defined(PRIu64)
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%" PRIu64, o.via.u64);
+#else
+        if (o.via.u64 > ULONG_MAX) {
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "over 4294967295");
+        } else {
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%lu", (unsigned long)o.via.u64);
+        }
+#endif
+        break;
+
+    case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+#if defined(PRIi64)
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%" PRIi64, o.via.i64);
+#else
+        if (o.via.i64 > LONG_MAX) {
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "over +2147483647");
+        } else if (o.via.i64 < LONG_MIN) {
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "under -2147483648");
+        } else {
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%ld", (signed long)o.via.i64);
+        }
+#endif
+        break;
+
+    case MSGPACK_OBJECT_FLOAT32:
+    case MSGPACK_OBJECT_FLOAT64:
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%f", o.via.f64);
+        break;
+
+    case MSGPACK_OBJECT_STR:
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\"");
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "%.*s", (int)o.via.str.size, o.via.str.ptr);
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\"");
+        break;
+
+    case MSGPACK_OBJECT_BIN:
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\"");
+        MSGPACK_CHECKED_CALL(ret, msgpack_object_bin_print_buffer, aux_buffer, aux_buffer_size, o.via.bin.ptr, o.via.bin.size);
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\"");
+        break;
+
+    case MSGPACK_OBJECT_EXT:
+#if defined(PRIi8)
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "(ext: %" PRIi8 ")", o.via.ext.type);
+#else
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "(ext: %d)", (int)o.via.ext.type);
+#endif
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\"");
+        MSGPACK_CHECKED_CALL(ret, msgpack_object_bin_print_buffer, aux_buffer, aux_buffer_size, o.via.ext.ptr, o.via.ext.size);
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "\"");
+        break;
+
+    case MSGPACK_OBJECT_ARRAY:
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "[");
+        if(o.via.array.size != 0) {
+            msgpack_object* p = o.via.array.ptr;
+            msgpack_object* const pend = o.via.array.ptr + o.via.array.size;
+            MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, *p);
+            ++p;
+            for(; p < pend; ++p) {
+                MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, ", ");
+                MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, *p);
+            }
+        }
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "]");
+        break;
+
+    case MSGPACK_OBJECT_MAP:
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "{");
+        if(o.via.map.size != 0) {
+            msgpack_object_kv* p = o.via.map.ptr;
+            msgpack_object_kv* const pend = o.via.map.ptr + o.via.map.size;
+            MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, p->key);
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "=>");
+            MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, p->val);
+            ++p;
+            for(; p < pend; ++p) {
+                MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, ", ");
+                MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, p->key);
+                MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "=>");
+                MSGPACK_CHECKED_CALL(ret, msgpack_object_print_buffer, aux_buffer, aux_buffer_size, p->val);
+            }
+        }
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "}");
+        break;
+
+    default:
+    // FIXME
+#if defined(PRIu64)
+        MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "#<UNKNOWN %i %" PRIu64 ">", o.type, o.via.u64);
+#else
+        if (o.via.u64 > ULONG_MAX) {
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "#<UNKNOWN %i over 4294967295>", o.type);
+        } else {
+            MSGPACK_CHECKED_CALL(ret, snprintf, aux_buffer, aux_buffer_size, "#<UNKNOWN %i %lu>", o.type, (unsigned long)o.via.u64);
+        }
+#endif
+    }
+
+    return (int)(buffer_size - aux_buffer_size);
+}
+
+#undef MSGPACK_CHECKED_CALL
+
+bool msgpack_object_equal(const msgpack_object x, const msgpack_object y)
+{
+    if(x.type != y.type) { return false; }
+
+    switch(x.type) {
+    case MSGPACK_OBJECT_NIL:
+        return true;
+
+    case MSGPACK_OBJECT_BOOLEAN:
+        return x.via.boolean == y.via.boolean;
+
+    case MSGPACK_OBJECT_POSITIVE_INTEGER:
+        return x.via.u64 == y.via.u64;
+
+    case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+        return x.via.i64 == y.via.i64;
+
+    case MSGPACK_OBJECT_FLOAT32:
+    case MSGPACK_OBJECT_FLOAT64:
+        return x.via.f64 == y.via.f64;
+
+    case MSGPACK_OBJECT_STR:
+        return x.via.str.size == y.via.str.size &&
+            memcmp(x.via.str.ptr, y.via.str.ptr, x.via.str.size) == 0;
+
+    case MSGPACK_OBJECT_BIN:
+        return x.via.bin.size == y.via.bin.size &&
+            memcmp(x.via.bin.ptr, y.via.bin.ptr, x.via.bin.size) == 0;
+
+    case MSGPACK_OBJECT_EXT:
+        return x.via.ext.size == y.via.ext.size &&
+            x.via.ext.type == y.via.ext.type &&
+            memcmp(x.via.ext.ptr, y.via.ext.ptr, x.via.ext.size) == 0;
+
+    case MSGPACK_OBJECT_ARRAY:
+        if(x.via.array.size != y.via.array.size) {
+            return false;
+        } else if(x.via.array.size == 0) {
+            return true;
+        } else {
+            msgpack_object* px = x.via.array.ptr;
+            msgpack_object* const pxend = x.via.array.ptr + x.via.array.size;
+            msgpack_object* py = y.via.array.ptr;
+            do {
+                if(!msgpack_object_equal(*px, *py)) {
+                    return false;
+                }
+                ++px;
+                ++py;
+            } while(px < pxend);
+            return true;
+        }
+
+    case MSGPACK_OBJECT_MAP:
+        if(x.via.map.size != y.via.map.size) {
+            return false;
+        } else if(x.via.map.size == 0) {
+            return true;
+        } else {
+            msgpack_object_kv* px = x.via.map.ptr;
+            msgpack_object_kv* const pxend = x.via.map.ptr + x.via.map.size;
+            msgpack_object_kv* py = y.via.map.ptr;
+            do {
+                if(!msgpack_object_equal(px->key, py->key) || !msgpack_object_equal(px->val, py->val)) {
+                    return false;
+                }
+                ++px;
+                ++py;
+            } while(px < pxend);
+            return true;
+        }
+
+    default:
+        return false;
+    }
+}
diff --git a/src/vendor/msgpack/pack.h b/src/vendor/msgpack/pack.h
new file mode 100644 (file)
index 0000000..08ab84b
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * MessagePack for C packing routine
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_PACK_H
+#define MSGPACK_PACK_H
+
+#include "pack_define.h"
+#include "object.h"
+#include "timestamp.h"
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @defgroup msgpack_buffer Buffers
+ * @ingroup msgpack
+ * @{
+ * @}
+ */
+
+/**
+ * @defgroup msgpack_pack Serializer
+ * @ingroup msgpack
+ * @{
+ */
+
+typedef int (*msgpack_packer_write)(void* data, const char* buf, size_t len);
+
+typedef struct msgpack_packer {
+    void* data;
+    msgpack_packer_write callback;
+} msgpack_packer;
+
+static void msgpack_packer_init(msgpack_packer* pk, void* data, msgpack_packer_write callback);
+
+static msgpack_packer* msgpack_packer_new(void* data, msgpack_packer_write callback);
+static void msgpack_packer_free(msgpack_packer* pk);
+
+static int msgpack_pack_char(msgpack_packer* pk, char d);
+
+static int msgpack_pack_signed_char(msgpack_packer* pk, signed char d);
+static int msgpack_pack_short(msgpack_packer* pk, short d);
+static int msgpack_pack_int(msgpack_packer* pk, int d);
+static int msgpack_pack_long(msgpack_packer* pk, long d);
+static int msgpack_pack_long_long(msgpack_packer* pk, long long d);
+static int msgpack_pack_unsigned_char(msgpack_packer* pk, unsigned char d);
+static int msgpack_pack_unsigned_short(msgpack_packer* pk, unsigned short d);
+static int msgpack_pack_unsigned_int(msgpack_packer* pk, unsigned int d);
+static int msgpack_pack_unsigned_long(msgpack_packer* pk, unsigned long d);
+static int msgpack_pack_unsigned_long_long(msgpack_packer* pk, unsigned long long d);
+
+static int msgpack_pack_uint8(msgpack_packer* pk, uint8_t d);
+static int msgpack_pack_uint16(msgpack_packer* pk, uint16_t d);
+static int msgpack_pack_uint32(msgpack_packer* pk, uint32_t d);
+static int msgpack_pack_uint64(msgpack_packer* pk, uint64_t d);
+static int msgpack_pack_int8(msgpack_packer* pk, int8_t d);
+static int msgpack_pack_int16(msgpack_packer* pk, int16_t d);
+static int msgpack_pack_int32(msgpack_packer* pk, int32_t d);
+static int msgpack_pack_int64(msgpack_packer* pk, int64_t d);
+
+static int msgpack_pack_fix_uint8(msgpack_packer* pk, uint8_t d);
+static int msgpack_pack_fix_uint16(msgpack_packer* pk, uint16_t d);
+static int msgpack_pack_fix_uint32(msgpack_packer* pk, uint32_t d);
+static int msgpack_pack_fix_uint64(msgpack_packer* pk, uint64_t d);
+static int msgpack_pack_fix_int8(msgpack_packer* pk, int8_t d);
+static int msgpack_pack_fix_int16(msgpack_packer* pk, int16_t d);
+static int msgpack_pack_fix_int32(msgpack_packer* pk, int32_t d);
+static int msgpack_pack_fix_int64(msgpack_packer* pk, int64_t d);
+
+static int msgpack_pack_float(msgpack_packer* pk, float d);
+static int msgpack_pack_double(msgpack_packer* pk, double d);
+
+static int msgpack_pack_nil(msgpack_packer* pk);
+static int msgpack_pack_true(msgpack_packer* pk);
+static int msgpack_pack_false(msgpack_packer* pk);
+
+static int msgpack_pack_array(msgpack_packer* pk, size_t n);
+
+static int msgpack_pack_map(msgpack_packer* pk, size_t n);
+
+static int msgpack_pack_str(msgpack_packer* pk, size_t l);
+static int msgpack_pack_str_body(msgpack_packer* pk, const void* b, size_t l);
+static int msgpack_pack_str_with_body(msgpack_packer* pk, const void* b, size_t l);
+
+static int msgpack_pack_v4raw(msgpack_packer* pk, size_t l);
+static int msgpack_pack_v4raw_body(msgpack_packer* pk, const void* b, size_t l);
+
+static int msgpack_pack_bin(msgpack_packer* pk, size_t l);
+static int msgpack_pack_bin_body(msgpack_packer* pk, const void* b, size_t l);
+static int msgpack_pack_bin_with_body(msgpack_packer* pk, const void* b, size_t l);
+
+static int msgpack_pack_ext(msgpack_packer* pk, size_t l, int8_t type);
+static int msgpack_pack_ext_body(msgpack_packer* pk, const void* b, size_t l);
+static int msgpack_pack_ext_with_body(msgpack_packer* pk, const void* b, size_t l, int8_t type);
+
+static int msgpack_pack_timestamp(msgpack_packer* pk, const msgpack_timestamp* d);
+
+MSGPACK_DLLEXPORT
+int msgpack_pack_object(msgpack_packer* pk, msgpack_object d);
+
+
+/** @} */
+
+
+#define msgpack_pack_inline_func(name) \
+    inline int msgpack_pack ## name
+
+#define msgpack_pack_inline_func_cint(name) \
+    inline int msgpack_pack ## name
+
+#define msgpack_pack_inline_func_fixint(name) \
+    inline int msgpack_pack_fix ## name
+
+#define msgpack_pack_user msgpack_packer*
+
+#define msgpack_pack_append_buffer(user, buf, len) \
+    return (*(user)->callback)((user)->data, (const char*)buf, len)
+
+#include "pack_template.h"
+
+inline void msgpack_packer_init(msgpack_packer* pk, void* data, msgpack_packer_write callback)
+{
+    pk->data = data;
+    pk->callback = callback;
+}
+
+inline msgpack_packer* msgpack_packer_new(void* data, msgpack_packer_write callback)
+{
+    msgpack_packer* pk = (msgpack_packer*)calloc(1, sizeof(msgpack_packer));
+    if(!pk) { return NULL; }
+    msgpack_packer_init(pk, data, callback);
+    return pk;
+}
+
+inline void msgpack_packer_free(msgpack_packer* pk)
+{
+    free(pk);
+}
+
+inline int msgpack_pack_str_with_body(msgpack_packer* pk, const void* b, size_t l)
+ {
+     int ret = msgpack_pack_str(pk, l);
+     if (ret != 0) { return ret; }
+     return msgpack_pack_str_body(pk, b, l);
+ }
+
+ inline int msgpack_pack_bin_with_body(msgpack_packer* pk, const void* b, size_t l)
+ {
+     int ret = msgpack_pack_bin(pk, l);
+     if (ret != 0) { return ret; }
+     return msgpack_pack_bin_body(pk, b, l);
+ }
+
+ inline int msgpack_pack_ext_with_body(msgpack_packer* pk, const void* b, size_t l, int8_t type)
+ {
+     int ret = msgpack_pack_ext(pk, l, type);
+     if (ret != 0) { return ret; }
+     return msgpack_pack_ext_body(pk, b, l);
+ }
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/pack.h */
diff --git a/src/vendor/msgpack/pack_define.h b/src/vendor/msgpack/pack_define.h
new file mode 100644 (file)
index 0000000..5033bb4
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * MessagePack unpacking routine template
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_PACK_DEFINE_H
+#define MSGPACK_PACK_DEFINE_H
+
+#include "vendor/msgpack/sysdep.h"
+#include <limits.h>
+#include <string.h>
+
+#endif /* msgpack/pack_define.h */
+
diff --git a/src/vendor/msgpack/pack_template.h b/src/vendor/msgpack/pack_template.h
new file mode 100644 (file)
index 0000000..ac112e1
--- /dev/null
@@ -0,0 +1,945 @@
+/*
+ * MessagePack packing routine template
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#if MSGPACK_ENDIAN_LITTLE_BYTE
+#define TAKE8_8(d)  ((uint8_t*)&d)[0]
+#define TAKE8_16(d) ((uint8_t*)&d)[0]
+#define TAKE8_32(d) ((uint8_t*)&d)[0]
+#define TAKE8_64(d) ((uint8_t*)&d)[0]
+#elif MSGPACK_ENDIAN_BIG_BYTE
+#define TAKE8_8(d)  ((uint8_t*)&d)[0]
+#define TAKE8_16(d) ((uint8_t*)&d)[1]
+#define TAKE8_32(d) ((uint8_t*)&d)[3]
+#define TAKE8_64(d) ((uint8_t*)&d)[7]
+#else
+#error msgpack-c supports only big endian and little endian
+#endif
+
+#ifndef msgpack_pack_inline_func
+#error msgpack_pack_inline_func template is not defined
+#endif
+
+#ifndef msgpack_pack_user
+#error msgpack_pack_user type is not defined
+#endif
+
+#ifndef msgpack_pack_append_buffer
+#error msgpack_pack_append_buffer callback is not defined
+#endif
+
+#if defined(_MSC_VER)
+#   pragma warning(push)
+#   pragma warning(disable : 4204)   /* nonstandard extension used: non-constant aggregate initializer */
+#endif
+
+/*
+ * Integer
+ */
+
+#define msgpack_pack_real_uint8(x, d) \
+do { \
+    if(d < (1<<7)) { \
+        /* fixnum */ \
+        msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \
+    } else { \
+        /* unsigned 8 */ \
+        unsigned char buf[2] = {0xcc, TAKE8_8(d)}; \
+        msgpack_pack_append_buffer(x, buf, 2); \
+    } \
+} while(0)
+
+#define msgpack_pack_real_uint16(x, d) \
+do { \
+    if(d < (1<<7)) { \
+        /* fixnum */ \
+        msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \
+    } else if(d < (1<<8)) { \
+        /* unsigned 8 */ \
+        unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \
+        msgpack_pack_append_buffer(x, buf, 2); \
+    } else { \
+        /* unsigned 16 */ \
+        unsigned char buf[3]; \
+        buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \
+        msgpack_pack_append_buffer(x, buf, 3); \
+    } \
+} while(0)
+
+#define msgpack_pack_real_uint32(x, d) \
+do { \
+    if(d < (1<<8)) { \
+        if(d < (1<<7)) { \
+            /* fixnum */ \
+            msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \
+        } else { \
+            /* unsigned 8 */ \
+            unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \
+            msgpack_pack_append_buffer(x, buf, 2); \
+        } \
+    } else { \
+        if(d < (1<<16)) { \
+            /* unsigned 16 */ \
+            unsigned char buf[3]; \
+            buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \
+            msgpack_pack_append_buffer(x, buf, 3); \
+        } else { \
+            /* unsigned 32 */ \
+            unsigned char buf[5]; \
+            buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \
+            msgpack_pack_append_buffer(x, buf, 5); \
+        } \
+    } \
+} while(0)
+
+#define msgpack_pack_real_uint64(x, d) \
+do { \
+    if(d < (1ULL<<8)) { \
+        if(d < (1ULL<<7)) { \
+            /* fixnum */ \
+            msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \
+        } else { \
+            /* unsigned 8 */ \
+            unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \
+            msgpack_pack_append_buffer(x, buf, 2); \
+        } \
+    } else { \
+        if(d < (1ULL<<16)) { \
+            /* unsigned 16 */ \
+            unsigned char buf[3]; \
+            buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \
+            msgpack_pack_append_buffer(x, buf, 3); \
+        } else if(d < (1ULL<<32)) { \
+            /* unsigned 32 */ \
+            unsigned char buf[5]; \
+            buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \
+            msgpack_pack_append_buffer(x, buf, 5); \
+        } else { \
+            /* unsigned 64 */ \
+            unsigned char buf[9]; \
+            buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \
+            msgpack_pack_append_buffer(x, buf, 9); \
+        } \
+    } \
+} while(0)
+
+#define msgpack_pack_real_int8(x, d) \
+do { \
+    if(d < -(1<<5)) { \
+        /* signed 8 */ \
+        unsigned char buf[2] = {0xd0, TAKE8_8(d)}; \
+        msgpack_pack_append_buffer(x, buf, 2); \
+    } else { \
+        /* fixnum */ \
+        msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \
+    } \
+} while(0)
+
+#define msgpack_pack_real_int16(x, d) \
+do { \
+    if(d < -(1<<5)) { \
+        if(d < -(1<<7)) { \
+            /* signed 16 */ \
+            unsigned char buf[3]; \
+            buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \
+            msgpack_pack_append_buffer(x, buf, 3); \
+        } else { \
+            /* signed 8 */ \
+            unsigned char buf[2] = {0xd0, TAKE8_16(d)}; \
+            msgpack_pack_append_buffer(x, buf, 2); \
+        } \
+    } else if(d < (1<<7)) { \
+        /* fixnum */ \
+        msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \
+    } else { \
+        if(d < (1<<8)) { \
+            /* unsigned 8 */ \
+            unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \
+            msgpack_pack_append_buffer(x, buf, 2); \
+        } else { \
+            /* unsigned 16 */ \
+            unsigned char buf[3]; \
+            buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \
+            msgpack_pack_append_buffer(x, buf, 3); \
+        } \
+    } \
+} while(0)
+
+#define msgpack_pack_real_int32(x, d) \
+do { \
+    if(d < -(1<<5)) { \
+        if(d < -(1<<15)) { \
+            /* signed 32 */ \
+            unsigned char buf[5]; \
+            buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \
+            msgpack_pack_append_buffer(x, buf, 5); \
+        } else if(d < -(1<<7)) { \
+            /* signed 16 */ \
+            unsigned char buf[3]; \
+            buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \
+            msgpack_pack_append_buffer(x, buf, 3); \
+        } else { \
+            /* signed 8 */ \
+            unsigned char buf[2] = {0xd0, TAKE8_32(d)}; \
+            msgpack_pack_append_buffer(x, buf, 2); \
+        } \
+    } else if(d < (1<<7)) { \
+        /* fixnum */ \
+        msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \
+    } else { \
+        if(d < (1<<8)) { \
+            /* unsigned 8 */ \
+            unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \
+            msgpack_pack_append_buffer(x, buf, 2); \
+        } else if(d < (1<<16)) { \
+            /* unsigned 16 */ \
+            unsigned char buf[3]; \
+            buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \
+            msgpack_pack_append_buffer(x, buf, 3); \
+        } else { \
+            /* unsigned 32 */ \
+            unsigned char buf[5]; \
+            buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \
+            msgpack_pack_append_buffer(x, buf, 5); \
+        } \
+    } \
+} while(0)
+
+#define msgpack_pack_real_int64(x, d) \
+do { \
+    if(d < -(1LL<<5)) { \
+        if(d < -(1LL<<15)) { \
+            if(d < -(1LL<<31)) { \
+                /* signed 64 */ \
+                unsigned char buf[9]; \
+                buf[0] = 0xd3; _msgpack_store64(&buf[1], d); \
+                msgpack_pack_append_buffer(x, buf, 9); \
+            } else { \
+                /* signed 32 */ \
+                unsigned char buf[5]; \
+                buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \
+                msgpack_pack_append_buffer(x, buf, 5); \
+            } \
+        } else { \
+            if(d < -(1<<7)) { \
+                /* signed 16 */ \
+                unsigned char buf[3]; \
+                buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \
+                msgpack_pack_append_buffer(x, buf, 3); \
+            } else { \
+                /* signed 8 */ \
+                unsigned char buf[2] = {0xd0, TAKE8_64(d)}; \
+                msgpack_pack_append_buffer(x, buf, 2); \
+            } \
+        } \
+    } else if(d < (1<<7)) { \
+        /* fixnum */ \
+        msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \
+    } else { \
+        if(d < (1LL<<16)) { \
+            if(d < (1<<8)) { \
+                /* unsigned 8 */ \
+                unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \
+                msgpack_pack_append_buffer(x, buf, 2); \
+            } else { \
+                /* unsigned 16 */ \
+                unsigned char buf[3]; \
+                buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \
+                msgpack_pack_append_buffer(x, buf, 3); \
+            } \
+        } else { \
+            if(d < (1LL<<32)) { \
+                /* unsigned 32 */ \
+                unsigned char buf[5]; \
+                buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \
+                msgpack_pack_append_buffer(x, buf, 5); \
+            } else { \
+                /* unsigned 64 */ \
+                unsigned char buf[9]; \
+                buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \
+                msgpack_pack_append_buffer(x, buf, 9); \
+            } \
+        } \
+    } \
+} while(0)
+
+
+#ifdef msgpack_pack_inline_func_fixint
+
+msgpack_pack_inline_func_fixint(_uint8)(msgpack_pack_user x, uint8_t d)
+{
+    unsigned char buf[2] = {0xcc, TAKE8_8(d)};
+    msgpack_pack_append_buffer(x, buf, 2);
+}
+
+msgpack_pack_inline_func_fixint(_uint16)(msgpack_pack_user x, uint16_t d)
+{
+    unsigned char buf[3];
+    buf[0] = 0xcd; _msgpack_store16(&buf[1], d);
+    msgpack_pack_append_buffer(x, buf, 3);
+}
+
+msgpack_pack_inline_func_fixint(_uint32)(msgpack_pack_user x, uint32_t d)
+{
+    unsigned char buf[5];
+    buf[0] = 0xce; _msgpack_store32(&buf[1], d);
+    msgpack_pack_append_buffer(x, buf, 5);
+}
+
+msgpack_pack_inline_func_fixint(_uint64)(msgpack_pack_user x, uint64_t d)
+{
+    unsigned char buf[9];
+    buf[0] = 0xcf; _msgpack_store64(&buf[1], d);
+    msgpack_pack_append_buffer(x, buf, 9);
+}
+
+msgpack_pack_inline_func_fixint(_int8)(msgpack_pack_user x, int8_t d)
+{
+    unsigned char buf[2] = {0xd0, TAKE8_8(d)};
+    msgpack_pack_append_buffer(x, buf, 2);
+}
+
+msgpack_pack_inline_func_fixint(_int16)(msgpack_pack_user x, int16_t d)
+{
+    unsigned char buf[3];
+    buf[0] = 0xd1; _msgpack_store16(&buf[1], d);
+    msgpack_pack_append_buffer(x, buf, 3);
+}
+
+msgpack_pack_inline_func_fixint(_int32)(msgpack_pack_user x, int32_t d)
+{
+    unsigned char buf[5];
+    buf[0] = 0xd2; _msgpack_store32(&buf[1], d);
+    msgpack_pack_append_buffer(x, buf, 5);
+}
+
+msgpack_pack_inline_func_fixint(_int64)(msgpack_pack_user x, int64_t d)
+{
+    unsigned char buf[9];
+    buf[0] = 0xd3; _msgpack_store64(&buf[1], d);
+    msgpack_pack_append_buffer(x, buf, 9);
+}
+
+#undef msgpack_pack_inline_func_fixint
+#endif
+
+
+msgpack_pack_inline_func(_uint8)(msgpack_pack_user x, uint8_t d)
+{
+    msgpack_pack_real_uint8(x, d);
+}
+
+msgpack_pack_inline_func(_uint16)(msgpack_pack_user x, uint16_t d)
+{
+    msgpack_pack_real_uint16(x, d);
+}
+
+msgpack_pack_inline_func(_uint32)(msgpack_pack_user x, uint32_t d)
+{
+    msgpack_pack_real_uint32(x, d);
+}
+
+msgpack_pack_inline_func(_uint64)(msgpack_pack_user x, uint64_t d)
+{
+    msgpack_pack_real_uint64(x, d);
+}
+
+msgpack_pack_inline_func(_int8)(msgpack_pack_user x, int8_t d)
+{
+    msgpack_pack_real_int8(x, d);
+}
+
+msgpack_pack_inline_func(_int16)(msgpack_pack_user x, int16_t d)
+{
+    msgpack_pack_real_int16(x, d);
+}
+
+msgpack_pack_inline_func(_int32)(msgpack_pack_user x, int32_t d)
+{
+    msgpack_pack_real_int32(x, d);
+}
+
+msgpack_pack_inline_func(_int64)(msgpack_pack_user x, int64_t d)
+{
+    msgpack_pack_real_int64(x, d);
+}
+
+msgpack_pack_inline_func(_char)(msgpack_pack_user x, char d)
+{
+#if defined(CHAR_MIN)
+#if CHAR_MIN < 0
+        msgpack_pack_real_int8(x, d);
+#else
+        msgpack_pack_real_uint8(x, d);
+#endif
+#else
+#error CHAR_MIN is not defined
+#endif
+}
+
+msgpack_pack_inline_func(_signed_char)(msgpack_pack_user x, signed char d)
+{
+    msgpack_pack_real_int8(x, d);
+}
+
+msgpack_pack_inline_func(_unsigned_char)(msgpack_pack_user x, unsigned char d)
+{
+    msgpack_pack_real_uint8(x, d);
+}
+
+#ifdef msgpack_pack_inline_func_cint
+
+msgpack_pack_inline_func_cint(_short)(msgpack_pack_user x, short d)
+{
+#if defined(SIZEOF_SHORT)
+#if SIZEOF_SHORT == 2
+    msgpack_pack_real_int16(x, d);
+#elif SIZEOF_SHORT == 4
+    msgpack_pack_real_int32(x, d);
+#else
+    msgpack_pack_real_int64(x, d);
+#endif
+
+#elif defined(SHRT_MAX)
+#if SHRT_MAX == 0x7fff
+    msgpack_pack_real_int16(x, d);
+#elif SHRT_MAX == 0x7fffffff
+    msgpack_pack_real_int32(x, d);
+#else
+    msgpack_pack_real_int64(x, d);
+#endif
+
+#else
+if(sizeof(short) == 2) {
+    msgpack_pack_real_int16(x, d);
+} else if(sizeof(short) == 4) {
+    msgpack_pack_real_int32(x, d);
+} else {
+    msgpack_pack_real_int64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_int)(msgpack_pack_user x, int d)
+{
+#if defined(SIZEOF_INT)
+#if SIZEOF_INT == 2
+    msgpack_pack_real_int16(x, d);
+#elif SIZEOF_INT == 4
+    msgpack_pack_real_int32(x, d);
+#else
+    msgpack_pack_real_int64(x, d);
+#endif
+
+#elif defined(INT_MAX)
+#if INT_MAX == 0x7fff
+    msgpack_pack_real_int16(x, d);
+#elif INT_MAX == 0x7fffffff
+    msgpack_pack_real_int32(x, d);
+#else
+    msgpack_pack_real_int64(x, d);
+#endif
+
+#else
+if(sizeof(int) == 2) {
+    msgpack_pack_real_int16(x, d);
+} else if(sizeof(int) == 4) {
+    msgpack_pack_real_int32(x, d);
+} else {
+    msgpack_pack_real_int64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_long)(msgpack_pack_user x, long d)
+{
+#if defined(SIZEOF_LONG)
+#if SIZEOF_LONG == 2
+    msgpack_pack_real_int16(x, d);
+#elif SIZEOF_LONG == 4
+    msgpack_pack_real_int32(x, d);
+#else
+    msgpack_pack_real_int64(x, d);
+#endif
+
+#elif defined(LONG_MAX)
+#if LONG_MAX == 0x7fffL
+    msgpack_pack_real_int16(x, d);
+#elif LONG_MAX == 0x7fffffffL
+    msgpack_pack_real_int32(x, d);
+#else
+    msgpack_pack_real_int64(x, d);
+#endif
+
+#else
+if(sizeof(long) == 2) {
+    msgpack_pack_real_int16(x, d);
+} else if(sizeof(long) == 4) {
+    msgpack_pack_real_int32(x, d);
+} else {
+    msgpack_pack_real_int64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_long_long)(msgpack_pack_user x, long long d)
+{
+#if defined(SIZEOF_LONG_LONG)
+#if SIZEOF_LONG_LONG == 2
+    msgpack_pack_real_int16(x, d);
+#elif SIZEOF_LONG_LONG == 4
+    msgpack_pack_real_int32(x, d);
+#else
+    msgpack_pack_real_int64(x, d);
+#endif
+
+#elif defined(LLONG_MAX)
+#if LLONG_MAX == 0x7fffL
+    msgpack_pack_real_int16(x, d);
+#elif LLONG_MAX == 0x7fffffffL
+    msgpack_pack_real_int32(x, d);
+#else
+    msgpack_pack_real_int64(x, d);
+#endif
+
+#else
+if(sizeof(long long) == 2) {
+    msgpack_pack_real_int16(x, d);
+} else if(sizeof(long long) == 4) {
+    msgpack_pack_real_int32(x, d);
+} else {
+    msgpack_pack_real_int64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_unsigned_short)(msgpack_pack_user x, unsigned short d)
+{
+#if defined(SIZEOF_SHORT)
+#if SIZEOF_SHORT == 2
+    msgpack_pack_real_uint16(x, d);
+#elif SIZEOF_SHORT == 4
+    msgpack_pack_real_uint32(x, d);
+#else
+    msgpack_pack_real_uint64(x, d);
+#endif
+
+#elif defined(USHRT_MAX)
+#if USHRT_MAX == 0xffffU
+    msgpack_pack_real_uint16(x, d);
+#elif USHRT_MAX == 0xffffffffU
+    msgpack_pack_real_uint32(x, d);
+#else
+    msgpack_pack_real_uint64(x, d);
+#endif
+
+#else
+if(sizeof(unsigned short) == 2) {
+    msgpack_pack_real_uint16(x, d);
+} else if(sizeof(unsigned short) == 4) {
+    msgpack_pack_real_uint32(x, d);
+} else {
+    msgpack_pack_real_uint64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_unsigned_int)(msgpack_pack_user x, unsigned int d)
+{
+#if defined(SIZEOF_INT)
+#if SIZEOF_INT == 2
+    msgpack_pack_real_uint16(x, d);
+#elif SIZEOF_INT == 4
+    msgpack_pack_real_uint32(x, d);
+#else
+    msgpack_pack_real_uint64(x, d);
+#endif
+
+#elif defined(UINT_MAX)
+#if UINT_MAX == 0xffffU
+    msgpack_pack_real_uint16(x, d);
+#elif UINT_MAX == 0xffffffffU
+    msgpack_pack_real_uint32(x, d);
+#else
+    msgpack_pack_real_uint64(x, d);
+#endif
+
+#else
+if(sizeof(unsigned int) == 2) {
+    msgpack_pack_real_uint16(x, d);
+} else if(sizeof(unsigned int) == 4) {
+    msgpack_pack_real_uint32(x, d);
+} else {
+    msgpack_pack_real_uint64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_unsigned_long)(msgpack_pack_user x, unsigned long d)
+{
+#if defined(SIZEOF_LONG)
+#if SIZEOF_LONG == 2
+    msgpack_pack_real_uint16(x, d);
+#elif SIZEOF_LONG == 4
+    msgpack_pack_real_uint32(x, d);
+#else
+    msgpack_pack_real_uint64(x, d);
+#endif
+
+#elif defined(ULONG_MAX)
+#if ULONG_MAX == 0xffffUL
+    msgpack_pack_real_uint16(x, d);
+#elif ULONG_MAX == 0xffffffffUL
+    msgpack_pack_real_uint32(x, d);
+#else
+    msgpack_pack_real_uint64(x, d);
+#endif
+
+#else
+if(sizeof(unsigned long) == 2) {
+    msgpack_pack_real_uint16(x, d);
+} else if(sizeof(unsigned long) == 4) {
+    msgpack_pack_real_uint32(x, d);
+} else {
+    msgpack_pack_real_uint64(x, d);
+}
+#endif
+}
+
+msgpack_pack_inline_func_cint(_unsigned_long_long)(msgpack_pack_user x, unsigned long long d)
+{
+#if defined(SIZEOF_LONG_LONG)
+#if SIZEOF_LONG_LONG == 2
+    msgpack_pack_real_uint16(x, d);
+#elif SIZEOF_LONG_LONG == 4
+    msgpack_pack_real_uint32(x, d);
+#else
+    msgpack_pack_real_uint64(x, d);
+#endif
+
+#elif defined(ULLONG_MAX)
+#if ULLONG_MAX == 0xffffUL
+    msgpack_pack_real_uint16(x, d);
+#elif ULLONG_MAX == 0xffffffffUL
+    msgpack_pack_real_uint32(x, d);
+#else
+    msgpack_pack_real_uint64(x, d);
+#endif
+
+#else
+if(sizeof(unsigned long long) == 2) {
+    msgpack_pack_real_uint16(x, d);
+} else if(sizeof(unsigned long long) == 4) {
+    msgpack_pack_real_uint32(x, d);
+} else {
+    msgpack_pack_real_uint64(x, d);
+}
+#endif
+}
+
+#undef msgpack_pack_inline_func_cint
+#endif
+
+
+
+/*
+ * Float
+ */
+
+msgpack_pack_inline_func(_float)(msgpack_pack_user x, float d)
+{
+    unsigned char buf[5];
+    union { float f; uint32_t i; } mem;
+    mem.f = d;
+    buf[0] = 0xca; _msgpack_store32(&buf[1], mem.i);
+    msgpack_pack_append_buffer(x, buf, 5);
+}
+
+msgpack_pack_inline_func(_double)(msgpack_pack_user x, double d)
+{
+    unsigned char buf[9];
+    union { double f; uint64_t i; } mem;
+    mem.f = d;
+    buf[0] = 0xcb;
+#if defined(TARGET_OS_IPHONE)
+    // ok
+#elif defined(__arm__) && !(__ARM_EABI__) // arm-oabi
+    // https://github.com/msgpack/msgpack-perl/pull/1
+    mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL);
+#endif
+    _msgpack_store64(&buf[1], mem.i);
+    msgpack_pack_append_buffer(x, buf, 9);
+}
+
+
+/*
+ * Nil
+ */
+
+msgpack_pack_inline_func(_nil)(msgpack_pack_user x)
+{
+    static const unsigned char d = 0xc0;
+    msgpack_pack_append_buffer(x, &d, 1);
+}
+
+
+/*
+ * Boolean
+ */
+
+msgpack_pack_inline_func(_true)(msgpack_pack_user x)
+{
+    static const unsigned char d = 0xc3;
+    msgpack_pack_append_buffer(x, &d, 1);
+}
+
+msgpack_pack_inline_func(_false)(msgpack_pack_user x)
+{
+    static const unsigned char d = 0xc2;
+    msgpack_pack_append_buffer(x, &d, 1);
+}
+
+
+/*
+ * Array
+ */
+
+msgpack_pack_inline_func(_array)(msgpack_pack_user x, size_t n)
+{
+    if(n < 16) {
+        unsigned char d = 0x90 | (uint8_t)n;
+        msgpack_pack_append_buffer(x, &d, 1);
+    } else if(n < 65536) {
+        unsigned char buf[3];
+        buf[0] = 0xdc; _msgpack_store16(&buf[1], (uint16_t)n);
+        msgpack_pack_append_buffer(x, buf, 3);
+    } else {
+        unsigned char buf[5];
+        buf[0] = 0xdd; _msgpack_store32(&buf[1], (uint32_t)n);
+        msgpack_pack_append_buffer(x, buf, 5);
+    }
+}
+
+
+/*
+ * Map
+ */
+
+msgpack_pack_inline_func(_map)(msgpack_pack_user x, size_t n)
+{
+    if(n < 16) {
+        unsigned char d = 0x80 | (uint8_t)n;
+        msgpack_pack_append_buffer(x, &TAKE8_8(d), 1);
+    } else if(n < 65536) {
+        unsigned char buf[3];
+        buf[0] = 0xde; _msgpack_store16(&buf[1], (uint16_t)n);
+        msgpack_pack_append_buffer(x, buf, 3);
+    } else {
+        unsigned char buf[5];
+        buf[0] = 0xdf; _msgpack_store32(&buf[1], (uint32_t)n);
+        msgpack_pack_append_buffer(x, buf, 5);
+    }
+}
+
+
+/*
+ * Str
+ */
+
+msgpack_pack_inline_func(_str)(msgpack_pack_user x, size_t l)
+{
+    if(l < 32) {
+        unsigned char d = 0xa0 | (uint8_t)l;
+        msgpack_pack_append_buffer(x, &TAKE8_8(d), 1);
+    } else if(l < 256) {
+        unsigned char buf[2];
+        buf[0] = 0xd9; buf[1] = (uint8_t)l;
+        msgpack_pack_append_buffer(x, buf, 2);
+    } else if(l < 65536) {
+        unsigned char buf[3];
+        buf[0] = 0xda; _msgpack_store16(&buf[1], (uint16_t)l);
+        msgpack_pack_append_buffer(x, buf, 3);
+    } else {
+        unsigned char buf[5];
+        buf[0] = 0xdb; _msgpack_store32(&buf[1], (uint32_t)l);
+        msgpack_pack_append_buffer(x, buf, 5);
+    }
+}
+
+msgpack_pack_inline_func(_str_body)(msgpack_pack_user x, const void* b, size_t l)
+{
+    msgpack_pack_append_buffer(x, (const unsigned char*)b, l);
+}
+
+/*
+ * Raw (V4)
+ */
+
+msgpack_pack_inline_func(_v4raw)(msgpack_pack_user x, size_t l)
+{
+    if(l < 32) {
+        unsigned char d = 0xa0 | (uint8_t)l;
+        msgpack_pack_append_buffer(x, &TAKE8_8(d), 1);
+    } else if(l < 65536) {
+        unsigned char buf[3];
+        buf[0] = 0xda; _msgpack_store16(&buf[1], (uint16_t)l);
+        msgpack_pack_append_buffer(x, buf, 3);
+    } else {
+        unsigned char buf[5];
+        buf[0] = 0xdb; _msgpack_store32(&buf[1], (uint32_t)l);
+        msgpack_pack_append_buffer(x, buf, 5);
+    }
+}
+
+msgpack_pack_inline_func(_v4raw_body)(msgpack_pack_user x, const void* b, size_t l)
+{
+    msgpack_pack_append_buffer(x, (const unsigned char*)b, l);
+}
+
+/*
+ * Bin
+ */
+
+msgpack_pack_inline_func(_bin)(msgpack_pack_user x, size_t l)
+{
+    if(l < 256) {
+        unsigned char buf[2];
+        buf[0] = 0xc4; buf[1] = (uint8_t)l;
+        msgpack_pack_append_buffer(x, buf, 2);
+    } else if(l < 65536) {
+        unsigned char buf[3];
+        buf[0] = 0xc5; _msgpack_store16(&buf[1], (uint16_t)l);
+        msgpack_pack_append_buffer(x, buf, 3);
+    } else {
+        unsigned char buf[5];
+        buf[0] = 0xc6; _msgpack_store32(&buf[1], (uint32_t)l);
+        msgpack_pack_append_buffer(x, buf, 5);
+    }
+}
+
+msgpack_pack_inline_func(_bin_body)(msgpack_pack_user x, const void* b, size_t l)
+{
+    msgpack_pack_append_buffer(x, (const unsigned char*)b, l);
+}
+
+/*
+ * Ext
+ */
+
+msgpack_pack_inline_func(_ext)(msgpack_pack_user x, size_t l, int8_t type)
+{
+    switch(l) {
+    case 1: {
+        unsigned char buf[2];
+        buf[0] = 0xd4;
+        buf[1] = (unsigned char)type;
+        msgpack_pack_append_buffer(x, buf, 2);
+    } break;
+    case 2: {
+        unsigned char buf[2];
+        buf[0] = 0xd5;
+        buf[1] = (unsigned char)type;
+        msgpack_pack_append_buffer(x, buf, 2);
+    } break;
+    case 4: {
+        unsigned char buf[2];
+        buf[0] = 0xd6;
+        buf[1] = (unsigned char)type;
+        msgpack_pack_append_buffer(x, buf, 2);
+    } break;
+    case 8: {
+        unsigned char buf[2];
+        buf[0] = 0xd7;
+        buf[1] = (unsigned char)type;
+        msgpack_pack_append_buffer(x, buf, 2);
+    } break;
+    case 16: {
+        unsigned char buf[2];
+        buf[0] = 0xd8;
+        buf[1] = (unsigned char)type;
+        msgpack_pack_append_buffer(x, buf, 2);
+    } break;
+    default:
+        if(l < 256) {
+            unsigned char buf[3];
+            buf[0] = 0xc7;
+            buf[1] = (unsigned char)l;
+            buf[2] = (unsigned char)type;
+            msgpack_pack_append_buffer(x, buf, 3);
+        } else if(l < 65536) {
+            unsigned char buf[4];
+            buf[0] = 0xc8;
+            _msgpack_store16(&buf[1], l);
+            buf[3] = (unsigned char)type;
+            msgpack_pack_append_buffer(x, buf, 4);
+        } else {
+            unsigned char buf[6];
+            buf[0] = 0xc9;
+            _msgpack_store32(&buf[1], l);
+            buf[5] = (unsigned char)type;
+            msgpack_pack_append_buffer(x, buf, 6);
+        }
+        break;
+    }
+}
+
+msgpack_pack_inline_func(_ext_body)(msgpack_pack_user x, const void* b, size_t l)
+{
+    msgpack_pack_append_buffer(x, (const unsigned char*)b, l);
+}
+
+msgpack_pack_inline_func(_timestamp)(msgpack_pack_user x, const msgpack_timestamp* d)
+{
+    if ((((int64_t)d->tv_sec) >> 34) == 0) {
+        uint64_t data64 = ((uint64_t) d->tv_nsec << 34) | (uint64_t)d->tv_sec;
+        if ((data64 & 0xffffffff00000000L) == 0)   {
+            // timestamp 32
+            char buf[4];
+            uint32_t data32 = (uint32_t)data64;
+            msgpack_pack_ext(x, 4, -1);
+            _msgpack_store32(buf, data32);
+            msgpack_pack_append_buffer(x, buf, 4);
+        } else {
+            // timestamp 64
+            char buf[8];
+            msgpack_pack_ext(x, 8, -1);
+            _msgpack_store64(buf, data64);
+            msgpack_pack_append_buffer(x, buf, 8);
+        }
+    } else  {
+        // timestamp 96
+        char buf[12];
+        _msgpack_store32(&buf[0], d->tv_nsec);
+        _msgpack_store64(&buf[4], d->tv_sec);
+        msgpack_pack_ext(x, 12, -1);
+        msgpack_pack_append_buffer(x, buf, 12);
+    }
+}
+
+#undef msgpack_pack_inline_func
+#undef msgpack_pack_user
+#undef msgpack_pack_append_buffer
+
+#undef TAKE8_8
+#undef TAKE8_16
+#undef TAKE8_32
+#undef TAKE8_64
+
+#undef msgpack_pack_real_uint8
+#undef msgpack_pack_real_uint16
+#undef msgpack_pack_real_uint32
+#undef msgpack_pack_real_uint64
+#undef msgpack_pack_real_int8
+#undef msgpack_pack_real_int16
+#undef msgpack_pack_real_int32
+#undef msgpack_pack_real_int64
+
+#if defined(_MSC_VER)
+#   pragma warning(pop)
+#endif
diff --git a/src/vendor/msgpack/predef.h b/src/vendor/msgpack/predef.h
new file mode 100644 (file)
index 0000000..b2309ee
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+Copyright Rene Rivera 2008-2015
+Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at
+http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#if !defined(MSGPACK_PREDEF_H) || defined(MSGPACK_PREDEF_INTERNAL_GENERATE_TESTS)
+#ifndef MSGPACK_PREDEF_H
+#define MSGPACK_PREDEF_H
+#endif
+
+#include <vendor/msgpack/predef/language.h>
+#include <vendor/msgpack/predef/architecture.h>
+#include <vendor/msgpack/predef/compiler.h>
+#include <vendor/msgpack/predef/library.h>
+#include <vendor/msgpack/predef/os.h>
+#include <vendor/msgpack/predef/other.h>
+#include <vendor/msgpack/predef/platform.h>
+#include <vendor/msgpack/predef/hardware.h>
+
+#include <vendor/msgpack/predef/version.h>
+
+#endif
diff --git a/src/vendor/msgpack/sbuffer.h b/src/vendor/msgpack/sbuffer.h
new file mode 100644 (file)
index 0000000..c494bae
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * MessagePack for C simple buffer implementation
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_SBUFFER_H
+#define MSGPACK_SBUFFER_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @defgroup msgpack_sbuffer Simple buffer
+ * @ingroup msgpack_buffer
+ * @{
+ */
+
+typedef struct msgpack_sbuffer {
+    size_t size;
+    char* data;
+    size_t alloc;
+} msgpack_sbuffer;
+
+static inline void msgpack_sbuffer_init(msgpack_sbuffer* sbuf)
+{
+    memset(sbuf, 0, sizeof(msgpack_sbuffer));
+}
+
+static inline void msgpack_sbuffer_destroy(msgpack_sbuffer* sbuf)
+{
+    free(sbuf->data);
+}
+
+static inline msgpack_sbuffer* msgpack_sbuffer_new(void)
+{
+    return (msgpack_sbuffer*)calloc(1, sizeof(msgpack_sbuffer));
+}
+
+static inline void msgpack_sbuffer_free(msgpack_sbuffer* sbuf)
+{
+    if(sbuf == NULL) { return; }
+    msgpack_sbuffer_destroy(sbuf);
+    free(sbuf);
+}
+
+#ifndef MSGPACK_SBUFFER_INIT_SIZE
+#define MSGPACK_SBUFFER_INIT_SIZE 8192
+#endif
+
+static inline int msgpack_sbuffer_write(void* data, const char* buf, size_t len)
+{
+    msgpack_sbuffer* sbuf = (msgpack_sbuffer*)data;
+
+    if(sbuf->alloc - sbuf->size < len) {
+        void* tmp;
+        size_t nsize = (sbuf->alloc) ?
+                sbuf->alloc * 2 : MSGPACK_SBUFFER_INIT_SIZE;
+
+        while(nsize < sbuf->size + len) {
+            size_t tmp_nsize = nsize * 2;
+            if (tmp_nsize <= nsize) {
+                nsize = sbuf->size + len;
+                break;
+            }
+            nsize = tmp_nsize;
+        }
+
+        tmp = realloc(sbuf->data, nsize);
+        if(!tmp) { return -1; }
+
+        sbuf->data = (char*)tmp;
+        sbuf->alloc = nsize;
+    }
+
+    memcpy(sbuf->data + sbuf->size, buf, len);
+    sbuf->size += len;
+    return 0;
+}
+
+static inline char* msgpack_sbuffer_release(msgpack_sbuffer* sbuf)
+{
+    char* tmp = sbuf->data;
+    sbuf->size = 0;
+    sbuf->data = NULL;
+    sbuf->alloc = 0;
+    return tmp;
+}
+
+static inline void msgpack_sbuffer_clear(msgpack_sbuffer* sbuf)
+{
+    sbuf->size = 0;
+}
+
+/** @} */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/sbuffer.h */
diff --git a/src/vendor/msgpack/sysdep.h b/src/vendor/msgpack/sysdep.h
new file mode 100644 (file)
index 0000000..ad5f615
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * MessagePack system dependencies
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_SYSDEP_H
+#define MSGPACK_SYSDEP_H
+
+#include <stdlib.h>
+#include <stddef.h>
+
+#include "vendor/msgpack/lttng-config.h"
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+#   define snprintf(buf, len, format,...) _snprintf_s(buf, len, _TRUNCATE, format, __VA_ARGS__)
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+    typedef signed __int8 int8_t;
+    typedef unsigned __int8 uint8_t;
+    typedef signed __int16 int16_t;
+    typedef unsigned __int16 uint16_t;
+    typedef signed __int32 int32_t;
+    typedef unsigned __int32 uint32_t;
+    typedef signed __int64 int64_t;
+    typedef unsigned __int64 uint64_t;
+#elif defined(_MSC_VER)  // && _MSC_VER >= 1600
+#   include <stdint.h>
+#else
+#   include <stdint.h>
+#   include <stdbool.h>
+#endif
+
+#if !defined(MSGPACK_DLLEXPORT)
+#if defined(_MSC_VER)
+#   define MSGPACK_DLLEXPORT __declspec(dllexport)
+#else  /* _MSC_VER */
+#   define MSGPACK_DLLEXPORT
+#endif /* _MSC_VER */
+#endif
+
+#ifdef _WIN32
+#   if defined(_KERNEL_MODE)
+#       define _msgpack_atomic_counter_header <ntddk.h>
+#   else
+#       define _msgpack_atomic_counter_header <windows.h>
+#       if !defined(WIN32_LEAN_AND_MEAN)
+#           define WIN32_LEAN_AND_MEAN
+#       endif /* WIN32_LEAN_AND_MEAN */
+#   endif
+    typedef long _msgpack_atomic_counter_t;
+#if defined(_AMD64_) || defined(_M_X64) || defined(_M_ARM64)
+#    define _msgpack_sync_decr_and_fetch(ptr) _InterlockedDecrement(ptr)
+#    define _msgpack_sync_incr_and_fetch(ptr) _InterlockedIncrement(ptr)
+#else
+#    define _msgpack_sync_decr_and_fetch(ptr) InterlockedDecrement(ptr)
+#    define _msgpack_sync_incr_and_fetch(ptr) InterlockedIncrement(ptr)
+#endif
+#elif defined(__GNUC__) && ((__GNUC__*10 + __GNUC_MINOR__) < 41)
+
+#   if defined(__cplusplus)
+#       define _msgpack_atomic_counter_header "vendor/msgpack/gcc_atomic.hpp"
+#   else
+#       define _msgpack_atomic_counter_header "vendor/msgpack/gcc_atomic.h"
+#   endif
+
+#else
+    typedef unsigned int _msgpack_atomic_counter_t;
+#   define _msgpack_sync_decr_and_fetch(ptr) __sync_sub_and_fetch(ptr, 1)
+#   define _msgpack_sync_incr_and_fetch(ptr) __sync_add_and_fetch(ptr, 1)
+#endif
+
+#ifdef _WIN32
+
+#   ifdef __cplusplus
+    /* numeric_limits<T>::min,max */
+#       ifdef max
+#           undef max
+#       endif
+#       ifdef min
+#           undef min
+#       endif
+#   endif
+
+#elif defined(unix) || defined(__unix) || defined(__APPLE__) || defined(__OpenBSD__)
+
+#include <arpa/inet.h>  /* __BYTE_ORDER */
+#   if defined(linux)
+#       include <byteswap.h>
+#   endif
+
+#endif
+
+#if !defined(MSGPACK_ENDIAN_LITTLE_BYTE) && !defined(MSGPACK_ENDIAN_BIG_BYTE)
+#include <msgpack/predef/other/endian.h>
+#endif // !defined(MSGPACK_ENDIAN_LITTLE_BYTE) && !defined(MSGPACK_ENDIAN_BIG_BYTE)
+
+#if MSGPACK_ENDIAN_LITTLE_BYTE
+
+#   if defined(unix) || defined(__unix) || defined(__APPLE__) || defined(__OpenBSD__)
+#       define _msgpack_be16(x) ntohs((uint16_t)x)
+#   else
+#       if defined(ntohs)
+#           define _msgpack_be16(x) ntohs(x)
+#       elif defined(_byteswap_ushort) || (defined(_MSC_VER) && _MSC_VER >= 1400)
+#           define _msgpack_be16(x) ((uint16_t)_byteswap_ushort((unsigned short)x))
+#       else
+#           define _msgpack_be16(x) ( \
+                ((((uint16_t)x) <<  8) ) | \
+                ((((uint16_t)x) >>  8) ) )
+#        endif
+#   endif
+
+#   if defined(unix) || defined(__unix) || defined(__APPLE__) || defined(__OpenBSD__)
+#       define _msgpack_be32(x) ntohl((uint32_t)x)
+#   else
+#       if defined(ntohl)
+#           define _msgpack_be32(x) ntohl(x)
+#       elif defined(_byteswap_ulong) || (defined(_MSC_VER) && _MSC_VER >= 1400)
+#           define _msgpack_be32(x) ((uint32_t)_byteswap_ulong((unsigned long)x))
+#       else
+#           define _msgpack_be32(x) \
+                ( ((((uint32_t)x) << 24)               ) | \
+                  ((((uint32_t)x) <<  8) & 0x00ff0000U ) | \
+                  ((((uint32_t)x) >>  8) & 0x0000ff00U ) | \
+                  ((((uint32_t)x) >> 24)               ) )
+#       endif
+#   endif
+
+#   if defined(_byteswap_uint64) || (defined(_MSC_VER) && _MSC_VER >= 1400)
+#        define _msgpack_be64(x) (_byteswap_uint64(x))
+#   elif defined(bswap_64)
+#        define _msgpack_be64(x) bswap_64(x)
+#   elif defined(__DARWIN_OSSwapInt64)
+#        define _msgpack_be64(x) __DARWIN_OSSwapInt64(x)
+#   else
+#        define _msgpack_be64(x) \
+             ( ((((uint64_t)x) << 56)                         ) | \
+               ((((uint64_t)x) << 40) & 0x00ff000000000000ULL ) | \
+               ((((uint64_t)x) << 24) & 0x0000ff0000000000ULL ) | \
+               ((((uint64_t)x) <<  8) & 0x000000ff00000000ULL ) | \
+               ((((uint64_t)x) >>  8) & 0x00000000ff000000ULL ) | \
+               ((((uint64_t)x) >> 24) & 0x0000000000ff0000ULL ) | \
+               ((((uint64_t)x) >> 40) & 0x000000000000ff00ULL ) | \
+               ((((uint64_t)x) >> 56)                         ) )
+#   endif
+
+#elif MSGPACK_ENDIAN_BIG_BYTE
+
+#   define _msgpack_be16(x) (x)
+#   define _msgpack_be32(x) (x)
+#   define _msgpack_be64(x) (x)
+
+#else
+#   error msgpack-c supports only big endian and little endian
+#endif /* MSGPACK_ENDIAN_LITTLE_BYTE */
+
+#define _msgpack_load16(cast, from, to) do {       \
+        memcpy((cast*)(to), (from), sizeof(cast)); \
+        *(to) = (cast)_msgpack_be16(*(to));      \
+    } while (0);
+
+#define _msgpack_load32(cast, from, to) do {       \
+        memcpy((cast*)(to), (from), sizeof(cast)); \
+        *(to) = (cast)_msgpack_be32(*(to));        \
+    } while (0);
+#define _msgpack_load64(cast, from, to) do {       \
+        memcpy((cast*)(to), (from), sizeof(cast)); \
+        *(to) = (cast)_msgpack_be64(*(to));        \
+    } while (0);
+
+#define _msgpack_store16(to, num) \
+    do { uint16_t val = _msgpack_be16(num); memcpy(to, &val, 2); } while(0)
+#define _msgpack_store32(to, num) \
+    do { uint32_t val = _msgpack_be32(num); memcpy(to, &val, 4); } while(0)
+#define _msgpack_store64(to, num) \
+    do { uint64_t val = _msgpack_be64(num); memcpy(to, &val, 8); } while(0)
+
+/*
+#define _msgpack_load16(cast, from) \
+    ({ cast val; memcpy(&val, (char*)from, 2); _msgpack_be16(val); })
+#define _msgpack_load32(cast, from) \
+    ({ cast val; memcpy(&val, (char*)from, 4); _msgpack_be32(val); })
+#define _msgpack_load64(cast, from) \
+    ({ cast val; memcpy(&val, (char*)from, 8); _msgpack_be64(val); })
+*/
+
+
+#if !defined(__cplusplus) && defined(_MSC_VER)
+#  if !defined(_KERNEL_MODE)
+#    if !defined(FALSE)
+#      define FALSE (0)
+#    endif
+#    if !defined(TRUE)
+#      define TRUE (!FALSE)
+#    endif
+#  endif
+#  if _MSC_VER >= 1800
+#    include <stdbool.h>
+#  else
+#    define bool int
+#    define true TRUE
+#    define false FALSE
+#  endif
+#  define inline __inline
+#endif
+
+#ifdef __APPLE__
+#  include <TargetConditionals.h>
+#endif
+
+#endif /* msgpack/sysdep.h */
diff --git a/src/vendor/msgpack/timestamp.h b/src/vendor/msgpack/timestamp.h
new file mode 100644 (file)
index 0000000..df94253
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * MessagePack for C TimeStamp
+ *
+ * Copyright (C) 2018 KONDO Takatoshi
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_TIMESTAMP_H
+#define MSGPACK_TIMESTAMP_H
+
+#include <vendor/msgpack/object.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct msgpack_timestamp {
+    int64_t tv_sec;
+    uint32_t tv_nsec;
+} msgpack_timestamp;
+
+static inline bool msgpack_object_to_timestamp(const msgpack_object* obj, msgpack_timestamp* ts) {
+    if (obj->type != MSGPACK_OBJECT_EXT) return false;
+    if (obj->via.ext.type != -1) return false;
+    switch (obj->via.ext.size) {
+    case 4:
+        ts->tv_nsec = 0;
+        {
+            uint32_t v;
+            _msgpack_load32(uint32_t, obj->via.ext.ptr, &v);
+            ts->tv_sec = v;
+        }
+        return true;
+    case 8: {
+        uint64_t value;
+        _msgpack_load64(uint64_t, obj->via.ext.ptr, &value);
+        ts->tv_nsec = (uint32_t)(value >> 34);
+        ts->tv_sec = value & 0x00000003ffffffffLL;
+        return true;
+    }
+    case 12:
+        _msgpack_load32(uint32_t, obj->via.ext.ptr, &ts->tv_nsec);
+        _msgpack_load64(int64_t, obj->via.ext.ptr + 4, &ts->tv_sec);
+        return true;
+    default:
+        return false;
+    }
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/timestamp.h */
diff --git a/src/vendor/msgpack/unpack.c b/src/vendor/msgpack/unpack.c
new file mode 100644 (file)
index 0000000..b390f58
--- /dev/null
@@ -0,0 +1,702 @@
+/*
+ * MessagePack for C unpacking routine
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#include "vendor/msgpack/unpack.h"
+#include "vendor/msgpack/unpack_define.h"
+#include "vendor/msgpack/util.h"
+#include <stdlib.h>
+
+#ifdef _msgpack_atomic_counter_header
+#include _msgpack_atomic_counter_header
+#endif
+
+
+typedef struct {
+    msgpack_zone** z;
+    bool referenced;
+} unpack_user;
+
+
+#define msgpack_unpack_struct(name) \
+    struct template ## name
+
+#define msgpack_unpack_func(ret, name) \
+    ret template ## name
+
+#define msgpack_unpack_callback(name) \
+    template_callback ## name
+
+#define msgpack_unpack_object msgpack_object
+
+#define msgpack_unpack_user unpack_user
+
+
+struct template_context;
+typedef struct template_context template_context;
+
+static void template_init(template_context* ctx);
+
+static msgpack_object template_data(template_context* ctx);
+
+static int template_execute(
+    template_context* ctx, const char* data, size_t len, size_t* off);
+
+
+static inline msgpack_object template_callback_root(unpack_user* u)
+{
+    msgpack_object o;
+    MSGPACK_UNUSED(u);
+    o.type = MSGPACK_OBJECT_NIL;
+    return o;
+}
+
+static inline int template_callback_uint8(unpack_user* u, uint8_t d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
+    o->via.u64 = d;
+    return 0;
+}
+
+static inline int template_callback_uint16(unpack_user* u, uint16_t d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
+    o->via.u64 = d;
+    return 0;
+}
+
+static inline int template_callback_uint32(unpack_user* u, uint32_t d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
+    o->via.u64 = d;
+    return 0;
+}
+
+static inline int template_callback_uint64(unpack_user* u, uint64_t d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
+    o->via.u64 = d;
+    return 0;
+}
+
+static inline int template_callback_int8(unpack_user* u, int8_t d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    if(d >= 0) {
+        o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
+        o->via.u64 = (uint64_t)d;
+        return 0;
+    }
+    else {
+        o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER;
+        o->via.i64 = d;
+        return 0;
+    }
+}
+
+static inline int template_callback_int16(unpack_user* u, int16_t d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    if(d >= 0) {
+        o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
+        o->via.u64 = (uint64_t)d;
+        return 0;
+    }
+    else {
+        o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER;
+        o->via.i64 = d;
+        return 0;
+    }
+}
+
+static inline int template_callback_int32(unpack_user* u, int32_t d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    if(d >= 0) {
+        o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
+        o->via.u64 = (uint64_t)d;
+        return 0;
+    }
+    else {
+        o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER;
+        o->via.i64 = d;
+        return 0;
+    }
+}
+
+static inline int template_callback_int64(unpack_user* u, int64_t d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    if(d >= 0) {
+        o->type = MSGPACK_OBJECT_POSITIVE_INTEGER;
+        o->via.u64 = (uint64_t)d;
+        return 0;
+    }
+    else {
+        o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER;
+        o->via.i64 = d;
+        return 0;
+    }
+}
+
+static inline int template_callback_float(unpack_user* u, float d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    o->type = MSGPACK_OBJECT_FLOAT32;
+    o->via.f64 = d;
+    return 0;
+}
+
+static inline int template_callback_double(unpack_user* u, double d, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    o->type = MSGPACK_OBJECT_FLOAT64;
+    o->via.f64 = d;
+    return 0;
+}
+
+static inline int template_callback_nil(unpack_user* u, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    o->type = MSGPACK_OBJECT_NIL;
+    return 0;
+}
+
+static inline int template_callback_true(unpack_user* u, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    o->type = MSGPACK_OBJECT_BOOLEAN;
+    o->via.boolean = true;
+    return 0;
+}
+
+static inline int template_callback_false(unpack_user* u, msgpack_object* o)
+{
+    MSGPACK_UNUSED(u);
+    o->type = MSGPACK_OBJECT_BOOLEAN;
+    o->via.boolean = false;
+    return 0;
+}
+
+static inline int template_callback_array(unpack_user* u, unsigned int n, msgpack_object* o)
+{
+    size_t size;
+    // Let's leverage the fact that sizeof(msgpack_object) is a compile time constant
+    // to check for int overflows.
+    // Note - while n is constrained to 32-bit, the product of n * sizeof(msgpack_object)
+    // might not be constrained to 4GB on 64-bit systems
+#if SIZE_MAX == UINT_MAX
+    if (n > SIZE_MAX/sizeof(msgpack_object))
+        return MSGPACK_UNPACK_NOMEM_ERROR;
+#endif
+
+    o->type = MSGPACK_OBJECT_ARRAY;
+    o->via.array.size = 0;
+
+    size = n * sizeof(msgpack_object);
+
+    if (*u->z == NULL) {
+        *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE);
+        if(*u->z == NULL) {
+            return MSGPACK_UNPACK_NOMEM_ERROR;
+        }
+    }
+
+    // Unsure whether size = 0 should be an error, and if so, what to return
+    o->via.array.ptr = (msgpack_object*)msgpack_zone_malloc(*u->z, size);
+    if(o->via.array.ptr == NULL) { return MSGPACK_UNPACK_NOMEM_ERROR; }
+    return 0;
+}
+
+static inline int template_callback_array_item(unpack_user* u, msgpack_object* c, msgpack_object o)
+{
+    MSGPACK_UNUSED(u);
+#if defined(__GNUC__) && !defined(__clang__)
+    memcpy(&c->via.array.ptr[c->via.array.size], &o, sizeof(msgpack_object));
+#else  /* __GNUC__ && !__clang__ */
+    c->via.array.ptr[c->via.array.size] = o;
+#endif /* __GNUC__ && !__clang__ */
+    ++c->via.array.size;
+    return 0;
+}
+
+static inline int template_callback_map(unpack_user* u, unsigned int n, msgpack_object* o)
+{
+    size_t size;
+    // Let's leverage the fact that sizeof(msgpack_object_kv) is a compile time constant
+    // to check for int overflows
+    // Note - while n is constrained to 32-bit, the product of n * sizeof(msgpack_object)
+    // might not be constrained to 4GB on 64-bit systems
+
+    // Note - this will always be false on 64-bit systems
+#if SIZE_MAX == UINT_MAX
+    if (n > SIZE_MAX/sizeof(msgpack_object_kv))
+        return MSGPACK_UNPACK_NOMEM_ERROR;
+#endif
+
+    o->type = MSGPACK_OBJECT_MAP;
+    o->via.map.size = 0;
+
+    size = n * sizeof(msgpack_object_kv);
+
+    if (*u->z == NULL) {
+        *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE);
+        if(*u->z == NULL) {
+            return MSGPACK_UNPACK_NOMEM_ERROR;
+        }
+    }
+
+    // Should size = 0 be an error? If so, what error to return?
+    o->via.map.ptr = (msgpack_object_kv*)msgpack_zone_malloc(*u->z, size);
+    if(o->via.map.ptr == NULL) { return MSGPACK_UNPACK_NOMEM_ERROR; }
+    return 0;
+}
+
+static inline int template_callback_map_item(unpack_user* u, msgpack_object* c, msgpack_object k, msgpack_object v)
+{
+    MSGPACK_UNUSED(u);
+#if defined(__GNUC__) && !defined(__clang__)
+    memcpy(&c->via.map.ptr[c->via.map.size].key, &k, sizeof(msgpack_object));
+    memcpy(&c->via.map.ptr[c->via.map.size].val, &v, sizeof(msgpack_object));
+#else  /* __GNUC__ && !__clang__ */
+    c->via.map.ptr[c->via.map.size].key = k;
+    c->via.map.ptr[c->via.map.size].val = v;
+#endif /* __GNUC__ && !__clang__ */
+    ++c->via.map.size;
+    return 0;
+}
+
+static inline int template_callback_str(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o)
+{
+    MSGPACK_UNUSED(b);
+    if (*u->z == NULL) {
+        *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE);
+        if(*u->z == NULL) {
+            return MSGPACK_UNPACK_NOMEM_ERROR;
+        }
+    }
+    o->type = MSGPACK_OBJECT_STR;
+    o->via.str.ptr = p;
+    o->via.str.size = l;
+    u->referenced = true;
+    return 0;
+}
+
+static inline int template_callback_bin(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o)
+{
+    MSGPACK_UNUSED(b);
+    if (*u->z == NULL) {
+        *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE);
+        if(*u->z == NULL) {
+            return MSGPACK_UNPACK_NOMEM_ERROR;
+        }
+    }
+    o->type = MSGPACK_OBJECT_BIN;
+    o->via.bin.ptr = p;
+    o->via.bin.size = l;
+    u->referenced = true;
+    return 0;
+}
+
+static inline int template_callback_ext(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o)
+{
+    MSGPACK_UNUSED(b);
+    if (l == 0) {
+        return MSGPACK_UNPACK_PARSE_ERROR;
+    }
+    if (*u->z == NULL) {
+        *u->z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE);
+        if(*u->z == NULL) {
+            return MSGPACK_UNPACK_NOMEM_ERROR;
+        }
+    }
+    o->type = MSGPACK_OBJECT_EXT;
+    o->via.ext.type = *p;
+    o->via.ext.ptr = p + 1;
+    o->via.ext.size = l - 1;
+    u->referenced = true;
+    return 0;
+}
+
+#include "vendor/msgpack/unpack_template.h"
+
+
+#define CTX_CAST(m) ((template_context*)(m))
+#define CTX_REFERENCED(mpac) CTX_CAST((mpac)->ctx)->user.referenced
+
+#define COUNTER_SIZE (sizeof(_msgpack_atomic_counter_t))
+
+
+static inline void init_count(void* buffer)
+{
+    *(volatile _msgpack_atomic_counter_t*)buffer = 1;
+}
+
+static inline void decr_count(void* buffer)
+{
+    // atomic if(--*(_msgpack_atomic_counter_t*)buffer == 0) { free(buffer); }
+    if(_msgpack_sync_decr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer) == 0) {
+        free(buffer);
+    }
+}
+
+static inline void incr_count(void* buffer)
+{
+    // atomic ++*(_msgpack_atomic_counter_t*)buffer;
+    _msgpack_sync_incr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer);
+}
+
+static inline _msgpack_atomic_counter_t get_count(void* buffer)
+{
+    return *(volatile _msgpack_atomic_counter_t*)buffer;
+}
+
+bool msgpack_unpacker_init(msgpack_unpacker* mpac, size_t initial_buffer_size)
+{
+    char* buffer;
+    void* ctx;
+
+    if(initial_buffer_size < COUNTER_SIZE) {
+        initial_buffer_size = COUNTER_SIZE;
+    }
+
+    buffer = (char*)malloc(initial_buffer_size);
+    if(buffer == NULL) {
+        return false;
+    }
+
+    ctx = malloc(sizeof(template_context));
+    if(ctx == NULL) {
+        free(buffer);
+        return false;
+    }
+
+    mpac->buffer = buffer;
+    mpac->used = COUNTER_SIZE;
+    mpac->free = initial_buffer_size - mpac->used;
+    mpac->off = COUNTER_SIZE;
+    mpac->parsed = 0;
+    mpac->initial_buffer_size = initial_buffer_size;
+    mpac->z = NULL;
+    mpac->ctx = ctx;
+
+    init_count(mpac->buffer);
+
+    template_init(CTX_CAST(mpac->ctx));
+    CTX_CAST(mpac->ctx)->user.z = &mpac->z;
+    CTX_CAST(mpac->ctx)->user.referenced = false;
+
+    return true;
+}
+
+void msgpack_unpacker_destroy(msgpack_unpacker* mpac)
+{
+    msgpack_zone_free(mpac->z);
+    free(mpac->ctx);
+    decr_count(mpac->buffer);
+}
+
+msgpack_unpacker* msgpack_unpacker_new(size_t initial_buffer_size)
+{
+    msgpack_unpacker* mpac = (msgpack_unpacker*)malloc(sizeof(msgpack_unpacker));
+    if(mpac == NULL) {
+        return NULL;
+    }
+
+    if(!msgpack_unpacker_init(mpac, initial_buffer_size)) {
+        free(mpac);
+        return NULL;
+    }
+
+    return mpac;
+}
+
+void msgpack_unpacker_free(msgpack_unpacker* mpac)
+{
+    msgpack_unpacker_destroy(mpac);
+    free(mpac);
+}
+
+bool msgpack_unpacker_expand_buffer(msgpack_unpacker* mpac, size_t size)
+{
+    if(mpac->used == mpac->off && get_count(mpac->buffer) == 1
+            && !CTX_REFERENCED(mpac)) {
+        // rewind buffer
+        mpac->free += mpac->used - COUNTER_SIZE;
+        mpac->used = COUNTER_SIZE;
+        mpac->off = COUNTER_SIZE;
+
+        if(mpac->free >= size) {
+            return true;
+        }
+    }
+
+    if(mpac->off == COUNTER_SIZE) {
+        char* tmp;
+        size_t next_size = (mpac->used + mpac->free) * 2;  // include COUNTER_SIZE
+        while(next_size < size + mpac->used) {
+            size_t tmp_next_size = next_size * 2;
+            if (tmp_next_size <= next_size) {
+                next_size = size + mpac->used;
+                break;
+            }
+            next_size = tmp_next_size;
+        }
+
+        tmp = (char*)realloc(mpac->buffer, next_size);
+        if(tmp == NULL) {
+            return false;
+        }
+
+        mpac->buffer = tmp;
+        mpac->free = next_size - mpac->used;
+
+    } else {
+        char* tmp;
+        size_t next_size = mpac->initial_buffer_size;  // include COUNTER_SIZE
+        size_t not_parsed = mpac->used - mpac->off;
+        while(next_size < size + not_parsed + COUNTER_SIZE) {
+            size_t tmp_next_size = next_size * 2;
+            if (tmp_next_size <= next_size) {
+                next_size = size + not_parsed + COUNTER_SIZE;
+                break;
+            }
+            next_size = tmp_next_size;
+        }
+
+        tmp = (char*)malloc(next_size);
+        if(tmp == NULL) {
+            return false;
+        }
+
+        init_count(tmp);
+
+        memcpy(tmp+COUNTER_SIZE, mpac->buffer+mpac->off, not_parsed);
+
+        if(CTX_REFERENCED(mpac)) {
+            if(!msgpack_zone_push_finalizer(mpac->z, decr_count, mpac->buffer)) {
+                free(tmp);
+                return false;
+            }
+            CTX_REFERENCED(mpac) = false;
+        } else {
+            decr_count(mpac->buffer);
+        }
+
+        mpac->buffer = tmp;
+        mpac->used = not_parsed + COUNTER_SIZE;
+        mpac->free = next_size - mpac->used;
+        mpac->off = COUNTER_SIZE;
+    }
+
+    return true;
+}
+
+int msgpack_unpacker_execute(msgpack_unpacker* mpac)
+{
+    size_t off = mpac->off;
+    int ret = template_execute(CTX_CAST(mpac->ctx),
+            mpac->buffer, mpac->used, &mpac->off);
+    if(mpac->off > off) {
+        mpac->parsed += mpac->off - off;
+    }
+    return ret;
+}
+
+msgpack_object msgpack_unpacker_data(msgpack_unpacker* mpac)
+{
+    return template_data(CTX_CAST(mpac->ctx));
+}
+
+msgpack_zone* msgpack_unpacker_release_zone(msgpack_unpacker* mpac)
+{
+    msgpack_zone* old = mpac->z;
+
+    if (old == NULL) return NULL;
+    if(!msgpack_unpacker_flush_zone(mpac)) {
+        return NULL;
+    }
+
+    mpac->z = NULL;
+    CTX_CAST(mpac->ctx)->user.z = &mpac->z;
+
+    return old;
+}
+
+void msgpack_unpacker_reset_zone(msgpack_unpacker* mpac)
+{
+    msgpack_zone_clear(mpac->z);
+}
+
+bool msgpack_unpacker_flush_zone(msgpack_unpacker* mpac)
+{
+    if(CTX_REFERENCED(mpac)) {
+        if(!msgpack_zone_push_finalizer(mpac->z, decr_count, mpac->buffer)) {
+            return false;
+        }
+        CTX_REFERENCED(mpac) = false;
+
+        incr_count(mpac->buffer);
+    }
+
+    return true;
+}
+
+void msgpack_unpacker_reset(msgpack_unpacker* mpac)
+{
+    template_init(CTX_CAST(mpac->ctx));
+    // don't reset referenced flag
+    mpac->parsed = 0;
+}
+
+static inline msgpack_unpack_return unpacker_next(msgpack_unpacker* mpac,
+                                                  msgpack_unpacked* result)
+{
+    int ret;
+
+    msgpack_unpacked_destroy(result);
+
+    ret = msgpack_unpacker_execute(mpac);
+
+    if(ret < 0) {
+        result->zone = NULL;
+        memset(&result->data, 0, sizeof(msgpack_object));
+        return (msgpack_unpack_return)ret;
+    }
+
+    if(ret == 0) {
+        return MSGPACK_UNPACK_CONTINUE;
+    }
+    result->zone = msgpack_unpacker_release_zone(mpac);
+    result->data = msgpack_unpacker_data(mpac);
+
+    return MSGPACK_UNPACK_SUCCESS;
+}
+
+msgpack_unpack_return msgpack_unpacker_next(msgpack_unpacker* mpac,
+                                            msgpack_unpacked* result)
+{
+    msgpack_unpack_return ret;
+
+    ret = unpacker_next(mpac, result);
+    if (ret == MSGPACK_UNPACK_SUCCESS) {
+        msgpack_unpacker_reset(mpac);
+    }
+
+    return ret;
+}
+
+msgpack_unpack_return
+msgpack_unpacker_next_with_size(msgpack_unpacker* mpac,
+                                msgpack_unpacked* result, size_t *p_bytes)
+{
+    msgpack_unpack_return ret;
+
+    ret = unpacker_next(mpac, result);
+    if (ret == MSGPACK_UNPACK_SUCCESS || ret == MSGPACK_UNPACK_CONTINUE) {
+        *p_bytes = mpac->parsed;
+    }
+
+    if (ret == MSGPACK_UNPACK_SUCCESS) {
+        msgpack_unpacker_reset(mpac);
+    }
+
+    return ret;
+}
+
+msgpack_unpack_return
+msgpack_unpack(const char* data, size_t len, size_t* off,
+        msgpack_zone* result_zone, msgpack_object* result)
+{
+    size_t noff = 0;
+    if(off != NULL) { noff = *off; }
+
+    if(len <= noff) {
+        // FIXME
+        return MSGPACK_UNPACK_CONTINUE;
+    }
+    else {
+        int e;
+        template_context ctx;
+        template_init(&ctx);
+
+        ctx.user.z = &result_zone;
+        ctx.user.referenced = false;
+
+        e = template_execute(&ctx, data, len, &noff);
+        if(e < 0) {
+            return (msgpack_unpack_return)e;
+        }
+
+        if(off != NULL) { *off = noff; }
+
+        if(e == 0) {
+            return MSGPACK_UNPACK_CONTINUE;
+        }
+
+        *result = template_data(&ctx);
+
+        if(noff < len) {
+            return MSGPACK_UNPACK_EXTRA_BYTES;
+        }
+
+        return MSGPACK_UNPACK_SUCCESS;
+    }
+}
+
+msgpack_unpack_return
+msgpack_unpack_next(msgpack_unpacked* result,
+        const char* data, size_t len, size_t* off)
+{
+    size_t noff = 0;
+    msgpack_unpacked_destroy(result);
+
+    if(off != NULL) { noff = *off; }
+
+    if(len <= noff) {
+        return MSGPACK_UNPACK_CONTINUE;
+    }
+
+    {
+        int e;
+        template_context ctx;
+        template_init(&ctx);
+
+        ctx.user.z = &result->zone;
+        ctx.user.referenced = false;
+
+        e = template_execute(&ctx, data, len, &noff);
+
+        if(off != NULL) { *off = noff; }
+
+        if(e < 0) {
+            msgpack_zone_free(result->zone);
+            result->zone = NULL;
+            return (msgpack_unpack_return)e;
+        }
+
+        if(e == 0) {
+            return MSGPACK_UNPACK_CONTINUE;
+        }
+
+        result->data = template_data(&ctx);
+
+        return MSGPACK_UNPACK_SUCCESS;
+    }
+}
+
+#if defined(MSGPACK_OLD_COMPILER_BUS_ERROR_WORKAROUND)
+// FIXME: Dirty hack to avoid a bus error caused by OS X's old gcc.
+static void dummy_function_to_avoid_bus_error()
+{
+}
+#endif
diff --git a/src/vendor/msgpack/unpack.h b/src/vendor/msgpack/unpack.h
new file mode 100644 (file)
index 0000000..036d575
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * MessagePack for C unpacking routine
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_UNPACKER_H
+#define MSGPACK_UNPACKER_H
+
+#include "zone.h"
+#include "object.h"
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @defgroup msgpack_unpack Deserializer
+ * @ingroup msgpack
+ * @{
+ */
+
+typedef struct msgpack_unpacked {
+    msgpack_zone* zone;
+    msgpack_object data;
+} msgpack_unpacked;
+
+typedef enum {
+    MSGPACK_UNPACK_SUCCESS              =  2,
+    MSGPACK_UNPACK_EXTRA_BYTES          =  1,
+    MSGPACK_UNPACK_CONTINUE             =  0,
+    MSGPACK_UNPACK_PARSE_ERROR          = -1,
+    MSGPACK_UNPACK_NOMEM_ERROR          = -2
+} msgpack_unpack_return;
+
+
+MSGPACK_DLLEXPORT
+msgpack_unpack_return
+msgpack_unpack_next(msgpack_unpacked* result,
+        const char* data, size_t len, size_t* off);
+
+/** @} */
+
+
+/**
+ * @defgroup msgpack_unpacker Streaming deserializer
+ * @ingroup msgpack
+ * @{
+ */
+
+typedef struct msgpack_unpacker {
+    char* buffer;
+    size_t used;
+    size_t free;
+    size_t off;
+    size_t parsed;
+    msgpack_zone* z;
+    size_t initial_buffer_size;
+    void* ctx;
+} msgpack_unpacker;
+
+
+#ifndef MSGPACK_UNPACKER_INIT_BUFFER_SIZE
+#define MSGPACK_UNPACKER_INIT_BUFFER_SIZE (64*1024)
+#endif
+
+/**
+ * Initializes a streaming deserializer.
+ * The initialized deserializer must be destroyed by msgpack_unpacker_destroy(msgpack_unpacker*).
+ */
+MSGPACK_DLLEXPORT
+bool msgpack_unpacker_init(msgpack_unpacker* mpac, size_t initial_buffer_size);
+
+/**
+ * Destroys a streaming deserializer initialized by msgpack_unpacker_init(msgpack_unpacker*, size_t).
+ */
+MSGPACK_DLLEXPORT
+void msgpack_unpacker_destroy(msgpack_unpacker* mpac);
+
+
+/**
+ * Creates a streaming deserializer.
+ * The created deserializer must be destroyed by msgpack_unpacker_free(msgpack_unpacker*).
+ */
+MSGPACK_DLLEXPORT
+msgpack_unpacker* msgpack_unpacker_new(size_t initial_buffer_size);
+
+/**
+ * Frees a streaming deserializer created by msgpack_unpacker_new(size_t).
+ */
+MSGPACK_DLLEXPORT
+void msgpack_unpacker_free(msgpack_unpacker* mpac);
+
+
+#ifndef MSGPACK_UNPACKER_RESERVE_SIZE
+#define MSGPACK_UNPACKER_RESERVE_SIZE (32*1024)
+#endif
+
+/**
+ * Reserves free space of the internal buffer.
+ * Use this function to fill the internal buffer with
+ * msgpack_unpacker_buffer(msgpack_unpacker*),
+ * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*) and
+ * msgpack_unpacker_buffer_consumed(msgpack_unpacker*).
+ */
+static inline bool   msgpack_unpacker_reserve_buffer(msgpack_unpacker* mpac, size_t size);
+
+/**
+ * Gets pointer to the free space of the internal buffer.
+ * Use this function to fill the internal buffer with
+ * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t),
+ * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*) and
+ * msgpack_unpacker_buffer_consumed(msgpack_unpacker*).
+ */
+static inline char*  msgpack_unpacker_buffer(msgpack_unpacker* mpac);
+
+/**
+ * Gets size of the free space of the internal buffer.
+ * Use this function to fill the internal buffer with
+ * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t),
+ * msgpack_unpacker_buffer(const msgpack_unpacker*) and
+ * msgpack_unpacker_buffer_consumed(msgpack_unpacker*).
+ */
+static inline size_t msgpack_unpacker_buffer_capacity(const msgpack_unpacker* mpac);
+
+/**
+ * Notifies the deserializer that the internal buffer filled.
+ * Use this function to fill the internal buffer with
+ * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t),
+ * msgpack_unpacker_buffer(msgpack_unpacker*) and
+ * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*).
+ */
+static inline void   msgpack_unpacker_buffer_consumed(msgpack_unpacker* mpac, size_t size);
+
+
+/**
+ * Deserializes one object.
+ * Returns true if it successes. Otherwise false is returned.
+ * @param pac  pointer to an initialized msgpack_unpacked object.
+ */
+MSGPACK_DLLEXPORT
+msgpack_unpack_return msgpack_unpacker_next(msgpack_unpacker* mpac, msgpack_unpacked* pac);
+
+/**
+ * Deserializes one object and set the number of parsed bytes involved.
+ * Returns true if it successes. Otherwise false is returned.
+ * @param mpac    pointer to an initialized msgpack_unpacker object.
+ * @param result  pointer to an initialized msgpack_unpacked object.
+ * @param p_bytes pointer to variable that will be set with the number of parsed bytes.
+ */
+MSGPACK_DLLEXPORT
+msgpack_unpack_return msgpack_unpacker_next_with_size(msgpack_unpacker* mpac,
+                                                      msgpack_unpacked* result,
+                                                      size_t *p_bytes);
+
+/**
+ * Initializes a msgpack_unpacked object.
+ * The initialized object must be destroyed by msgpack_unpacked_destroy(msgpack_unpacker*).
+ * Use the object with msgpack_unpacker_next(msgpack_unpacker*, msgpack_unpacked*) or
+ * msgpack_unpack_next(msgpack_unpacked*, const char*, size_t, size_t*).
+ */
+static inline void msgpack_unpacked_init(msgpack_unpacked* result);
+
+/**
+ * Destroys a streaming deserializer initialized by msgpack_unpacked().
+ */
+static inline void msgpack_unpacked_destroy(msgpack_unpacked* result);
+
+/**
+ * Releases the memory zone from msgpack_unpacked object.
+ * The released zone must be freed by msgpack_zone_free(msgpack_zone*).
+ */
+static inline msgpack_zone* msgpack_unpacked_release_zone(msgpack_unpacked* result);
+
+
+MSGPACK_DLLEXPORT
+int msgpack_unpacker_execute(msgpack_unpacker* mpac);
+
+MSGPACK_DLLEXPORT
+msgpack_object msgpack_unpacker_data(msgpack_unpacker* mpac);
+
+MSGPACK_DLLEXPORT
+msgpack_zone* msgpack_unpacker_release_zone(msgpack_unpacker* mpac);
+
+MSGPACK_DLLEXPORT
+void msgpack_unpacker_reset_zone(msgpack_unpacker* mpac);
+
+MSGPACK_DLLEXPORT
+void msgpack_unpacker_reset(msgpack_unpacker* mpac);
+
+static inline size_t msgpack_unpacker_message_size(const msgpack_unpacker* mpac);
+
+
+/** @} */
+
+
+// obsolete
+MSGPACK_DLLEXPORT
+msgpack_unpack_return
+msgpack_unpack(const char* data, size_t len, size_t* off,
+        msgpack_zone* result_zone, msgpack_object* result);
+
+
+
+
+static inline size_t msgpack_unpacker_parsed_size(const msgpack_unpacker* mpac);
+
+MSGPACK_DLLEXPORT
+bool msgpack_unpacker_flush_zone(msgpack_unpacker* mpac);
+
+MSGPACK_DLLEXPORT
+bool msgpack_unpacker_expand_buffer(msgpack_unpacker* mpac, size_t size);
+
+static inline bool msgpack_unpacker_reserve_buffer(msgpack_unpacker* mpac, size_t size)
+{
+    if(mpac->free >= size) { return true; }
+    return msgpack_unpacker_expand_buffer(mpac, size);
+}
+
+static inline char* msgpack_unpacker_buffer(msgpack_unpacker* mpac)
+{
+    return mpac->buffer + mpac->used;
+}
+
+static inline size_t msgpack_unpacker_buffer_capacity(const msgpack_unpacker* mpac)
+{
+    return mpac->free;
+}
+
+static inline void msgpack_unpacker_buffer_consumed(msgpack_unpacker* mpac, size_t size)
+{
+    mpac->used += size;
+    mpac->free -= size;
+}
+
+static inline size_t msgpack_unpacker_message_size(const msgpack_unpacker* mpac)
+{
+    return mpac->parsed - mpac->off + mpac->used;
+}
+
+static inline size_t msgpack_unpacker_parsed_size(const msgpack_unpacker* mpac)
+{
+    return mpac->parsed;
+}
+
+
+static inline void msgpack_unpacked_init(msgpack_unpacked* result)
+{
+    memset(result, 0, sizeof(msgpack_unpacked));
+}
+
+static inline void msgpack_unpacked_destroy(msgpack_unpacked* result)
+{
+    if(result->zone != NULL) {
+        msgpack_zone_free(result->zone);
+        result->zone = NULL;
+        memset(&result->data, 0, sizeof(msgpack_object));
+    }
+}
+
+static inline msgpack_zone* msgpack_unpacked_release_zone(msgpack_unpacked* result)
+{
+    if(result->zone != NULL) {
+        msgpack_zone* z = result->zone;
+        result->zone = NULL;
+        return z;
+    }
+    return NULL;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/unpack.h */
diff --git a/src/vendor/msgpack/unpack_define.h b/src/vendor/msgpack/unpack_define.h
new file mode 100644 (file)
index 0000000..1948339
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * MessagePack unpacking routine template
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_UNPACK_DEFINE_H
+#define MSGPACK_UNPACK_DEFINE_H
+
+#include "vendor/msgpack/sysdep.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef MSGPACK_EMBED_STACK_SIZE
+#define MSGPACK_EMBED_STACK_SIZE 32
+#endif
+
+
+typedef enum {
+    MSGPACK_CS_HEADER            = 0x00,  // nil
+
+    //MSGPACK_CS_                = 0x01,
+    //MSGPACK_CS_                = 0x02,  // false
+    //MSGPACK_CS_                = 0x03,  // true
+
+    MSGPACK_CS_BIN_8             = 0x04,
+    MSGPACK_CS_BIN_16            = 0x05,
+    MSGPACK_CS_BIN_32            = 0x06,
+
+    MSGPACK_CS_EXT_8             = 0x07,
+    MSGPACK_CS_EXT_16            = 0x08,
+    MSGPACK_CS_EXT_32            = 0x09,
+
+    MSGPACK_CS_FLOAT             = 0x0a,
+    MSGPACK_CS_DOUBLE            = 0x0b,
+    MSGPACK_CS_UINT_8            = 0x0c,
+    MSGPACK_CS_UINT_16           = 0x0d,
+    MSGPACK_CS_UINT_32           = 0x0e,
+    MSGPACK_CS_UINT_64           = 0x0f,
+    MSGPACK_CS_INT_8             = 0x10,
+    MSGPACK_CS_INT_16            = 0x11,
+    MSGPACK_CS_INT_32            = 0x12,
+    MSGPACK_CS_INT_64            = 0x13,
+
+    MSGPACK_CS_FIXEXT_1          = 0x14,
+    MSGPACK_CS_FIXEXT_2          = 0x15,
+    MSGPACK_CS_FIXEXT_4          = 0x16,
+    MSGPACK_CS_FIXEXT_8          = 0x17,
+    MSGPACK_CS_FIXEXT_16         = 0x18,
+
+    MSGPACK_CS_STR_8             = 0x19, // str8
+    MSGPACK_CS_STR_16            = 0x1a, // str16
+    MSGPACK_CS_STR_32            = 0x1b, // str32
+    MSGPACK_CS_ARRAY_16          = 0x1c,
+    MSGPACK_CS_ARRAY_32          = 0x1d,
+    MSGPACK_CS_MAP_16            = 0x1e,
+    MSGPACK_CS_MAP_32            = 0x1f,
+
+    //MSGPACK_ACS_BIG_INT_VALUE,
+    //MSGPACK_ACS_BIG_FLOAT_VALUE,
+    MSGPACK_ACS_STR_VALUE,
+    MSGPACK_ACS_BIN_VALUE,
+    MSGPACK_ACS_EXT_VALUE
+} msgpack_unpack_state;
+
+
+typedef enum {
+    MSGPACK_CT_ARRAY_ITEM,
+    MSGPACK_CT_MAP_KEY,
+    MSGPACK_CT_MAP_VALUE
+} msgpack_container_type;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/unpack_define.h */
+
diff --git a/src/vendor/msgpack/unpack_template.h b/src/vendor/msgpack/unpack_template.h
new file mode 100644 (file)
index 0000000..de30f3c
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * MessagePack unpacking routine template
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#ifndef msgpack_unpack_func
+#error msgpack_unpack_func template is not defined
+#endif
+
+#ifndef msgpack_unpack_callback
+#error msgpack_unpack_callback template is not defined
+#endif
+
+#ifndef msgpack_unpack_struct
+#error msgpack_unpack_struct template is not defined
+#endif
+
+#ifndef msgpack_unpack_struct_decl
+#define msgpack_unpack_struct_decl(name) msgpack_unpack_struct(name)
+#endif
+
+#ifndef msgpack_unpack_object
+#error msgpack_unpack_object type is not defined
+#endif
+
+#ifndef msgpack_unpack_user
+#error msgpack_unpack_user type is not defined
+#endif
+
+#ifndef USE_CASE_RANGE
+#if !defined(_MSC_VER)
+#define USE_CASE_RANGE
+#endif
+#endif
+
+#if defined(_KERNEL_MODE)
+#undef  assert
+#define assert NT_ASSERT
+#endif
+
+msgpack_unpack_struct_decl(_stack) {
+    msgpack_unpack_object obj;
+    size_t count;
+    unsigned int ct;
+    msgpack_unpack_object map_key;
+};
+
+msgpack_unpack_struct_decl(_context) {
+    msgpack_unpack_user user;
+    unsigned int cs;
+    unsigned int trail;
+    unsigned int top;
+    /*
+    msgpack_unpack_struct(_stack)* stack;
+    unsigned int stack_size;
+    msgpack_unpack_struct(_stack) embed_stack[MSGPACK_EMBED_STACK_SIZE];
+    */
+    msgpack_unpack_struct(_stack) stack[MSGPACK_EMBED_STACK_SIZE];
+};
+
+
+msgpack_unpack_func(void, _init)(msgpack_unpack_struct(_context)* ctx)
+{
+    ctx->cs = MSGPACK_CS_HEADER;
+    ctx->trail = 0;
+    ctx->top = 0;
+    /*
+    ctx->stack = ctx->embed_stack;
+    ctx->stack_size = MSGPACK_EMBED_STACK_SIZE;
+    */
+    ctx->stack[0].obj = msgpack_unpack_callback(_root)(&ctx->user);
+}
+
+/*
+msgpack_unpack_func(void, _destroy)(msgpack_unpack_struct(_context)* ctx)
+{
+    if(ctx->stack_size != MSGPACK_EMBED_STACK_SIZE) {
+        free(ctx->stack);
+    }
+}
+*/
+
+msgpack_unpack_func(msgpack_unpack_object, _data)(msgpack_unpack_struct(_context)* ctx)
+{
+    return (ctx)->stack[0].obj;
+}
+
+
+msgpack_unpack_func(int, _execute)(msgpack_unpack_struct(_context)* ctx, const char* data, size_t len, size_t* off)
+{
+    assert(len >= *off);
+    {
+        const unsigned char* p = (unsigned char*)data + *off;
+        const unsigned char* const pe = (unsigned char*)data + len;
+        const void* n = NULL;
+
+        unsigned int trail = ctx->trail;
+        unsigned int cs = ctx->cs;
+        unsigned int top = ctx->top;
+        msgpack_unpack_struct(_stack)* stack = ctx->stack;
+        /*
+        unsigned int stack_size = ctx->stack_size;
+        */
+        msgpack_unpack_user* user = &ctx->user;
+
+        msgpack_unpack_object obj;
+        msgpack_unpack_struct(_stack)* c = NULL;
+
+        int ret;
+
+#define push_simple_value(func) \
+        ret = msgpack_unpack_callback(func)(user, &obj); \
+        if(ret < 0) { goto _failed; } \
+        goto _push
+#define push_fixed_value(func, arg) \
+        ret = msgpack_unpack_callback(func)(user, arg, &obj); \
+        if(ret < 0) { goto _failed; } \
+        goto _push
+#define push_variable_value(func, base, pos, len) \
+        ret = msgpack_unpack_callback(func)(user, \
+            (const char*)base, (const char*)pos, len, &obj); \
+        if(ret < 0) { goto _failed; } \
+        goto _push
+
+#define again_fixed_trail(_cs, trail_len) \
+        trail = trail_len; \
+        cs = _cs; \
+        goto _fixed_trail_again
+#define again_fixed_trail_if_zero(_cs, trail_len, ifzero) \
+        trail = trail_len; \
+        if(trail == 0) { goto ifzero; } \
+        cs = _cs; \
+        goto _fixed_trail_again
+
+#define start_container(func, count_, ct_) \
+        if(top >= MSGPACK_EMBED_STACK_SIZE) { \
+            ret = MSGPACK_UNPACK_NOMEM_ERROR; \
+            goto _failed; \
+        } /* FIXME */ \
+        ret = msgpack_unpack_callback(func)(user, count_, &stack[top].obj); \
+        if(ret < 0) { goto _failed; } \
+        if((count_) == 0) { obj = stack[top].obj; goto _push; } \
+        stack[top].ct = ct_; \
+        stack[top].count = count_; \
+        ++top; \
+        goto _header_again
+
+#define NEXT_CS(p) \
+        ((unsigned int)*p & 0x1f)
+
+#ifdef USE_CASE_RANGE
+#define SWITCH_RANGE_BEGIN     switch(*p) {
+#define SWITCH_RANGE(FROM, TO) case FROM ... TO:
+#define SWITCH_RANGE_DEFAULT   default:
+#define SWITCH_RANGE_END       }
+#else
+#define SWITCH_RANGE_BEGIN     { if(0) {
+#define SWITCH_RANGE(FROM, TO) } else if(FROM <= *p && *p <= TO) {
+#define SWITCH_RANGE_DEFAULT   } else {
+#define SWITCH_RANGE_END       } }
+#endif
+
+        if(p == pe) { goto _out; }
+        do {
+            switch(cs) {
+            case MSGPACK_CS_HEADER:
+                SWITCH_RANGE_BEGIN
+                SWITCH_RANGE(0x00, 0x7f)  // Positive Fixnum
+                    push_fixed_value(_uint8, *(uint8_t*)p);
+                SWITCH_RANGE(0xe0, 0xff)  // Negative Fixnum
+                    push_fixed_value(_int8, *(int8_t*)p);
+                SWITCH_RANGE(0xc0, 0xdf)  // Variable
+                    switch(*p) {
+                    case 0xc0:  // nil
+                        push_simple_value(_nil);
+                    //case 0xc1:  // string
+                    //  again_terminal_trail(NEXT_CS(p), p+1);
+                    case 0xc2:  // false
+                        push_simple_value(_false);
+                    case 0xc3:  // true
+                        push_simple_value(_true);
+                    case 0xc4: // bin 8
+                    case 0xc5: // bin 16
+                    case 0xc6: // bin 32
+                        again_fixed_trail(NEXT_CS(p), 1 << (((unsigned int)*p) & 0x03));
+                    case 0xc7: // ext 8
+                    case 0xc8: // ext 16
+                    case 0xc9: // ext 32
+                        again_fixed_trail(NEXT_CS(p), 1 << ((((unsigned int)*p) + 1) & 0x03));
+                    case 0xca:  // float
+                    case 0xcb:  // double
+                    case 0xcc:  // unsigned int  8
+                    case 0xcd:  // unsigned int 16
+                    case 0xce:  // unsigned int 32
+                    case 0xcf:  // unsigned int 64
+                    case 0xd0:  // signed int  8
+                    case 0xd1:  // signed int 16
+                    case 0xd2:  // signed int 32
+                    case 0xd3:  // signed int 64
+                        again_fixed_trail(NEXT_CS(p), 1 << (((unsigned int)*p) & 0x03));
+                    case 0xd4:  // fixext 1
+                    case 0xd5:  // fixext 2
+                    case 0xd6:  // fixext 4
+                    case 0xd7:  // fixext 8
+                        again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE,
+                            (1 << (((unsigned int)*p) & 0x03)) + 1, _ext_zero);
+                    case 0xd8:  // fixext 16
+                        again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 16+1, _ext_zero);
+
+                    case 0xd9:  // str 8
+                    case 0xda:  // str 16
+                    case 0xdb:  // str 32
+                        again_fixed_trail(NEXT_CS(p), 1 << ((((unsigned int)*p) & 0x03) - 1));
+                    case 0xdc:  // array 16
+                    case 0xdd:  // array 32
+                    case 0xde:  // map 16
+                    case 0xdf:  // map 32
+                        again_fixed_trail(NEXT_CS(p), 2u << (((unsigned int)*p) & 0x01));
+                    default:
+                        ret = MSGPACK_UNPACK_PARSE_ERROR;
+                        goto _failed;
+                    }
+                SWITCH_RANGE(0xa0, 0xbf)  // FixStr
+                    again_fixed_trail_if_zero(MSGPACK_ACS_STR_VALUE, ((unsigned int)*p & 0x1f), _str_zero);
+                SWITCH_RANGE(0x90, 0x9f)  // FixArray
+                    start_container(_array, ((unsigned int)*p) & 0x0f, MSGPACK_CT_ARRAY_ITEM);
+                SWITCH_RANGE(0x80, 0x8f)  // FixMap
+                    start_container(_map, ((unsigned int)*p) & 0x0f, MSGPACK_CT_MAP_KEY);
+
+                SWITCH_RANGE_DEFAULT
+                    ret = MSGPACK_UNPACK_PARSE_ERROR;
+                    goto _failed;
+                SWITCH_RANGE_END
+                // end MSGPACK_CS_HEADER
+
+
+            _fixed_trail_again:
+                ++p;
+                // fallthrough
+
+            default:
+                if((size_t)(pe - p) < trail) { goto _out; }
+                n = p;  p += trail - 1;
+                switch(cs) {
+                //case MSGPACK_CS_
+                //case MSGPACK_CS_
+                case MSGPACK_CS_FLOAT: {
+                        union { uint32_t i; float f; } mem;
+                        _msgpack_load32(uint32_t, n, &mem.i);
+                        push_fixed_value(_float, mem.f); }
+                case MSGPACK_CS_DOUBLE: {
+                        union { uint64_t i; double f; } mem;
+                        _msgpack_load64(uint64_t, n, &mem.i);
+#if defined(TARGET_OS_IPHONE)
+                    // ok
+#elif defined(__arm__) && !(__ARM_EABI__) // arm-oabi
+                        // https://github.com/msgpack/msgpack-perl/pull/1
+                        mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL);
+#endif
+                        push_fixed_value(_double, mem.f); }
+                case MSGPACK_CS_UINT_8:
+                    push_fixed_value(_uint8, *(uint8_t*)n);
+                case MSGPACK_CS_UINT_16:{
+                    uint16_t tmp;
+                    _msgpack_load16(uint16_t,n,&tmp);
+                    push_fixed_value(_uint16, tmp);
+                }
+                case MSGPACK_CS_UINT_32:{
+                    uint32_t tmp;
+                    _msgpack_load32(uint32_t,n,&tmp);
+                    push_fixed_value(_uint32, tmp);
+                }
+                case MSGPACK_CS_UINT_64:{
+                    uint64_t tmp;
+                    _msgpack_load64(uint64_t,n,&tmp);
+                    push_fixed_value(_uint64, tmp);
+                }
+                case MSGPACK_CS_INT_8:
+                    push_fixed_value(_int8, *(int8_t*)n);
+                case MSGPACK_CS_INT_16:{
+                    int16_t tmp;
+                    _msgpack_load16(int16_t,n,&tmp);
+                    push_fixed_value(_int16, tmp);
+                }
+                case MSGPACK_CS_INT_32:{
+                    int32_t tmp;
+                    _msgpack_load32(int32_t,n,&tmp);
+                    push_fixed_value(_int32, tmp);
+                }
+                case MSGPACK_CS_INT_64:{
+                    int64_t tmp;
+                    _msgpack_load64(int64_t,n,&tmp);
+                    push_fixed_value(_int64, tmp);
+                }
+                case MSGPACK_CS_FIXEXT_1:
+                    again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 1+1, _ext_zero);
+                case MSGPACK_CS_FIXEXT_2:
+                    again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 2+1, _ext_zero);
+                case MSGPACK_CS_FIXEXT_4:
+                    again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 4+1, _ext_zero);
+                case MSGPACK_CS_FIXEXT_8:
+                    again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 8+1, _ext_zero);
+                case MSGPACK_CS_FIXEXT_16:
+                    again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, 16+1, _ext_zero);
+                case MSGPACK_CS_STR_8:
+                    again_fixed_trail_if_zero(MSGPACK_ACS_STR_VALUE, *(uint8_t*)n, _str_zero);
+                case MSGPACK_CS_BIN_8:
+                    again_fixed_trail_if_zero(MSGPACK_ACS_BIN_VALUE, *(uint8_t*)n, _bin_zero);
+                case MSGPACK_CS_EXT_8:
+                    again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, (*(uint8_t*)n) + 1, _ext_zero);
+                case MSGPACK_CS_STR_16:{
+                    uint16_t tmp;
+                    _msgpack_load16(uint16_t,n,&tmp);
+                    again_fixed_trail_if_zero(MSGPACK_ACS_STR_VALUE, tmp, _str_zero);
+                }
+                case MSGPACK_CS_BIN_16:{
+                    uint16_t tmp;
+                    _msgpack_load16(uint16_t,n,&tmp);
+                    again_fixed_trail_if_zero(MSGPACK_ACS_BIN_VALUE, tmp, _bin_zero);
+                }
+                case MSGPACK_CS_EXT_16:{
+                    uint16_t tmp;
+                    _msgpack_load16(uint16_t,n,&tmp);
+                    again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, tmp + 1, _ext_zero);
+                }
+                case MSGPACK_CS_STR_32:{
+                    uint32_t tmp;
+                    _msgpack_load32(uint32_t,n,&tmp);
+                    again_fixed_trail_if_zero(MSGPACK_ACS_STR_VALUE, tmp, _str_zero);
+                }
+                case MSGPACK_CS_BIN_32:{
+                    uint32_t tmp;
+                    _msgpack_load32(uint32_t,n,&tmp);
+                    again_fixed_trail_if_zero(MSGPACK_ACS_BIN_VALUE, tmp, _bin_zero);
+                }
+                case MSGPACK_CS_EXT_32:{
+                    uint32_t tmp;
+                    _msgpack_load32(uint32_t,n,&tmp);
+                    again_fixed_trail_if_zero(MSGPACK_ACS_EXT_VALUE, tmp + 1, _ext_zero);
+                }
+                case MSGPACK_ACS_STR_VALUE:
+                _str_zero:
+                    push_variable_value(_str, data, n, trail);
+                case MSGPACK_ACS_BIN_VALUE:
+                _bin_zero:
+                    push_variable_value(_bin, data, n, trail);
+                case MSGPACK_ACS_EXT_VALUE:
+                _ext_zero:
+                    push_variable_value(_ext, data, n, trail);
+
+                case MSGPACK_CS_ARRAY_16:{
+                    uint16_t tmp;
+                    _msgpack_load16(uint16_t,n,&tmp);
+                    start_container(_array, tmp, MSGPACK_CT_ARRAY_ITEM);
+                }
+                case MSGPACK_CS_ARRAY_32:{
+                    /* FIXME security guard */
+                    uint32_t tmp;
+                    _msgpack_load32(uint32_t,n,&tmp);
+                    start_container(_array, tmp, MSGPACK_CT_ARRAY_ITEM);
+                }
+
+                case MSGPACK_CS_MAP_16:{
+                    uint16_t tmp;
+                    _msgpack_load16(uint16_t,n,&tmp);
+                    start_container(_map, tmp, MSGPACK_CT_MAP_KEY);
+                }
+                case MSGPACK_CS_MAP_32:{
+                    /* FIXME security guard */
+                    uint32_t tmp;
+                    _msgpack_load32(uint32_t,n,&tmp);
+                    start_container(_map, tmp, MSGPACK_CT_MAP_KEY);
+                }
+
+                default:
+                    ret = MSGPACK_UNPACK_PARSE_ERROR;
+                    goto _failed;
+                }
+            }
+
+    _push:
+        if(top == 0) { goto _finish; }
+        c = &stack[top-1];
+        switch(c->ct) {
+        case MSGPACK_CT_ARRAY_ITEM:
+            ret = msgpack_unpack_callback(_array_item)(user, &c->obj, obj); \
+            if(ret < 0) { goto _failed; }
+            if(--c->count == 0) {
+                obj = c->obj;
+                --top;
+                /*printf("stack pop %d\n", top);*/
+                goto _push;
+            }
+            goto _header_again;
+        case MSGPACK_CT_MAP_KEY:
+            c->map_key = obj;
+            c->ct = MSGPACK_CT_MAP_VALUE;
+            goto _header_again;
+        case MSGPACK_CT_MAP_VALUE:
+            ret = msgpack_unpack_callback(_map_item)(user, &c->obj, c->map_key, obj); \
+            if(ret < 0) { goto _failed; }
+            if(--c->count == 0) {
+                obj = c->obj;
+                --top;
+                /*printf("stack pop %d\n", top);*/
+                goto _push;
+            }
+            c->ct = MSGPACK_CT_MAP_KEY;
+            goto _header_again;
+
+        default:
+            ret = MSGPACK_UNPACK_PARSE_ERROR;
+            goto _failed;
+        }
+
+    _header_again:
+            cs = MSGPACK_CS_HEADER;
+            ++p;
+        } while(p != pe);
+        goto _out;
+
+
+    _finish:
+        stack[0].obj = obj;
+        ++p;
+        ret = 1;
+        /*printf("-- finish --\n"); */
+        goto _end;
+
+    _failed:
+        /*printf("** FAILED **\n"); */
+        goto _end;
+
+    _out:
+        ret = 0;
+        goto _end;
+
+    _end:
+        ctx->cs = cs;
+        ctx->trail = trail;
+        ctx->top = top;
+        *off = (size_t)(p - (const unsigned char*)data);
+
+        return ret;
+    }
+}
+
+#undef msgpack_unpack_func
+#undef msgpack_unpack_callback
+#undef msgpack_unpack_struct
+#undef msgpack_unpack_object
+#undef msgpack_unpack_user
+
+#undef push_simple_value
+#undef push_fixed_value
+#undef push_variable_value
+#undef again_fixed_trail
+#undef again_fixed_trail_if_zero
+#undef start_container
+
+#undef NEXT_CS
+
+#undef SWITCH_RANGE_BEGIN
+#undef SWITCH_RANGE
+#undef SWITCH_RANGE_DEFAULT
+#undef SWITCH_RANGE_END
diff --git a/src/vendor/msgpack/util.h b/src/vendor/msgpack/util.h
new file mode 100644 (file)
index 0000000..959b56b
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * MessagePack for C utilities
+ *
+ * Copyright (C) 2014 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_UTIL_H
+#define MSGPACK_UTIL_H
+
+#define MSGPACK_UNUSED(a) (void)(a)
+
+#endif /* MSGPACK_UTIL_H */
diff --git a/src/vendor/msgpack/version.c b/src/vendor/msgpack/version.c
new file mode 100644 (file)
index 0000000..50016f6
--- /dev/null
@@ -0,0 +1,22 @@
+#include "vendor/msgpack/msgpack.h"
+
+const char* msgpack_version(void)
+{
+    return MSGPACK_VERSION;
+}
+
+int msgpack_version_major(void)
+{
+    return MSGPACK_VERSION_MAJOR;
+}
+
+int msgpack_version_minor(void)
+{
+    return MSGPACK_VERSION_MINOR;
+}
+
+int msgpack_version_revision(void)
+{
+    return MSGPACK_VERSION_REVISION;
+}
+
diff --git a/src/vendor/msgpack/version.h b/src/vendor/msgpack/version.h
new file mode 100644 (file)
index 0000000..bd6605b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * MessagePack for C version information
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_VERSION_H
+#define MSGPACK_VERSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MSGPACK_DLLEXPORT
+const char* msgpack_version(void);
+MSGPACK_DLLEXPORT
+int msgpack_version_major(void);
+MSGPACK_DLLEXPORT
+int msgpack_version_minor(void);
+MSGPACK_DLLEXPORT
+int msgpack_version_revision(void);
+
+#include "version_master.h"
+
+#define MSGPACK_STR(v) #v
+#define MSGPACK_VERSION_I(maj, min, rev) MSGPACK_STR(maj) "." MSGPACK_STR(min) "." MSGPACK_STR(rev)
+
+#define MSGPACK_VERSION MSGPACK_VERSION_I(MSGPACK_VERSION_MAJOR, MSGPACK_VERSION_MINOR, MSGPACK_VERSION_REVISION)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/version.h */
+
diff --git a/src/vendor/msgpack/version_master.h b/src/vendor/msgpack/version_master.h
new file mode 100644 (file)
index 0000000..9db6023
--- /dev/null
@@ -0,0 +1,3 @@
+#define MSGPACK_VERSION_MAJOR    3
+#define MSGPACK_VERSION_MINOR    3
+#define MSGPACK_VERSION_REVISION 0
diff --git a/src/vendor/msgpack/vrefbuffer.c b/src/vendor/msgpack/vrefbuffer.c
new file mode 100644 (file)
index 0000000..9c7b3b5
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * MessagePack for C zero-copy buffer implementation
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#include "vendor/msgpack/vrefbuffer.h"
+#include <stdlib.h>
+#include <string.h>
+
+#define MSGPACK_PACKER_MAX_BUFFER_SIZE 9
+
+struct msgpack_vrefbuffer_chunk {
+    struct msgpack_vrefbuffer_chunk* next;
+    /* data ... */
+};
+
+bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf,
+        size_t ref_size, size_t chunk_size)
+{
+    size_t nfirst;
+    struct iovec* array;
+    msgpack_vrefbuffer_chunk* chunk;
+
+    if (ref_size == 0) {
+        ref_size = MSGPACK_VREFBUFFER_REF_SIZE;
+    }
+    if(chunk_size == 0) {
+        chunk_size = MSGPACK_VREFBUFFER_CHUNK_SIZE;
+    }
+    vbuf->chunk_size = chunk_size;
+    vbuf->ref_size =
+        ref_size > MSGPACK_PACKER_MAX_BUFFER_SIZE + 1 ?
+        ref_size : MSGPACK_PACKER_MAX_BUFFER_SIZE + 1 ;
+
+    if((sizeof(msgpack_vrefbuffer_chunk) + chunk_size) < chunk_size) {
+        return false;
+    }
+
+    nfirst = (sizeof(struct iovec) < 72/2) ?
+            72 / sizeof(struct iovec) : 8;
+
+    array = (struct iovec*)malloc(
+            sizeof(struct iovec) * nfirst);
+    if(array == NULL) {
+        return false;
+    }
+
+    vbuf->tail  = array;
+    vbuf->end   = array + nfirst;
+    vbuf->array = array;
+
+    chunk = (msgpack_vrefbuffer_chunk*)malloc(
+            sizeof(msgpack_vrefbuffer_chunk) + chunk_size);
+    if(chunk == NULL) {
+        free(array);
+        return false;
+    }
+    else {
+        msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer;
+
+        ib->free = chunk_size;
+        ib->ptr  = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk);
+        ib->head = chunk;
+        chunk->next = NULL;
+
+        return true;
+    }
+}
+
+void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf)
+{
+    msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head;
+    while(true) {
+        msgpack_vrefbuffer_chunk* n = c->next;
+        free(c);
+        if(n != NULL) {
+            c = n;
+        } else {
+            break;
+        }
+    }
+    free(vbuf->array);
+}
+
+void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vbuf)
+{
+    msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head->next;
+    msgpack_vrefbuffer_chunk* n;
+    while(c != NULL) {
+        n = c->next;
+        free(c);
+        c = n;
+    }
+
+    {
+        msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer;
+        msgpack_vrefbuffer_chunk* chunk = ib->head;
+        chunk->next = NULL;
+        ib->free = vbuf->chunk_size;
+        ib->ptr  = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk);
+
+        vbuf->tail = vbuf->array;
+    }
+}
+
+int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf,
+        const char* buf, size_t len)
+{
+    if(vbuf->tail == vbuf->end) {
+        const size_t nused = (size_t)(vbuf->tail - vbuf->array);
+        const size_t nnext = nused * 2;
+
+        struct iovec* nvec = (struct iovec*)realloc(
+                vbuf->array, sizeof(struct iovec)*nnext);
+        if(nvec == NULL) {
+            return -1;
+        }
+
+        vbuf->array = nvec;
+        vbuf->end   = nvec + nnext;
+        vbuf->tail  = nvec + nused;
+    }
+
+    vbuf->tail->iov_base = (char*)buf;
+    vbuf->tail->iov_len  = len;
+    ++vbuf->tail;
+
+    return 0;
+}
+
+int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf,
+        const char* buf, size_t len)
+{
+    msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer;
+    char* m;
+
+    if(ib->free < len) {
+        msgpack_vrefbuffer_chunk* chunk;
+        size_t sz = vbuf->chunk_size;
+        if(sz < len) {
+            sz = len;
+        }
+
+        if((sizeof(msgpack_vrefbuffer_chunk) + sz) < sz){
+            return -1;
+        }
+        chunk = (msgpack_vrefbuffer_chunk*)malloc(
+                sizeof(msgpack_vrefbuffer_chunk) + sz);
+        if(chunk == NULL) {
+            return -1;
+        }
+
+        chunk->next = ib->head;
+        ib->head = chunk;
+        ib->free = sz;
+        ib->ptr  = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk);
+    }
+
+    m = ib->ptr;
+    memcpy(m, buf, len);
+    ib->free -= len;
+    ib->ptr  += len;
+
+    if(vbuf->tail != vbuf->array && m ==
+            (const char*)((vbuf->tail-1)->iov_base) + (vbuf->tail-1)->iov_len) {
+        (vbuf->tail-1)->iov_len += len;
+        return 0;
+    } else {
+        return msgpack_vrefbuffer_append_ref(vbuf, m, len);
+    }
+}
+
+int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to)
+{
+    size_t sz = vbuf->chunk_size;
+    msgpack_vrefbuffer_chunk* empty;
+
+    if((sizeof(msgpack_vrefbuffer_chunk) + sz) < sz){
+        return -1;
+    }
+
+    empty = (msgpack_vrefbuffer_chunk*)malloc(
+            sizeof(msgpack_vrefbuffer_chunk) + sz);
+    if(empty == NULL) {
+        return -1;
+    }
+
+    empty->next = NULL;
+
+    {
+        const size_t nused = (size_t)(vbuf->tail - vbuf->array);
+        if(to->tail + nused < vbuf->end) {
+            struct iovec* nvec;
+            const size_t tosize = (size_t)(to->tail - to->array);
+            const size_t reqsize = nused + tosize;
+            size_t nnext = (size_t)(to->end - to->array) * 2;
+            while(nnext < reqsize) {
+                size_t tmp_nnext = nnext * 2;
+                if (tmp_nnext <= nnext) {
+                    nnext = reqsize;
+                    break;
+                }
+                nnext = tmp_nnext;
+            }
+
+            nvec = (struct iovec*)realloc(
+                    to->array, sizeof(struct iovec)*nnext);
+            if(nvec == NULL) {
+                free(empty);
+                return -1;
+            }
+
+            to->array = nvec;
+            to->end   = nvec + nnext;
+            to->tail  = nvec + tosize;
+        }
+
+        memcpy(to->tail, vbuf->array, sizeof(struct iovec)*nused);
+
+        to->tail += nused;
+        vbuf->tail = vbuf->array;
+
+        {
+            msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer;
+            msgpack_vrefbuffer_inner_buffer* const toib = &to->inner_buffer;
+
+            msgpack_vrefbuffer_chunk* last = ib->head;
+            while(last->next != NULL) {
+                last = last->next;
+            }
+            last->next = toib->head;
+            toib->head = ib->head;
+
+            if(toib->free < ib->free) {
+                toib->free = ib->free;
+                toib->ptr  = ib->ptr;
+            }
+
+            ib->head = empty;
+            ib->free = sz;
+            ib->ptr  = ((char*)empty) + sizeof(msgpack_vrefbuffer_chunk);
+        }
+    }
+
+    return 0;
+}
diff --git a/src/vendor/msgpack/vrefbuffer.h b/src/vendor/msgpack/vrefbuffer.h
new file mode 100644 (file)
index 0000000..ab51aef
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * MessagePack for C zero-copy buffer implementation
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_VREFBUFFER_H
+#define MSGPACK_VREFBUFFER_H
+
+#include "zone.h"
+#include <stdlib.h>
+
+#if defined(unix) || defined(__unix) || defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__QNX__) || defined(__QNXTO__) || defined(__HAIKU__)
+#include <sys/uio.h>
+#else
+struct iovec {
+    void  *iov_base;
+    size_t iov_len;
+};
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @defgroup msgpack_vrefbuffer Vectored Referencing buffer
+ * @ingroup msgpack_buffer
+ * @{
+ */
+
+struct msgpack_vrefbuffer_chunk;
+typedef struct msgpack_vrefbuffer_chunk msgpack_vrefbuffer_chunk;
+
+typedef struct msgpack_vrefbuffer_inner_buffer {
+    size_t free;
+    char*  ptr;
+    msgpack_vrefbuffer_chunk* head;
+} msgpack_vrefbuffer_inner_buffer;
+
+typedef struct msgpack_vrefbuffer {
+    struct iovec* tail;
+    struct iovec* end;
+    struct iovec* array;
+
+    size_t chunk_size;
+    size_t ref_size;
+
+    msgpack_vrefbuffer_inner_buffer inner_buffer;
+} msgpack_vrefbuffer;
+
+
+#ifndef MSGPACK_VREFBUFFER_REF_SIZE
+#define MSGPACK_VREFBUFFER_REF_SIZE 32
+#endif
+
+#ifndef MSGPACK_VREFBUFFER_CHUNK_SIZE
+#define MSGPACK_VREFBUFFER_CHUNK_SIZE 8192
+#endif
+
+MSGPACK_DLLEXPORT
+bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf,
+        size_t ref_size, size_t chunk_size);
+MSGPACK_DLLEXPORT
+void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf);
+
+static inline msgpack_vrefbuffer* msgpack_vrefbuffer_new(size_t ref_size, size_t chunk_size);
+static inline void msgpack_vrefbuffer_free(msgpack_vrefbuffer* vbuf);
+
+static inline int msgpack_vrefbuffer_write(void* data, const char* buf, size_t len);
+
+static inline const struct iovec* msgpack_vrefbuffer_vec(const msgpack_vrefbuffer* vref);
+static inline size_t msgpack_vrefbuffer_veclen(const msgpack_vrefbuffer* vref);
+
+MSGPACK_DLLEXPORT
+int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf,
+        const char* buf, size_t len);
+
+MSGPACK_DLLEXPORT
+int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf,
+        const char* buf, size_t len);
+
+MSGPACK_DLLEXPORT
+int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to);
+
+MSGPACK_DLLEXPORT
+void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vref);
+
+/** @} */
+
+
+static inline msgpack_vrefbuffer* msgpack_vrefbuffer_new(size_t ref_size, size_t chunk_size)
+{
+    msgpack_vrefbuffer* vbuf = (msgpack_vrefbuffer*)malloc(sizeof(msgpack_vrefbuffer));
+    if (vbuf == NULL) return NULL;
+    if(!msgpack_vrefbuffer_init(vbuf, ref_size, chunk_size)) {
+        free(vbuf);
+        return NULL;
+    }
+    return vbuf;
+}
+
+static inline void msgpack_vrefbuffer_free(msgpack_vrefbuffer* vbuf)
+{
+    if(vbuf == NULL) { return; }
+    msgpack_vrefbuffer_destroy(vbuf);
+    free(vbuf);
+}
+
+static inline int msgpack_vrefbuffer_write(void* data, const char* buf, size_t len)
+{
+    msgpack_vrefbuffer* vbuf = (msgpack_vrefbuffer*)data;
+
+    if(len < vbuf->ref_size) {
+        return msgpack_vrefbuffer_append_copy(vbuf, buf, len);
+    } else {
+        return msgpack_vrefbuffer_append_ref(vbuf, buf, len);
+    }
+}
+
+static inline const struct iovec* msgpack_vrefbuffer_vec(const msgpack_vrefbuffer* vref)
+{
+    return vref->array;
+}
+
+static inline size_t msgpack_vrefbuffer_veclen(const msgpack_vrefbuffer* vref)
+{
+    return (size_t)(vref->tail - vref->array);
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/vrefbuffer.h */
diff --git a/src/vendor/msgpack/zbuffer.h b/src/vendor/msgpack/zbuffer.h
new file mode 100644 (file)
index 0000000..524906f
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * MessagePack for C deflate buffer implementation
+ *
+ * Copyright (C) 2010 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_ZBUFFER_H
+#define MSGPACK_ZBUFFER_H
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @defgroup msgpack_zbuffer Compressed buffer
+ * @ingroup msgpack_buffer
+ * @{
+ */
+
+typedef struct msgpack_zbuffer {
+    z_stream stream;
+    char* data;
+    size_t init_size;
+} msgpack_zbuffer;
+
+#ifndef MSGPACK_ZBUFFER_INIT_SIZE
+#define MSGPACK_ZBUFFER_INIT_SIZE 8192
+#endif
+
+static inline bool msgpack_zbuffer_init(
+    msgpack_zbuffer* zbuf, int level, size_t init_size);
+static inline void msgpack_zbuffer_destroy(msgpack_zbuffer* zbuf);
+
+static inline msgpack_zbuffer* msgpack_zbuffer_new(int level, size_t init_size);
+static inline void msgpack_zbuffer_free(msgpack_zbuffer* zbuf);
+
+static inline char* msgpack_zbuffer_flush(msgpack_zbuffer* zbuf);
+
+static inline const char* msgpack_zbuffer_data(const msgpack_zbuffer* zbuf);
+static inline size_t msgpack_zbuffer_size(const msgpack_zbuffer* zbuf);
+
+static inline bool msgpack_zbuffer_reset(msgpack_zbuffer* zbuf);
+static inline void msgpack_zbuffer_reset_buffer(msgpack_zbuffer* zbuf);
+static inline char* msgpack_zbuffer_release_buffer(msgpack_zbuffer* zbuf);
+
+
+#ifndef MSGPACK_ZBUFFER_RESERVE_SIZE
+#define MSGPACK_ZBUFFER_RESERVE_SIZE 512
+#endif
+
+static inline int msgpack_zbuffer_write(void* data, const char* buf, size_t len);
+
+static inline bool msgpack_zbuffer_expand(msgpack_zbuffer* zbuf);
+
+
+static inline bool msgpack_zbuffer_init(msgpack_zbuffer* zbuf,
+        int level, size_t init_size)
+{
+    memset(zbuf, 0, sizeof(msgpack_zbuffer));
+    zbuf->init_size = init_size;
+    if(deflateInit(&zbuf->stream, level) != Z_OK) {
+        free(zbuf->data);
+        return false;
+    }
+    return true;
+}
+
+static inline void msgpack_zbuffer_destroy(msgpack_zbuffer* zbuf)
+{
+    deflateEnd(&zbuf->stream);
+    free(zbuf->data);
+}
+
+static inline msgpack_zbuffer* msgpack_zbuffer_new(int level, size_t init_size)
+{
+    msgpack_zbuffer* zbuf = (msgpack_zbuffer*)malloc(sizeof(msgpack_zbuffer));
+    if (zbuf == NULL) return NULL;
+    if(!msgpack_zbuffer_init(zbuf, level, init_size)) {
+        free(zbuf);
+        return NULL;
+    }
+    return zbuf;
+}
+
+static inline void msgpack_zbuffer_free(msgpack_zbuffer* zbuf)
+{
+    if(zbuf == NULL) { return; }
+    msgpack_zbuffer_destroy(zbuf);
+    free(zbuf);
+}
+
+static inline bool msgpack_zbuffer_expand(msgpack_zbuffer* zbuf)
+{
+    size_t used = (size_t)((char *)(zbuf->stream.next_out) - zbuf->data);
+    size_t csize = used + zbuf->stream.avail_out;
+
+    size_t nsize = (csize == 0) ? zbuf->init_size : csize * 2;
+
+    char* tmp = (char*)realloc(zbuf->data, nsize);
+    if(tmp == NULL) {
+        return false;
+    }
+
+    zbuf->data = tmp;
+    zbuf->stream.next_out  = (Bytef*)(tmp + used);
+    zbuf->stream.avail_out = (uInt)(nsize - used);
+
+    return true;
+}
+
+static inline int msgpack_zbuffer_write(void* data, const char* buf, size_t len)
+{
+    msgpack_zbuffer* zbuf = (msgpack_zbuffer*)data;
+
+    zbuf->stream.next_in = (Bytef*)buf;
+    zbuf->stream.avail_in = (uInt)len;
+
+    while(zbuf->stream.avail_in > 0) {
+        if(zbuf->stream.avail_out < MSGPACK_ZBUFFER_RESERVE_SIZE) {
+            if(!msgpack_zbuffer_expand(zbuf)) {
+                return -1;
+            }
+        }
+
+        if(deflate(&zbuf->stream, Z_NO_FLUSH) != Z_OK) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static inline char* msgpack_zbuffer_flush(msgpack_zbuffer* zbuf)
+{
+    while(true) {
+        switch(deflate(&zbuf->stream, Z_FINISH)) {
+        case Z_STREAM_END:
+            return zbuf->data;
+        case Z_OK:
+        case Z_BUF_ERROR:
+            if(!msgpack_zbuffer_expand(zbuf)) {
+                return NULL;
+            }
+            break;
+        default:
+            return NULL;
+        }
+    }
+}
+
+static inline const char* msgpack_zbuffer_data(const msgpack_zbuffer* zbuf)
+{
+    return zbuf->data;
+}
+
+static inline size_t msgpack_zbuffer_size(const msgpack_zbuffer* zbuf)
+{
+    return (size_t)((char *)(zbuf->stream.next_out) - zbuf->data);
+}
+
+static inline void msgpack_zbuffer_reset_buffer(msgpack_zbuffer* zbuf)
+{
+    zbuf->stream.avail_out += (uInt)((char*)zbuf->stream.next_out - zbuf->data);
+    zbuf->stream.next_out = (Bytef*)zbuf->data;
+}
+
+static inline bool msgpack_zbuffer_reset(msgpack_zbuffer* zbuf)
+{
+    if(deflateReset(&zbuf->stream) != Z_OK) {
+        return false;
+    }
+    msgpack_zbuffer_reset_buffer(zbuf);
+    return true;
+}
+
+static inline char* msgpack_zbuffer_release_buffer(msgpack_zbuffer* zbuf)
+{
+    char* tmp = zbuf->data;
+    zbuf->data = NULL;
+    zbuf->stream.next_out = NULL;
+    zbuf->stream.avail_out = 0;
+    return tmp;
+}
+
+/** @} */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/zbuffer.h */
diff --git a/src/vendor/msgpack/zone.c b/src/vendor/msgpack/zone.c
new file mode 100644 (file)
index 0000000..d63b9d6
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * MessagePack for C memory pool implementation
+ *
+ * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#include "vendor/msgpack/zone.h"
+#include <stdlib.h>
+#include <string.h>
+
+struct msgpack_zone_chunk {
+    struct msgpack_zone_chunk* next;
+    /* data ... */
+};
+
+static inline bool init_chunk_list(msgpack_zone_chunk_list* cl, size_t chunk_size)
+{
+    msgpack_zone_chunk* chunk = (msgpack_zone_chunk*)malloc(
+            sizeof(msgpack_zone_chunk) + chunk_size);
+    if(chunk == NULL) {
+        return false;
+    }
+
+    cl->head = chunk;
+    cl->free = chunk_size;
+    cl->ptr  = ((char*)chunk) + sizeof(msgpack_zone_chunk);
+    chunk->next = NULL;
+
+    return true;
+}
+
+static inline void destroy_chunk_list(msgpack_zone_chunk_list* cl)
+{
+    msgpack_zone_chunk* c = cl->head;
+    while(true) {
+        msgpack_zone_chunk* n = c->next;
+        free(c);
+        if(n != NULL) {
+            c = n;
+        } else {
+            break;
+        }
+    }
+}
+
+static inline void clear_chunk_list(msgpack_zone_chunk_list* cl, size_t chunk_size)
+{
+    msgpack_zone_chunk* c = cl->head;
+    while(true) {
+        msgpack_zone_chunk* n = c->next;
+        if(n != NULL) {
+            free(c);
+            c = n;
+        } else {
+            cl->head = c;
+            break;
+        }
+    }
+    cl->head->next = NULL;
+    cl->free = chunk_size;
+    cl->ptr  = ((char*)cl->head) + sizeof(msgpack_zone_chunk);
+}
+
+void* msgpack_zone_malloc_expand(msgpack_zone* zone, size_t size)
+{
+    msgpack_zone_chunk_list* const cl = &zone->chunk_list;
+    msgpack_zone_chunk* chunk;
+
+    size_t sz = zone->chunk_size;
+
+    while(sz < size) {
+        size_t tmp_sz = sz * 2;
+        if (tmp_sz <= sz) {
+            sz = size;
+            break;
+        }
+        sz = tmp_sz;
+    }
+
+    chunk = (msgpack_zone_chunk*)malloc(
+            sizeof(msgpack_zone_chunk) + sz);
+    if (chunk == NULL) {
+        return NULL;
+    }
+    else {
+        char* ptr = ((char*)chunk) + sizeof(msgpack_zone_chunk);
+        chunk->next = cl->head;
+        cl->head = chunk;
+        cl->free = sz - size;
+        cl->ptr  = ptr + size;
+
+        return ptr;
+    }
+}
+
+
+static inline void init_finalizer_array(msgpack_zone_finalizer_array* fa)
+{
+    fa->tail  = NULL;
+    fa->end   = NULL;
+    fa->array = NULL;
+}
+
+static inline void call_finalizer_array(msgpack_zone_finalizer_array* fa)
+{
+    msgpack_zone_finalizer* fin = fa->tail;
+    for(; fin != fa->array; --fin) {
+        (*(fin-1)->func)((fin-1)->data);
+    }
+}
+
+static inline void destroy_finalizer_array(msgpack_zone_finalizer_array* fa)
+{
+    call_finalizer_array(fa);
+    free(fa->array);
+}
+
+static inline void clear_finalizer_array(msgpack_zone_finalizer_array* fa)
+{
+    call_finalizer_array(fa);
+    fa->tail = fa->array;
+}
+
+bool msgpack_zone_push_finalizer_expand(msgpack_zone* zone,
+        void (*func)(void* data), void* data)
+{
+    msgpack_zone_finalizer_array* const fa = &zone->finalizer_array;
+    msgpack_zone_finalizer* tmp;
+
+    const size_t nused = (size_t)(fa->end - fa->array);
+
+    size_t nnext;
+    if(nused == 0) {
+        nnext = (sizeof(msgpack_zone_finalizer) < 72/2) ?
+                72 / sizeof(msgpack_zone_finalizer) : 8;
+
+    } else {
+        nnext = nused * 2;
+    }
+
+    tmp = (msgpack_zone_finalizer*)realloc(fa->array,
+                sizeof(msgpack_zone_finalizer) * nnext);
+    if(tmp == NULL) {
+        return false;
+    }
+
+    fa->array  = tmp;
+    fa->end    = tmp + nnext;
+    fa->tail   = tmp + nused;
+
+    fa->tail->func = func;
+    fa->tail->data = data;
+
+    ++fa->tail;
+
+    return true;
+}
+
+
+bool msgpack_zone_is_empty(msgpack_zone* zone)
+{
+    msgpack_zone_chunk_list* const cl = &zone->chunk_list;
+    msgpack_zone_finalizer_array* const fa = &zone->finalizer_array;
+    return cl->free == zone->chunk_size && cl->head->next == NULL &&
+        fa->tail == fa->array;
+}
+
+
+void msgpack_zone_destroy(msgpack_zone* zone)
+{
+    destroy_finalizer_array(&zone->finalizer_array);
+    destroy_chunk_list(&zone->chunk_list);
+}
+
+void msgpack_zone_clear(msgpack_zone* zone)
+{
+    clear_finalizer_array(&zone->finalizer_array);
+    clear_chunk_list(&zone->chunk_list, zone->chunk_size);
+}
+
+bool msgpack_zone_init(msgpack_zone* zone, size_t chunk_size)
+{
+    zone->chunk_size = chunk_size;
+
+    if(!init_chunk_list(&zone->chunk_list, chunk_size)) {
+        return false;
+    }
+
+    init_finalizer_array(&zone->finalizer_array);
+
+    return true;
+}
+
+msgpack_zone* msgpack_zone_new(size_t chunk_size)
+{
+    msgpack_zone* zone = (msgpack_zone*)malloc(
+            sizeof(msgpack_zone));
+    if(zone == NULL) {
+        return NULL;
+    }
+
+    zone->chunk_size = chunk_size;
+
+    if(!init_chunk_list(&zone->chunk_list, chunk_size)) {
+        free(zone);
+        return NULL;
+    }
+
+    init_finalizer_array(&zone->finalizer_array);
+
+    return zone;
+}
+
+void msgpack_zone_free(msgpack_zone* zone)
+{
+    if(zone == NULL) { return; }
+    msgpack_zone_destroy(zone);
+    free(zone);
+}
diff --git a/src/vendor/msgpack/zone.h b/src/vendor/msgpack/zone.h
new file mode 100644 (file)
index 0000000..9005be7
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * MessagePack for C memory pool implementation
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ *    Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *    http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef MSGPACK_ZONE_H
+#define MSGPACK_ZONE_H
+
+#include "sysdep.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @defgroup msgpack_zone Memory zone
+ * @ingroup msgpack
+ * @{
+ */
+
+typedef struct msgpack_zone_finalizer {
+    void (*func)(void* data);
+    void* data;
+} msgpack_zone_finalizer;
+
+typedef struct msgpack_zone_finalizer_array {
+    msgpack_zone_finalizer* tail;
+    msgpack_zone_finalizer* end;
+    msgpack_zone_finalizer* array;
+} msgpack_zone_finalizer_array;
+
+struct msgpack_zone_chunk;
+typedef struct msgpack_zone_chunk msgpack_zone_chunk;
+
+typedef struct msgpack_zone_chunk_list {
+    size_t free;
+    char* ptr;
+    msgpack_zone_chunk* head;
+} msgpack_zone_chunk_list;
+
+typedef struct msgpack_zone {
+    msgpack_zone_chunk_list chunk_list;
+    msgpack_zone_finalizer_array finalizer_array;
+    size_t chunk_size;
+} msgpack_zone;
+
+#ifndef MSGPACK_ZONE_CHUNK_SIZE
+#define MSGPACK_ZONE_CHUNK_SIZE 8192
+#endif
+
+MSGPACK_DLLEXPORT
+bool msgpack_zone_init(msgpack_zone* zone, size_t chunk_size);
+MSGPACK_DLLEXPORT
+void msgpack_zone_destroy(msgpack_zone* zone);
+
+MSGPACK_DLLEXPORT
+msgpack_zone* msgpack_zone_new(size_t chunk_size);
+MSGPACK_DLLEXPORT
+void msgpack_zone_free(msgpack_zone* zone);
+
+static inline void* msgpack_zone_malloc(msgpack_zone* zone, size_t size);
+static inline void* msgpack_zone_malloc_no_align(msgpack_zone* zone, size_t size);
+
+static inline bool msgpack_zone_push_finalizer(msgpack_zone* zone,
+        void (*func)(void* data), void* data);
+
+static inline void msgpack_zone_swap(msgpack_zone* a, msgpack_zone* b);
+
+MSGPACK_DLLEXPORT
+bool msgpack_zone_is_empty(msgpack_zone* zone);
+
+MSGPACK_DLLEXPORT
+void msgpack_zone_clear(msgpack_zone* zone);
+
+/** @} */
+
+
+#ifndef MSGPACK_ZONE_ALIGN
+#define MSGPACK_ZONE_ALIGN sizeof(void*)
+#endif
+
+MSGPACK_DLLEXPORT
+void* msgpack_zone_malloc_expand(msgpack_zone* zone, size_t size);
+
+static inline void* msgpack_zone_malloc_no_align(msgpack_zone* zone, size_t size)
+{
+    char* ptr;
+    msgpack_zone_chunk_list* cl = &zone->chunk_list;
+
+    if(zone->chunk_list.free < size) {
+        return msgpack_zone_malloc_expand(zone, size);
+    }
+
+    ptr = cl->ptr;
+    cl->free -= size;
+    cl->ptr  += size;
+
+    return ptr;
+}
+
+static inline void* msgpack_zone_malloc(msgpack_zone* zone, size_t size)
+{
+    char* aligned =
+        (char*)(
+            (size_t)(
+                zone->chunk_list.ptr + (MSGPACK_ZONE_ALIGN - 1)
+            ) / MSGPACK_ZONE_ALIGN * MSGPACK_ZONE_ALIGN
+        );
+    size_t adjusted_size = size + (size_t)(aligned - zone->chunk_list.ptr);
+    if(zone->chunk_list.free >= adjusted_size) {
+        zone->chunk_list.free -= adjusted_size;
+        zone->chunk_list.ptr  += adjusted_size;
+        return aligned;
+    }
+    {
+        void* ptr = msgpack_zone_malloc_expand(zone, size + (MSGPACK_ZONE_ALIGN - 1));
+        if (ptr) {
+            return (char*)((size_t)(ptr) / MSGPACK_ZONE_ALIGN * MSGPACK_ZONE_ALIGN);
+        }
+    }
+    return NULL;
+}
+
+
+bool msgpack_zone_push_finalizer_expand(msgpack_zone* zone,
+        void (*func)(void* data), void* data);
+
+static inline bool msgpack_zone_push_finalizer(msgpack_zone* zone,
+        void (*func)(void* data), void* data)
+{
+    msgpack_zone_finalizer_array* const fa = &zone->finalizer_array;
+    msgpack_zone_finalizer* fin = fa->tail;
+
+    if(fin == fa->end) {
+        return msgpack_zone_push_finalizer_expand(zone, func, data);
+    }
+
+    fin->func = func;
+    fin->data = data;
+
+    ++fa->tail;
+
+    return true;
+}
+
+static inline void msgpack_zone_swap(msgpack_zone* a, msgpack_zone* b)
+{
+    msgpack_zone tmp = *a;
+    *a = *b;
+    *b = tmp;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* msgpack/zone.h */
index cbac90da792f43a17ad1e8d5ed6fbb9792004ebb..b422480442a189e7983fd7e13cfab7526b48b5da 100644 (file)
@@ -27,8 +27,17 @@ TESTS = tools/filtering/test_invalid_filter \
        tools/crash/test_crash \
        tools/regen-metadata/test_ust \
        tools/regen-statedump/test_ust \
        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_capture \
+       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_capture \
+       tools/notification/test_notification_kernel_instrumentation \
+       tools/notification/test_notification_kernel_syscall \
+       tools/notification/test_notification_trigger_discarded_count \
+       tools/notification/test_notification_kernel_userspace_probe \
        tools/notification/test_notification_multi_app \
        tools/rotation/test_ust \
        tools/rotation/test_kernel \
        tools/notification/test_notification_multi_app \
        tools/rotation/test_ust \
        tools/rotation/test_kernel \
@@ -36,10 +45,13 @@ TESTS = tools/filtering/test_invalid_filter \
        tools/rotation/test_schedule_api \
        tools/metadata/test_kernel \
        tools/working-directory/test_relayd_working_directory \
        tools/rotation/test_schedule_api \
        tools/metadata/test_kernel \
        tools/working-directory/test_relayd_working_directory \
-       tools/notification/test_notification_multi_app \
        tools/clear/test_ust \
        tools/clear/test_kernel \
        tools/clear/test_ust \
        tools/clear/test_kernel \
-       tools/tracker/test_event_tracker
+       tools/tracker/test_event_tracker \
+       tools/trigger/start-stop/test_start_stop \
+       tools/trigger/test_add_trigger_cli \
+       tools/trigger/test_list_triggers_cli \
+       tools/trigger/test_remove_trigger_cli
 
 if HAVE_LIBLTTNG_UST_CTL
 SUBDIRS += ust
 
 if HAVE_LIBLTTNG_UST_CTL
 SUBDIRS += ust
index c4a6200dae0351e2ad86ee727d11631fec0f28fb..42c3fcce404195accc5c047113900ad41d3a6d88 100755 (executable)
@@ -33,12 +33,15 @@ function lttng_track_pid()
 function run_workload()
 {
        local TEST_APP=$1
 function run_workload()
 {
        local TEST_APP=$1
+       # shift the first argument, passing along the other args if any to the
+       # test app.
+       shift
        local start_file_sync
        start_file_sync=$(mktemp -u)
 
        lttng_untrack_all
 
        local start_file_sync
        start_file_sync=$(mktemp -u)
 
        lttng_untrack_all
 
-       ./"$TEST_APP" "$start_file_sync" &
+       ./"$TEST_APP" "$start_file_sync" "$@" &
        PID=$!
        lttng_track_pid $PID
 
        PID=$!
        lttng_track_pid $PID
 
@@ -108,7 +111,7 @@ function test_kernel_callstack()
        lttng_enable_kernel_syscall_ok "$SESSION_NAME" "$EVENT_NAME" "$CHANNEL_NAME"
        add_context_kernel_ok "$SESSION_NAME" "$CHANNEL_NAME" "callstack-kernel"
 
        lttng_enable_kernel_syscall_ok "$SESSION_NAME" "$EVENT_NAME" "$CHANNEL_NAME"
        add_context_kernel_ok "$SESSION_NAME" "$CHANNEL_NAME" "callstack-kernel"
 
-       run_workload $TEST_APP_KERNELSPACE
+       run_workload "$TEST_APP_KERNELSPACE" "/proc/cpuinfo" "/proc/cmdline"
 
        destroy_lttng_session_ok "$SESSION_NAME"
 
 
        destroy_lttng_session_ok "$SESSION_NAME"
 
index 487ee668e2a583fef364044b753e64f86afb82e7..d09d61a95aeb2e96f73ab98c47b451813dc71ecb 100755 (executable)
@@ -27,7 +27,7 @@ function trace_testapp()
        lttng_untrack_kernel_all_ok
 
        # Launch the testapp and save its Process ID
        lttng_untrack_kernel_all_ok
 
        # Launch the testapp and save its Process ID
-       ./"$TESTCMD" "$start_file_sync" &
+       ./"$TESTCMD" "$start_file_sync" "/proc/cpuinfo" "/proc/cmdline" &
        PID=$!
 
        # Set LTTng to track this PID and start the tracing
        PID=$!
 
        # Set LTTng to track this PID and start the tracing
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 \
 
 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 f1f2a1667982469df24f1f36757d92683931b42f..c086bcf38b2e2b57d9a7053be51cfda2b702b3d6 100644 (file)
@@ -9,9 +9,25 @@ noinst_PROGRAMS = base_client notification rotation
 
 if NO_SHARED
 
 
 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
-
+CLEANFILES = libpause_consumer.so libpause_consumer.so.debug libpause_sessiond.so libpause_sessiond.so.debug
+EXTRA_DIST = \
+            base_client.c \
+            consumer_testpoints.c \
+            sessiond_testpoints.c \
+            notification.c \
+            test_notification_kernel_buffer_usage \
+            test_notification_kernel_capture \
+            test_notification_kernel_error \
+            test_notification_kernel_instrumentation \
+            test_notification_kernel_syscall \
+            test_notification_kernel_userspace_probe \
+            test_notification_multi_app \
+            test_notification_trigger_discarded_count \
+            test_notification_ust_buffer_usage \
+            test_notification_ust_capture \
+            test_notification_ust_error \
+            test_notification_ust_event_rule_condition_exclusion \
+            util_event_generator.sh
 else
 
 # In order to test the health check feature, the helper library
 else
 
 # In order to test the health check feature, the helper library
@@ -26,7 +42,14 @@ libpause_consumer_la_LIBADD = \
        $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
        $(DL_LIBS)
 libpause_consumer_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS)
        $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
        $(DL_LIBS)
 libpause_consumer_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS)
-noinst_LTLIBRARIES = libpause_consumer.la
+
+libpause_sessiond_la_SOURCES = sessiond_testpoints.c
+libpause_sessiond_la_LIBADD = \
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
+       $(DL_LIBS)
+libpause_sessiond_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS)
+noinst_LTLIBRARIES = libpause_sessiond.la libpause_consumer.la
 
 base_client_SOURCES = base_client.c
 base_client_LDADD = $(LIB_LTTNG_CTL)
 
 base_client_SOURCES = base_client.c
 base_client_LDADD = $(LIB_LTTNG_CTL)
@@ -37,9 +60,35 @@ notification_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm
 rotation_SOURCES = rotation.c
 rotation_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_kernel_buffer_usage \
+       test_notification_kernel_error \
+       test_notification_kernel_instrumentation \
+       test_notification_kernel_syscall \
+       test_notification_kernel_userspace_probe \
+       test_notification_multi_app \
+       test_notification_trigger_discarded_count \
+       test_notification_ust_buffer_usage \
+       test_notification_ust_error \
+       test_notification_ust_event_rule_condition_exclusion \
+       test_rotation \
+       util_event_generator.sh
 
 
+EXTRA_DIST = \
+       test_notification_kernel_buffer_usage \
+       test_notification_kernel_capture \
+       test_notification_kernel_error \
+       test_notification_kernel_instrumentation \
+       test_notification_kernel_syscall \
+       test_notification_kernel_userspace_probe \
+       test_notification_multi_app \
+       test_notification_trigger_discarded_count \
+       test_notification_ust_buffer_usage \
+       test_notification_ust_capture \
+       test_notification_ust_error \
+       test_notification_ust_event_rule_condition_exclusion \
+       test_rotation \
+       util_event_generator.sh
 
 all-local:
        @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
 
 all-local:
        @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
index 364d41895bb78531f61b20a1842bfe4a02960680..303d8bc8c3859ef7145ae641c5814cc64c0f7ea2 100644 (file)
 #include <lttng/condition/buffer-usage.h>
 #include <lttng/condition/condition.h>
 #include <lttng/condition/evaluation.h>
 #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/domain.h>
 #include <lttng/endpoint.h>
+#include <lttng/event-field-value.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/kernel-probe.h>
 #include <lttng/lttng-error.h>
 #include <lttng/lttng-error.h>
+#include <lttng/lttng.h>
 #include <lttng/notification/channel.h>
 #include <lttng/notification/notification.h>
 #include <lttng/notification/channel.h>
 #include <lttng/notification/notification.h>
+#include <lttng/condition/evaluation.h>
 #include <lttng/trigger/trigger.h>
 #include <lttng/trigger/trigger.h>
-#include <lttng/lttng.h>
+#include <lttng/userspace-probe.h>
 
 #include <tap/tap.h>
 
 
 #include <tap/tap.h>
 
-#define NUM_TESTS 104
+/* A callback to populate the condition capture descriptor */
+typedef int (*condition_capture_desc_cb)(struct lttng_condition *condition);
+
+/* A callback for captured field validation */
+typedef int (*validate_cb)(const struct lttng_event_field_value *event_field, unsigned iteration);
 
 int nb_args = 0;
 int named_pipe_args_start = 0;
 
 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;
 
 const char *app_state_file = NULL;
 
+enum field_type {
+       FIELD_TYPE_PAYLOAD,
+       FIELD_TYPE_CONTEXT,
+       FIELD_TYPE_APP_CONTEXT,
+       FIELD_TYPE_ARRAY_FIELD,
+};
+
+struct capture_base_field_tuple {
+       char* field_name;
+       enum field_type field_type;
+       bool expected_ust; // Do we expect a capture?
+       bool expected_kernel; // Do we expect a capture?
+       validate_cb validate_ust;
+       validate_cb validate_kernel;
+};
+
 static
 static
-void wait_on_file(const char *path, bool file_exist)
+const char *field_value_type_to_str(enum lttng_event_field_value_type type)
 {
 {
-       if (!path) {
-               return;
+       switch (type) {
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN:
+               return "UNKNOWN";
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID:
+               return "INVALID";
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT:
+               return "UNSIGNED INT";
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT:
+               return "SIGNED INT";
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+               return "UNSIGNED ENUM";
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+               return "SIGNED ENUM";
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL:
+               return "REAL";
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING:
+               return "STRING";
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY:
+               return "ARRAY";
+       default:
+               abort();
        }
        }
-       for (;;) {
-               int ret;
-               struct stat buf;
+}
 
 
-               ret = stat(path, &buf);
-               if (ret == -1 && errno == ENOENT) {
-                       if (file_exist) {
-                               /*
-                                * The file does not exist. wait a bit and
-                                * continue looping until it does.
-                                */
-                               (void) poll(NULL, 0, 10);
-                               continue;
-                       }
+static int validate_type(
+               const struct lttng_event_field_value *event_field,
+               enum lttng_event_field_value_type expect)
+{
+       int ret;
+       enum lttng_event_field_value_type value;
 
 
-                       /*
-                        * File does not exist and the exit condition we want.
-                        * Break from the loop and return.
-                        */
-                       break;
-               }
-               if (ret) {
-                       perror("stat");
-                       exit(EXIT_FAILURE);
-               }
-               /*
-                * stat() returned 0, so the file exists. break now only if
-                * that's the exit condition we want.
-                */
-               if (file_exist) {
-                       break;
-               }
+       value = lttng_event_field_value_get_type(event_field);
+       if (value == LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID) {
+               ret = 1;
+               goto end;
        }
        }
+
+       ret = (expect == value);
+       ok(ret, "Expected field type: %s got %s",
+                       field_value_type_to_str(expect),
+                       field_value_type_to_str(value));
+
+       ret = !ret;
+
+end:
+       return ret;
 }
 
 }
 
-static
-int write_pipe(const char *path, uint8_t data)
+/*
+ * Validate unsigned captured field against the iteration number.
+ * The iteration number is always unsigned and will always be compared to value
+ * under MAX_UINT.
+ */
+static int validate_unsigned_int_field(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
 {
 {
-       int ret = 0;
-       int fd = 0;
+       int ret;
+       uint64_t value;
+       enum lttng_event_field_value_status status;
 
 
-       fd = open(path, O_WRONLY | O_NONBLOCK);
-       if (fd < 0) {
-               perror("Could not open consumer control named pipe");
+       ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT);
+       if (ret) {
                goto end;
        }
 
                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;
+       status = lttng_event_field_value_unsigned_int_get_value(
+               event_field , &value);
+       if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               fail("lttng_event_field_value_unsigned_int_get_value");
+               ret = 1;
                goto end;
        }
 
                goto end;
        }
 
-       ret = close(fd);
-       if (ret < 0) {
-               perror("Name pipe closing failed");
-               ret = -1;
-               goto end;
-       }
+       ret = (value == (uint64_t) iteration);
+       ok (ret, "Expected unsigned int of value: %u got %" PRIu64, iteration, value);
+
+       ret = !ret;
+
 end:
 end:
+
        return ret;
 }
 
        return ret;
 }
 
-static
-int stop_consumer(const char **argv)
+/*
+ * Validate signed captured field.
+ * Value should be -1.
+ */
+static int validate_signed_int_field(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
 {
 {
-       int ret = 0, i;
+       int ret;
+       int64_t expected = -1;
+       int64_t value;
+       enum lttng_event_field_value_status status;
 
 
-       for (i = named_pipe_args_start; i < nb_args; i++) {
-               ret = write_pipe(argv[i], 49);
+       /* Unused */
+       (void) iteration;
+
+       ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT);
+       if (ret) {
+               goto end;
+       }
+
+       status = lttng_event_field_value_signed_int_get_value(
+               event_field , &value);
+       if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               fail("lttng_event_field_value_signed_int_get_value");
+               ret = 1;
+               goto end;
        }
        }
+
+       ret = (value == expected);
+       ok(ret, "Expected signed int of value: %" PRId64 " got %" PRId64, expected, value);
+
+       ret = !ret;
+
+end:
+
        return ret;
 }
 
        return ret;
 }
 
-static
-int resume_consumer(const char **argv)
+/*
+ * Validate array of unsigned int.
+ */
+static int validate_array_unsigned_int_field(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
 {
 {
-       int ret = 0, i;
+       int ret;
+       enum lttng_event_field_value_status status;
+       unsigned int expected = 3;
+       unsigned int count;
 
 
-       for (i = named_pipe_args_start; i < nb_args; i++) {
-               ret = write_pipe(argv[i], 0);
+       /* Unused */
+       (void) iteration;
+
+       ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY);
+       if (ret) {
+               goto end;
+       }
+
+       status = lttng_event_field_value_array_get_length(event_field, &count);
+       if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               fail("lttng_event_field_value_array_get_length");
+               ret = 1;
+               goto end;
+       }
+
+       ret = (count == expected);
+       ok(ret, "Expected %d subelements got %d", expected, count);
+       if (!ret) {
+               ret = 1;
+               goto end;
+       }
+
+       for (unsigned int i = 1; i < count + 1; i++) {
+               const struct lttng_event_field_value *value;
+               status = lttng_event_field_value_array_get_element_at_index(
+                               event_field, i - 1, &value);
+               if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+                       fail("lttng_event_field_value_array_get_element_at_index");
+                       ret = 1;
+                       goto end;
+               }
+               ret = validate_unsigned_int_field(value, i);
+               if (ret) {
+                       goto end;
+               }
        }
        }
+
+       ret = 0;
+end:
+
        return ret;
 }
        return ret;
 }
-
-static
-int suspend_application(void)
+static int validate_array_unsigned_int_field_at_index(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
 {
        int ret;
 {
        int ret;
-       struct stat buf;
+       uint64_t expected_value = 2;
+       enum lttng_event_field_value_status status;
+       uint64_t value;
 
 
-       if (!stat(app_state_file, &buf)) {
-               fail("App is already in a suspended state.");
-               ret = -1;
-               goto error;
-       }
+       /* Unused */
+       (void) iteration;
 
 
-       /*
-        * Send SIGUSR1 to application instructing it to bypass tracepoint.
-        */
-       ret = kill(app_pid, SIGUSR1);
+       ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT);
        if (ret) {
        if (ret) {
-               fail("SIGUSR1 failed. errno %d", errno);
-               ret = -1;
-               goto error;
+               goto end;
        }
 
        }
 
-       wait_on_file(app_state_file, true);
+       status = lttng_event_field_value_unsigned_int_get_value(
+               event_field , &value);
+       if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               fail("lttng_event_field_value_unsigned_int_get_value");
+               ret = 1;
+               goto end;
+       }
 
 
-error:
-       return ret;
+       ret = (value == expected_value);
+       ok (ret, "Expected unsigned int of value: %u got %" PRIu64,
+                       expected_value, value);
 
 
+       ret = 0;
+end:
+       return ret;
 }
 
 }
 
-static
-int resume_application()
+/*
+ * Validate sequence for a string (seqfield1):
+ *
+ * Value: "test" in utf8  [116, 101, 115, 116]
+ */
+static int validate_seqfield1(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
 {
        int ret;
 {
        int ret;
-       struct stat buf;
+       enum lttng_event_field_value_status status;
+       unsigned int count;
+       unsigned int expect[4] = {116, 101, 115, 116};
 
 
-       ret = stat(app_state_file, &buf);
-       if (ret == -1 && errno == ENOENT) {
-               fail("State file does not exist");
-               goto error;
-       }
+       /* Unused */
+       (void) iteration;
+
+       ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY);
        if (ret) {
        if (ret) {
-               perror("stat");
-               goto error;
+               goto end;
        }
 
        }
 
-       ret = kill(app_pid, SIGUSR1);
-       if (ret) {
-               fail("SIGUSR1 failed. errno %d", errno);
-               ret = -1;
-               goto error;
+       status = lttng_event_field_value_array_get_length(event_field, &count);
+       if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               fail("lttng_event_field_value_array_get_length");
+               ret = 1;
+               goto end;
        }
 
        }
 
-       wait_on_file(app_state_file, false);
+       ret = (count == 4);
+       ok(ret, "Expected 4 subelement got %d", count);
+       if (!ret) {
+               ret = 1;
+               goto end;
+       }
 
 
-error:
-       return ret;
+       for (unsigned int i = 0; i < count ; i++) {
+               const struct lttng_event_field_value *value;
+               status = lttng_event_field_value_array_get_element_at_index(
+                               event_field, i, &value);
+               if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+                       fail("lttng_event_field_value_array_get_element_at_index");
+                       ret = 1;
+                       goto end;
+               }
+               ret = validate_unsigned_int_field(value, expect[i]);
+               if (ret) {
+                       goto end;
+               }
+       }
 
 
-}
+       ret = 0;
+end:
 
 
+       return ret;
+}
 
 
-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)
+static int validate_string(
+               const struct lttng_event_field_value *event_field,
+               const char *expect)
 {
 {
-       unsigned int test_vector_size = 5, i;
-       enum lttng_condition_status condition_status;
-       struct lttng_action *action;
+       int ret;
+       const char *value = NULL;
 
 
-       /* Set-up */
-       action = lttng_action_notify_create();
-       if (!action) {
-               fail("Setup error on action creation");
+       ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_STRING);
+       if (ret) {
                goto end;
        }
 
                goto end;
        }
 
-       /* Test lttng_register_trigger with null value */
-       ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected");
+       value = lttng_event_field_value_string_get_value(event_field);
+       if (!value) {
+               fail("lttng_event_field_value_array_get_length");
+               ret = 1;
+               goto end;
+       }
 
 
-       /* Test: register a trigger */
+       ok(!strcmp(value, expect), "Expected string: \"%s\" got \"%s\"", expect, value);
 
 
-       for (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;
+       ret = 0;
+end:
 
 
-               struct lttng_trigger *trigger = NULL;
-               struct lttng_condition *condition = NULL;
+       return ret;
+}
 
 
-               /* 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;
-               }
+/*
+ * Validate string. Expected value is "test".
+ */
+static int validate_string_test(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
+{
+       int ret;
+       const char *expect = "test";
 
 
-               if (!condition) {
-                       loop_ret = 1;
-                       goto loop_end;
+       /* Unused */
+       (void) iteration;
 
 
-               }
+       ret = validate_string(event_field, expect);
+       return ret;
+}
 
 
-               /* Prepare the condition for trigger registration test */
+/*
+ * Validate escaped string. Expected value is "\*".
+ */
+static int validate_string_escaped(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
+{
+       int ret;
+       const char *expect = "\\*";
 
 
-               /* 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++;
+       /* Unused */
+       (void) iteration;
 
 
-               /* 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;
+       ret = validate_string(event_field, expect);
+       return ret;
+}
+
+/*
+ * Validate real field.
+ */
+static int validate_real(
+               const struct lttng_event_field_value *event_field,
+               double expect)
+{
+       int ret;
+       double value;
+       enum lttng_event_field_value_status status;
+
+       ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_REAL);
+       if (ret) {
+               goto end;
+       }
+
+       status = lttng_event_field_value_real_get_value(event_field, &value);
+       if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               fail("lttng_event_field_value_real_get_value");
+               ret = 1;
+               goto end;
+       }
+
+       ret = (value == expect);
+       ok(ret, "Real expected: %f got: %f", expect, value);
+
+       ret = !ret;
+end:
+       return ret;
+}
+
+/*
+ * Validate floatfield.
+ */
+static int validate_floatfield(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
+{
+       int ret;
+       double expect = 2222.0;
+
+       /* Unused */
+       (void) iteration;
+
+       ret = validate_real(event_field, expect);
+       return ret;
+}
+
+/*
+ * Validate doublefield.
+ */
+static int validate_doublefield(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
+{
+       int ret;
+       double expect = 2.0;
+
+       /* Unused */
+       (void) iteration;
+
+       ret = validate_real(event_field, expect);
+       return ret;
+}
+
+/*
+ * Validate enum0:  enum0 = ( "AUTO: EXPECT 0" : container = 0 )
+ */
+static int validate_enum0(const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
+{
+       int ret;
+       enum lttng_event_field_value_status status;
+       uint64_t value;
+       uint64_t expected_value = 0;
+
+       /* Unused */
+       (void) iteration;
+
+       ret = validate_type(event_field,
+                       LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM);
+       if (ret) {
+               goto end;
+       }
+
+       status = lttng_event_field_value_unsigned_int_get_value(
+                       event_field, &value);
+       if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               fail("lttng_event_field_value_unsigned_int_get_value");
+               ret = 1;
+               goto end;
+       }
+
+       ok(value == expected_value,
+                       "Enum value expected: %" PRIu64 " got %" PRIu64,
+                       expected_value, value);
+
+end:
+       return ret;
+}
+
+/*
+ * Validate enumnegative:  enumnegative = ( "AUTO: EXPECT 0" : container = 0 )
+ *
+ * We expect 2 labels here.
+ */
+static int validate_enumnegative(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
+{
+       int ret;
+       enum lttng_event_field_value_status status;
+       int64_t value;
+       int64_t expected_value = -1;
+
+       /* Unused */
+       (void) iteration;
+
+       ret = validate_type(
+                       event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM);
+       if (ret) {
+               goto end;
+       }
+
+       status = lttng_event_field_value_signed_int_get_value(
+                       event_field, &value);
+       if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               fail("lttng_event_field_value_unsigned_int_get_value");
+               ret = 1;
+               goto end;
+       }
+
+       ok(value == expected_value,
+                       "Enum value expected: %" PRId64 " got %" PRId64,
+                       expected_value, value);
+
+end:
+       return ret;
+}
+
+static int validate_context_procname_ust(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
+{
+       int ret;
+
+       /* Unused */
+       (void) iteration;
+
+       ret = validate_string(event_field, "gen-ust-events");
+       return ret;
+}
+
+static int validate_context_procname_kernel(
+               const struct lttng_event_field_value *event_field,
+               unsigned int iteration)
+{
+       int ret;
+
+       /* Unused */
+       (void) iteration;
+
+       ret = validate_string(event_field, "echo");
+       return ret;
+}
+
+struct capture_base_field_tuple test_capture_base_fields[] = {
+               {"DOESNOTEXIST", FIELD_TYPE_PAYLOAD, false, false, NULL, NULL},
+               {"intfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field},
+               {"longfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field},
+               {"signedfield", FIELD_TYPE_PAYLOAD, true, true, validate_signed_int_field, validate_signed_int_field},
+               {"arrfield1", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field},
+               {"arrfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test},
+               {"arrfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field},
+               {"seqfield1", FIELD_TYPE_PAYLOAD, true, true, validate_seqfield1, validate_seqfield1},
+               {"seqfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test},
+               {"seqfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field},
+               {"seqfield4", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field},
+               {"arrfield1[1]", FIELD_TYPE_ARRAY_FIELD, true, true, validate_array_unsigned_int_field_at_index, validate_array_unsigned_int_field_at_index},
+               {"stringfield", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test},
+               {"stringfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_escaped, validate_string_escaped},
+               {"floatfield", FIELD_TYPE_PAYLOAD, true, false, validate_floatfield, validate_floatfield},
+               {"doublefield", FIELD_TYPE_PAYLOAD, true, false, validate_doublefield, validate_doublefield},
+               {"enum0", FIELD_TYPE_PAYLOAD, true, true, validate_enum0, validate_enum0},
+               {"enumnegative", FIELD_TYPE_PAYLOAD, true, true, validate_enumnegative, validate_enumnegative},
+               {"$ctx.procname", FIELD_TYPE_CONTEXT, true, true, validate_context_procname_ust, validate_context_procname_kernel},
+};
+
+static const char *get_notification_trigger_name(
+               struct lttng_notification *notification)
+{
+       const char *name = NULL;
+       enum lttng_evaluation_status status;
+       const struct lttng_evaluation *evaluation;
+       evaluation = lttng_notification_get_evaluation(notification);
+       if (evaluation == NULL) {
+               fail("lttng_notification_get_evaluation");
+               goto end;
+       }
+
+       switch (lttng_evaluation_get_type(evaluation)) {
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+       {
+               status = lttng_evaluation_event_rule_get_trigger_name(
+                               evaluation, &name);
+               if (status != LTTNG_EVALUATION_STATUS_OK) {
+                       fail("lttng_evaluation_event_rule_get_trigger_name");
+                       name = NULL;
+                       goto end;
                }
                }
-               mask_position++;
+               break;
+       }
+       default:
+               fail("Wrong notification evaluation type \n");
+               goto end;
+       }
+end:
+       return name;
+}
 
 
-               /* 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;
+static int validator_notification_trigger_name(
+               struct lttng_notification *notification,
+               const char *trigger_name)
+{
+       int ret;
+       bool name_is_equal;
+       const char *name;
+
+       assert(notification);
+       assert(trigger_name);
+
+       name = get_notification_trigger_name(notification);
+       if (name == NULL) {
+               ret = 1;
+               goto end;
+       }
+
+       name_is_equal = (strcmp(trigger_name, name) == 0);
+       ok(name_is_equal, "Expected trigger name: %s got %s", trigger_name,
+                       name);
+
+       ret = !name_is_equal;
+
+end:
+       return ret;
+}
+
+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) {
+                               /*
+                                * The file does not exist. wait a bit and
+                                * continue looping until it does.
+                                */
+                               (void) poll(NULL, 0, 10);
+                               continue;
                        }
                        }
-                       threshold_ratio_set = true;
+
+                       /*
+                        * File does not exist and the exit condition we want.
+                        * Break from the loop and return.
+                        */
+                       break;
                }
                }
-               mask_position++;
+               if (ret) {
+                       perror("stat");
+                       exit(EXIT_FAILURE);
+               }
+               /*
+                * stat() returned 0, so the file exists. break now only if
+                * that's the exit condition we want.
+                */
+               if (file_exist) {
+                       break;
+               }
+       }
+}
+
+static
+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;
+}
+
+static
+int stop_consumer(const char **argv)
+{
+       int ret = 0, i;
+
+       for (i = named_pipe_args_start; i < nb_args; i++) {
+               ret = write_pipe(argv[i], 49);
+       }
+       return ret;
+}
+
+static
+int resume_consumer(const char **argv)
+{
+       int ret = 0, i;
+
+       for (i = named_pipe_args_start; i < nb_args; i++) {
+               ret = write_pipe(argv[i], 0);
+       }
+       return ret;
+}
+
+static
+int suspend_application(void)
+{
+       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.
+        */
+       assert(app_pid > 1);
+
+       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;
+
+}
+
+static
+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;
+       }
+
+       assert(app_pid > 1);
+
+       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;
+
+}
+
+
+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)
+{
+       unsigned int test_vector_size = 5, i;
+       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 */
+
+       for (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-registered 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);
+}
+
+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)
+{
+       enum lttng_condition_status condition_status;
+       int ret = 0;
+
+       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;
+       }
+
+       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;
+       }
+
+       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;
+       }
+
+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 (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) {
+               fail("Setup error on condition creation");
+               goto end;
+       }
+
+       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;
+       }
+
+       /*
+        * 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");
+
+       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;
+       }
+
+       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();
+       }
+
+       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(
+                       tmp_condition, ratio);
+       if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+               fail("Setup error on condition creation");
+               ret = -1;
+               goto error;
+       }
+
+       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;
+       }
+
+       /* 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;
+       }
+
+       ret = lttng_register_trigger(tmp_trigger);
+       if (ret) {
+               fail("Setup error on trigger registration");
+               ret = -1;
+               goto error;
+       }
+
+       *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;
+       }
+
+       /* 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);
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_action_destroy(action);
+       lttng_condition_destroy(condition);
+}
+
+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;
+       /* This is not 99 since we can end up in scenario where an event is
+        * bigger than 1% of the buffer and hence the buffer ratio will never
+        * trigger since the event will always be discarder by the tracer.
+        */
+
+       double high_ratio = 0.90;
+
+       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 = 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);
+       ok(notification_channel, "Notification channel object creation");
+       if (!notification_channel) {
+               goto end;
+       }
+
+       /* 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 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 high condition");
+
+       resume_application();
+
+       /* Wait for notification to happen */
+       stop_consumer(argv);
+       lttng_start_tracing(session_name);
+
+       /* Wait for high notification */
+       do {
+               nc_status = lttng_notification_channel_get_next_notification(
+                               notification_channel, &notification);
+       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
+       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");
+
+       do {
+               nc_status = lttng_notification_channel_get_next_notification(
+                               notification_channel, &notification);
+       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
+       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);
+
+       do {
+               nc_status = lttng_notification_channel_get_next_notification(
+                               notification_channel, &notification);
+       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
+       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);
+
+       do {
+               nc_status = lttng_notification_channel_get_next_notification(
+                               notification_channel, &notification);
+       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
+       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);
+
+       do {
+               nc_status = lttng_notification_channel_get_next_notification(
+                               notification_channel, &notification);
+       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
+       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;
+
+       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");
+
+end:
+       lttng_notification_channel_destroy(notification_channel);
+       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);
+}
+
+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,
+               condition_capture_desc_cb capture_desc_cb,
+               struct lttng_condition **condition,
+               struct lttng_action **action,
+               struct lttng_trigger **trigger)
+{
+       enum lttng_event_rule_status event_rule_status;
+       enum lttng_trigger_status trigger_status;
+
+       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);
+       }
+
+       if (exclusions) {
+               int i;
+               bool success = true;
+               assert(domain_type == LTTNG_DOMAIN_UST);
+               assert(exclusion_count > 0);
+
+               for (i = 0; i < exclusion_count; i++) {
+                       event_rule_status =
+                                       lttng_event_rule_tracepoint_add_exclusion(
+                                                       event_rule,
+                                                       exclusions[i]);
+                       if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+                               fail("Setting tracepoint event rule exclusion \"%s\".",
+                                               exclusions[i]);
+                               success = false;
+                       }
+               }
+               ok(success, "Setting tracepoint event rule exclusions");
+       }
+
+       tmp_condition = lttng_condition_event_rule_create(event_rule);
+       ok(tmp_condition, "Condition event rule object creation");
+
+       if (capture_desc_cb) {
+               ret = capture_desc_cb(tmp_condition);
+               if (ret) {
+                       assert("Generating the condition capture descriptor");
+               }
+       }
+
+       tmp_action = lttng_action_notify_create();
+       ok(tmp_action, "Action event rule object creation");
+
+       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);
+
+       lttng_event_rule_destroy(event_rule);
+
+       *condition = tmp_condition;
+       *action = tmp_action;
+       *trigger = tmp_trigger;
+
+       return;
+}
+
+static struct lttng_notification *get_next_notification(
+               struct lttng_notification_channel *notification_channel)
+{
+       struct lttng_notification *local_notification = NULL;
+       enum lttng_notification_channel_status status;
+
+       /* Receive the next notification. */
+       status = lttng_notification_channel_get_next_notification(
+                       notification_channel, &local_notification);
+
+       switch (status) {
+       case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK:
+               break;
+       case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED:
+               fail("Notifications have been dropped");
+               local_notification = NULL;
+               break;
+       default:
+               /* Unhandled conditions / errors. */
+               fail("error: Unknown notification channel status\n");
+               local_notification = NULL;
+               break;
+       }
+
+       return local_notification;
+}
+
+static void test_tracepoint_event_rule_notification(
+               enum lttng_domain_type domain_type)
+{
+       int i;
+       int ret;
+       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;
 
 
-               /* 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++;
+       if (domain_type == LTTNG_DOMAIN_UST) {
+               pattern = "tp:tptest";
+       } else {
+               pattern = "lttng_test_filter_event";
+       }
 
 
-               /* 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;
-               }
+       create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0,
+                       NULL, domain_type, NULL, &condition, &action, &trigger);
 
 
-               /* Safety check */
-               if (mask_position != test_vector_size -1) {
-                       assert("Logic error for test vector generation");
-               }
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
 
 
-               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;
+       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++) {
+               struct lttng_notification *notification = get_next_notification(
+                               notification_channel);
+               ok(notification, "Received notification");
+
+               /* Error */
+               if (notification == NULL) {
+                       goto end;
                }
 
                }
 
-               /* Create trigger */
-               trigger = lttng_trigger_create(condition, action);
-               if (!trigger) {
-                       loop_ret = 1;
-                       goto loop_end;
+               ret = validator_notification_trigger_name(notification, trigger_name);
+               lttng_notification_destroy(notification);
+               if (ret) {
+                       goto end;
                }
                }
+       }
 
 
-               loop_ret = lttng_register_trigger(trigger);
+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;
+}
 
 
-loop_end:
-               if (loop_ret == 1) {
-                       fail("Setup error occurred for tuple: %s", test_tuple_string);
-                       goto loop_cleanup;
-               }
+static void test_tracepoint_event_rule_notification_filter(
+               enum lttng_domain_type domain_type)
+{
+       int i;
+       enum lttng_notification_channel_status nc_status;
 
 
-               /* 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);
+       struct lttng_condition *ctrl_condition = NULL, *condition = NULL;
+       struct lttng_action *ctrl_action = NULL, *action = 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";
+       }
 
 
-                       /*
-                        * 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);
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
 
 
-                       /* Test that a trigger can be unregistered */
-                       loop_ret = lttng_unregister_trigger(trigger);
-                       ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string);
+       create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL,
+                       0, NULL, domain_type, NULL, &ctrl_condition,
+                       &ctrl_action, &ctrl_trigger);
 
 
-                       /*
-                        * 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-registered 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);
+       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, NULL,
+                       &condition, &action, &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++) {
+               const char *name;
+               struct lttng_notification *notification = get_next_notification(
+                               notification_channel);
+               ok(notification, "Received notification");
+
+               /* Error */
+               if (notification == NULL) {
+                       goto end;
                }
 
                }
 
-loop_cleanup:
-               free(test_tuple_string);
-               lttng_trigger_destroy(trigger);
-               lttng_condition_destroy(condition);
+               name = get_notification_trigger_name(notification);
+               if (name == NULL) {
+                       lttng_notification_destroy(notification);
+                       goto end;
+               }
+
+               if (strcmp(ctrl_trigger_name, name) == 0) {
+                       ctrl_count++;
+               } else if (strcmp(trigger_name, name) == 0) {
+                       count++;
+               }
+               lttng_notification_destroy(notification);
        }
        }
+       ok(ctrl_count / 2 == count,
+                       "Get twice as many control notif as of regular notif");
 
 end:
 
 end:
+       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);
        lttng_action_destroy(action);
        lttng_action_destroy(action);
+       lttng_action_destroy(ctrl_action);
+       return;
 }
 
 }
 
-static
-void wait_data_pending(const char *session_name)
+static void test_tracepoint_event_rule_notification_exclusion(
+               enum lttng_domain_type domain_type)
 {
 {
-       int ret;
+       enum lttng_notification_channel_status nc_status;
+       struct lttng_condition *ctrl_condition = NULL, *condition = NULL;
+       struct lttng_action *ctrl_action = NULL, *action = 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");
 
 
-       do {
-               ret = lttng_data_pending(session_name);
-               assert(ret >= 0);
-       } while (ret != 0);
+       create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL,
+                       0, NULL, domain_type, NULL, &ctrl_condition,
+                       &ctrl_action, &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, NULL, &condition, &action,
+                       &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++) {
+               const char *name;
+               struct lttng_notification *notification = get_next_notification(
+                               notification_channel);
+               ok(notification, "Received notification");
+
+               /* Error */
+               if (notification == NULL) {
+                       goto end;
+               }
+
+               name = get_notification_trigger_name(notification);
+               if (name == NULL) {
+                       lttng_notification_destroy(notification);
+                       goto end;
+               }
+
+               if (strcmp(ctrl_trigger_name, name) == 0) {
+                       ctrl_count++;
+               } else if (strcmp(trigger_name, name) == 0) {
+                       count++;
+               }
+               lttng_notification_destroy(notification);
+       }
+       ok(ctrl_count / 5 == count,
+                       "Got 5 times as many control notif as of regular notif");
+
+end:
+       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);
+       lttng_action_destroy(action);
+       lttng_action_destroy(ctrl_action);
+       return;
 }
 
 }
 
-static
-void test_notification_channel(const char *session_name, const char *channel_name, const enum lttng_domain_type domain_type, const char **argv)
+static void test_kprobe_event_rule_notification(
+               enum lttng_domain_type domain_type)
 {
 {
-       int ret = 0;
-       enum lttng_condition_status condition_status;
        enum lttng_notification_channel_status nc_status;
        enum lttng_notification_channel_status nc_status;
+       enum lttng_event_rule_status event_rule_status;
+       enum lttng_trigger_status trigger_status;
 
 
-       struct lttng_action *action = NULL;
-       struct lttng_notification *notification = NULL;
        struct lttng_notification_channel *notification_channel = NULL;
        struct lttng_notification_channel *notification_channel = NULL;
+       struct lttng_condition *condition = NULL;
+       struct lttng_kernel_probe_location *location = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+       struct lttng_action *action = NULL;
        struct lttng_trigger *trigger = NULL;
        struct lttng_trigger *trigger = NULL;
+       const char *trigger_name = "kprobe_trigger";
+       const char *symbol_name = "_do_fork";
+       int i, ret;
 
 
-       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;
        }
 
        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");
+       location = lttng_kernel_probe_location_symbol_create(symbol_name, 0);
+       if (!location) {
+               fail("Could not create kernel probe location.");
                goto end;
        }
 
                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;
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
 
 
-       }
-       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");
+       event_rule = lttng_event_rule_kprobe_create();
+       ok(event_rule, "kprobe event rule object creation");
+
+       event_rule_status = lttng_event_rule_kprobe_set_location(
+                       event_rule, location);
+       ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+                       "Setting kprobe event rule location: %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;
        }
 
                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");
+       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;
        }
                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");
+
+       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++) {
+               struct lttng_notification *notification = get_next_notification(
+                               notification_channel);
+               ok(notification, "Received notification");
+
+               /* Error */
+               if (notification == NULL) {
+                       goto end;
+               }
+
+               ret = validator_notification_trigger_name(notification, trigger_name);
+               lttng_notification_destroy(notification);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       suspend_application();
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_unregister_trigger(trigger);
+       lttng_trigger_destroy(trigger);
+       lttng_action_destroy(action);
+       lttng_event_rule_destroy(event_rule);
+       lttng_condition_destroy(condition);
+       lttng_kernel_probe_location_destroy(location);
+       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;
        }
                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");
+
+       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;
        }
 
                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");
+       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;
        }
                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");
+
+       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;
        }
 
                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");
+       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;
        }
                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");
+
+       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++) {
+               struct lttng_notification *notification = get_next_notification(
+                               notification_channel);
+               ok(notification, "Received notification");
+
+               /* Error */
+               if (notification == NULL) {
+                       goto end;
+               }
+
+               ret = validator_notification_trigger_name(notification, trigger_name);
+               lttng_notification_destroy(notification);
+               if (ret) {
+                       goto end;
+               }
+       }
+end:
+       suspend_application();
+       lttng_notification_channel_destroy(notification_channel);
+       lttng_unregister_trigger(trigger);
+       lttng_trigger_destroy(trigger);
+       lttng_action_destroy(action);
+       lttng_event_rule_destroy(event_rule);
+       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;
        }
                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;
 
 
-       }
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
 
 
-       /* 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;
-       }
+       event_rule = lttng_event_rule_syscall_create();
+       ok(event_rule, "syscall event rule object creation");
 
 
-       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");
+       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;
        }
 
                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");
+       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;
        }
                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;
+
+       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++) {
+               struct lttng_notification *notification = get_next_notification(
+                               notification_channel);
+               ok(notification, "Received notification");
+
+               /* Error */
+               if (notification == NULL) {
+                       goto end;
+               }
+
+               ret = validator_notification_trigger_name(notification, trigger_name);
+               lttng_notification_destroy(notification);
+               if (ret) {
+                       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");
+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;
        }
 
                goto end;
        }
 
-       /* Register the triggers for low and high condition */
-       trigger = lttng_trigger_create(low_condition, action);
+       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) {
        if (!trigger) {
-               fail("Setup error on low trigger creation");
+               fail("Setup error on trigger creation");
                goto end;
        }
 
                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) {
        ret = lttng_register_trigger(trigger);
        if (ret) {
-               fail("Setup error on low trigger registration");
+               fail("Setup error on trigger registration");
                goto end;
        }
 
                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++) {
+               struct lttng_notification *notification = get_next_notification(
+                               notification_channel);
+               ok(notification, "Received notification");
+
+               /* Error */
+               if (notification == NULL) {
+                       goto end;
+               }
+
+               ret = validator_notification_trigger_name(notification, trigger_name);
+               lttng_notification_destroy(notification);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       suspend_application();
+       lttng_unregister_trigger(trigger);
+       lttng_notification_channel_destroy(notification_channel);
        lttng_trigger_destroy(trigger);
        lttng_trigger_destroy(trigger);
-       trigger = NULL;
+       lttng_event_rule_destroy(event_rule);
+       lttng_condition_destroy(condition);
+       return;
+}
 
 
-       trigger = lttng_trigger_create(high_condition, action);
-       if (!trigger) {
-               fail("Setup error on high trigger creation");
-               goto end;
+static int generate_capture_descr(struct lttng_condition *condition)
+{
+       int ret;
+       struct lttng_event_expr *expr = NULL;
+       unsigned int basic_field_size;
+       enum lttng_condition_status cond_status;
+
+       basic_field_size = sizeof(test_capture_base_fields) / sizeof(*test_capture_base_fields);
+       for (int i = 0; i < basic_field_size; i++) {
+
+               diag("Adding capture descriptor \"%s\"", test_capture_base_fields[i].field_name);
+
+               switch (test_capture_base_fields[i].field_type) {
+               case FIELD_TYPE_PAYLOAD:
+                       expr = lttng_event_expr_event_payload_field_create(
+                                       test_capture_base_fields[i].field_name);
+                       break;
+               case FIELD_TYPE_CONTEXT:
+                       expr = lttng_event_expr_channel_context_field_create(
+                                       test_capture_base_fields[i].field_name);
+                       break;
+               case FIELD_TYPE_ARRAY_FIELD:
+               {
+                       int nb_matches;
+                       unsigned int index;
+                       char field_name[256];
+                       struct lttng_event_expr *array_expr = NULL;
+                       nb_matches = sscanf(test_capture_base_fields[i].field_name,
+                                       "%[^[][%u]", field_name, &index);
+                       if (nb_matches != 2) {
+                               ret = 1;
+                               goto end;
+                       }
+                       array_expr = lttng_event_expr_event_payload_field_create(
+                               field_name);
+
+                       expr = lttng_event_expr_array_field_element_create(
+                               array_expr, index);
+                       break;
+               }
+               case FIELD_TYPE_APP_CONTEXT:
+                       fail("Application context not tested yet.");
+               default:
+                       ret = 1;
+                       goto end;
+               }
+               if (expr == NULL) {
+                       fail("Creating capture expression");
+                       ret = -1;
+                       goto end;
+               }
+               cond_status = lttng_condition_event_rule_append_capture_descriptor(
+                               condition, expr);
+               if (cond_status != LTTNG_CONDITION_STATUS_OK) {
+                       fail("Appending capture_descriptor");
+                       ret = -1;
+                       lttng_event_expr_destroy(expr);
+                       goto end;
+               }
        }
 
        }
 
-       ret = lttng_register_trigger(trigger);
-       if (ret) {
-               fail("Setup error on high trigger registration");
+       ret = 0;
+
+end:
+       return ret;
+}
+
+static int validator_notification_trigger_capture(
+               enum lttng_domain_type domain,
+               struct lttng_notification *notification,
+               const int iteration)
+{
+       int ret;
+       unsigned int capture_count, i;
+       enum lttng_evaluation_status evaluation_status;
+       enum lttng_event_field_value_status event_field_value_status;
+       const struct lttng_evaluation *evaluation;
+       const struct lttng_event_field_value *captured_fields;
+       bool at_least_one_error = false;
+
+       evaluation = lttng_notification_get_evaluation(notification);
+       if (evaluation == NULL) {
+               fail("lttng_notification_get_evaluation");
+               ret = 1;
                goto end;
        }
 
                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) {
+       /* TODO: it seems weird that lttng_evaluation_get_captured_values return
+        * INVALID if no capture were present. might be better to return
+        * something with more meaning. Another question is how we link the
+        * notion of capture and the descriptor from the perspective of a
+        * client. Is it really invalid to ask for captured value when there might
+        * not be any?
+        */
+       evaluation_status = lttng_evaluation_get_captured_values(evaluation, &captured_fields);
+       if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) {
+               diag("lttng_evaluation_get_captured_values");
+               ret = 1;
                goto end;
        }
 
                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");
+       event_field_value_status =
+               lttng_event_field_value_array_get_length(captured_fields,
+                               &capture_count);
+       if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+               assert(0 && "get count of captured field");
+       }
 
 
-       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");
+       for (i = 0; i < capture_count; i++) {
+               const struct lttng_event_field_value *captured_field = NULL;
+               validate_cb validate;
+               bool expected;
 
 
-       nc_status = lttng_notification_channel_subscribe(notification_channel, dummy_invalid_condition);
-       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Subscribing to an invalid condition");
+               diag("Validating capture \"%s\"", test_capture_base_fields[i].field_name);
+               event_field_value_status = lttng_event_field_value_array_get_element_at_index(captured_fields, i, &captured_field);
 
 
-       nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_invalid_condition);
-       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Unsubscribing from an invalid condition");
+               switch(domain) {
+               case LTTNG_DOMAIN_UST:
+                       expected = test_capture_base_fields[i].expected_ust;
+                       break;
+               case LTTNG_DOMAIN_KERNEL:
+                       expected = test_capture_base_fields[i].expected_kernel;
+                       break;
+               default:
+                       assert(0 && "Domain invalid for this test");
+               }
 
 
-       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");
+               if (!expected) {
+                       ok(event_field_value_status == LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE, "No payload captured");
+                       continue;
+               }
 
 
-       /* 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");
+               if (domain == LTTNG_DOMAIN_UST) {
+                       validate = test_capture_base_fields[i].validate_ust;
+               } else {
+                       validate = test_capture_base_fields[i].validate_kernel;
+               }
 
 
-       /* 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");
+               if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+                       const char *reason;
+                       if (event_field_value_status ==
+                                       LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) {
+                               reason = "Expected a capture but it is unavailable";
+                       } else {
+                               reason = "lttng_event_field_value_array_get_element_at_index";
+                       }
+                       fail(reason);
+                       ret = 1;
+                       goto end;
+               }
+               diag("Captured field of type %s",
+                               field_value_type_to_str(
+                                       lttng_event_field_value_get_type(captured_field)));
 
 
-       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");
+               assert(validate);
+               ret = validate(captured_field, iteration);
+               if (ret) {
+                       at_least_one_error = true;
+               }
+       }
 
 
-       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");
+       ret = at_least_one_error;
 
 
-       /* Wait for notification to happen */
-       stop_consumer(argv);
-       lttng_start_tracing(session_name);
+end:
+       return ret;
+}
 
 
-       /* Wait for high notification */
-       do {
-               nc_status = lttng_notification_channel_get_next_notification(notification_channel, &notification);
-       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
-       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;
+static void test_tracepoint_event_rule_notification_capture(
+               enum lttng_domain_type domain_type)
+{
+       enum lttng_notification_channel_status nc_status;
 
 
-       suspend_application();
-       lttng_stop_tracing_no_wait(session_name);
-       resume_consumer(argv);
-       wait_data_pending(session_name);
+       int i, ret;
+       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;
 
 
-       /*
-        * Test that communication still work even if there is notification
-        * waiting for consumption.
-        */
+       if (domain_type == LTTNG_DOMAIN_UST) {
+               pattern = "tp:tptest";
+       } else {
+               pattern = "lttng_test_filter_event";
+       }
 
 
-       nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition);
-       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe with pending notification");
+       create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0,
+                       NULL, domain_type, generate_capture_descr, &condition,
+                       &action, &trigger);
 
 
-       nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition);
-       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "subscribe with pending notification");
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       ok(notification_channel, "Notification channel object creation");
 
 
-       do {
-               nc_status = lttng_notification_channel_get_next_notification(notification_channel, &notification);
-       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
-       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;
+       nc_status = lttng_notification_channel_subscribe(
+                       notification_channel, condition);
+       ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+                       "Subscribe to tracepoint event rule condition");
 
 
-       /* Stop consumer to force a high notification */
-       stop_consumer(argv);
        resume_application();
        resume_application();
-       lttng_start_tracing(session_name);
-
-       do {
-               nc_status = lttng_notification_channel_get_next_notification(notification_channel, &notification);
-       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
-       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);
 
 
-       do {
-               nc_status = lttng_notification_channel_get_next_notification(notification_channel, &notification);
-       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
-       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;
+       /* Get 3 notifications */
+       for (i = 0; i < 3; i++) {
+               struct lttng_notification *notification = get_next_notification(
+                               notification_channel);
+               ok(notification, "Received notification");
 
 
-       stop_consumer(argv);
-       resume_application();
-       /* Stop consumer to force a high notification */
-       lttng_start_tracing(session_name);
+               /* Error */
+               if (notification == NULL) {
+                       goto end;
+               }
 
 
-       do {
-               nc_status = lttng_notification_channel_get_next_notification(notification_channel, &notification);
-       } while (nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED);
-       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;
+               ret = validator_notification_trigger_name(notification, trigger_name);
+               if (ret) {
+                       lttng_notification_destroy(notification);
+                       goto end;
+               }
 
 
-       /* Resume consumer to allow event consumption */
-       suspend_application();
-       lttng_stop_tracing_no_wait(session_name);
-       resume_consumer(argv);
-       wait_data_pending(session_name);
+               ret = validator_notification_trigger_capture(domain_type, notification, i);
+               if (ret) {
+                       lttng_notification_destroy(notification);
+                       goto end;
+               }
 
 
-       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");
+               lttng_notification_destroy(notification);
+       }
 
 end:
 
 end:
+       suspend_application();
        lttng_notification_channel_destroy(notification_channel);
        lttng_notification_channel_destroy(notification_channel);
+       lttng_unregister_trigger(trigger);
        lttng_trigger_destroy(trigger);
        lttng_action_destroy(action);
        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);
+       lttng_condition_destroy(condition);
+       return;
 }
 
 int main(int argc, const char *argv[])
 {
 }
 
 int main(int argc, const char *argv[])
 {
-       const char *session_name = NULL;
-       const char *channel_name = NULL;
+       int test_scenario;
        const char *domain_type_string = NULL;
        enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
 
        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);
+       if (argc < 5) {
+               fail("Missing test scenario, domain type, pid, or application state file argument(s)");
                goto error;
        }
 
                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];
+       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_UST", domain_type_string)) {
                domain_type = LTTNG_DOMAIN_UST;
@@ -736,13 +2498,172 @@ int main(int argc, const char *argv[])
                goto error;
        }
 
                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);
+       /*
+        * 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(44);
+
+               /* 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(25);
+
+               /*
+                * 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(13);
+               /* 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(25);
+               /* 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(13);
+
+               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;
+       }
+       case 7:
+       {
+               switch(domain_type) {
+               case LTTNG_DOMAIN_UST:
+                       plan_tests(222);
+                       break;
+               case LTTNG_DOMAIN_KERNEL:
+                       plan_tests(216);
+                       break;
+               default:
+                       assert(0);
+               }
+
+               diag("Test tracepoint event rule notification captures for domain %s",
+                               domain_type_string);
+               test_tracepoint_event_rule_notification_capture(domain_type);
+
+               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();
 }
 error:
        return exit_status();
 }
diff --git a/tests/regression/tools/notification/sessiond_testpoints.c b/tests/regression/tools/notification/sessiond_testpoints.c
new file mode 100644 (file)
index 0000000..90b3527
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#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 *trigger_notif_consumption_state;;
+
+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);
+}
+
+/*
+ */
+int __testpoint_sessiond_thread_notification(void);
+int __testpoint_sessiond_thread_notification(void)
+{
+       int ret = 0;
+       const char *pause_pipe_path_prefix;
+
+       pause_pipe_path_prefix = lttng_secure_getenv(
+                       "TRIGGER_PAUSE_PIPE_PATH");
+       if (!pause_pipe_path_prefix) {
+               ret = -1;
+               goto end;
+       }
+
+       trigger_notif_consumption_state = dlsym(NULL, "trigger_consumption_paused");
+       assert(trigger_notif_consumption_state);
+
+       ret = asprintf(&pause_pipe_path, "%s", pause_pipe_path_prefix);
+       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_sessiond_handle_trigger_event_pipe(void);
+int __testpoint_sessiond_handle_trigger_event_pipe(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) {
+               *trigger_notif_consumption_state = !!value;
+               DBG("Message received on pause pipe: %s data consumption",
+                               *trigger_notif_consumption_state ? "paused" : "resumed");
+       }
+end:
+       return ret;
+}
diff --git a/tests/regression/tools/notification/test_notification_kernel b/tests/regression/tools/notification/test_notification_kernel
deleted file mode 100755 (executable)
index 1eb0d10..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 Jonathan Rajotte <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..3214f90
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte <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_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_buffer_usage_notification
+{
+       local event_name="lttng_test_filter_event"
+       local trace_path
+       local page_size
+       local consumerd_pipe=()
+
+       trace_path=$(mktemp -d)
+       page_size=$(getconf PAGE_SIZE)
+
+       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=$!
+
+       # 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
+
+       "$CURDIR/notification" 2 LTTNG_DOMAIN_KERNEL $APP_PID "$TESTAPP_STATE_PATH" \
+               $SESSION_NAME $CHANNEL_NAME "${consumerd_pipe[@]}"
+
+       destroy_lttng_session_notap $SESSION_NAME
+
+       kill -SIGUSR2 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+
+       validate_lttng_modules_present
+
+
+       modprobe lttng-test
+
+       # Used on sessiond launch.
+       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
+       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
+
+rm -rf "$TMPDIR"
diff --git a/tests/regression/tools/notification/test_notification_kernel_capture b/tests/regression/tools/notification/test_notification_kernel_capture
new file mode 100755 (executable)
index 0000000..9b6e7ac
--- /dev/null
@@ -0,0 +1,55 @@
+#!/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)
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_basic_error_path
+{
+       kernel_event_generator_run_once_per_transition generate_filter_events \
+               "$TESTAPP_STATE_PATH" 10 &
+       APP_PID=$!
+
+       "$CURDIR/notification" 7 LTTNG_DOMAIN_KERNEL $APP_PID \
+               "$TESTAPP_STATE_PATH"
+
+       kill -SIGUSR2 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+
+if [ "$(id -u)" == "0" ]; then
+       validate_lttng_modules_present
+
+       modprobe lttng-test
+
+       start_lttng_sessiond_notap
+
+       test_basic_error_path
+
+       stop_lttng_sessiond_notap
+       rmmod lttng-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 "$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..ae39e9e
--- /dev/null
@@ -0,0 +1,55 @@
+#!/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)
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_basic_error_path
+{
+       kernel_event_generator_run_once_per_transition generate_filter_events \
+               "$TESTAPP_STATE_PATH" 10 &
+       APP_PID=$!
+
+       "$CURDIR/notification" 1 LTTNG_DOMAIN_KERNEL $APP_PID \
+               "$TESTAPP_STATE_PATH"
+
+       kill -SIGUSR2 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+
+if [ "$(id -u)" == "0" ]; then
+       validate_lttng_modules_present
+
+       modprobe lttng-test
+
+       start_lttng_sessiond_notap
+
+       test_basic_error_path
+
+       stop_lttng_sessiond_notap
+       rmmod lttng-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 "$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..1bc702b
--- /dev/null
@@ -0,0 +1,51 @@
+#!/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)
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+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 -SIGUSR2 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+       validate_lttng_modules_present
+
+       modprobe lttng-test
+
+       start_lttng_sessiond_notap
+
+       test_kernel_instrumentation_notification
+
+       stop_lttng_sessiond_notap
+       rmmod lttng-test
+
+else
+       # Kernel tests are skipped.
+       plan_tests $NUM_TESTS
+       skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+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..cf6a0e0
--- /dev/null
@@ -0,0 +1,52 @@
+#!/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)
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_kernel_syscall_notification
+{
+       kernel_event_generator_run_once_per_transition generate_syscalls \
+               "$TESTAPP_STATE_PATH" 10 /proc/cpuinfo /proc/cmdline &
+       APP_PID=$!
+
+       # Pass the syscall_generator_file for filtering
+       "$CURDIR/notification" 5 LTTNG_DOMAIN_KERNEL $APP_PID \
+               "$TESTAPP_STATE_PATH"
+
+
+       kill -SIGUSR2 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+       validate_lttng_modules_present
+
+       start_lttng_sessiond_notap
+
+       test_kernel_syscall_notification
+
+       stop_lttng_sessiond_notap
+
+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 "$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..6a50869
--- /dev/null
@@ -0,0 +1,49 @@
+#!/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)
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_kernel_userspace_probe_notification
+{
+       kernel_event_generator_run_once_per_transition userspace_probe_testapp "$TESTAPP_STATE_PATH" 10 &
+       APP_PID=$!
+
+       "$CURDIR/notification" 6 LTTNG_DOMAIN_KERNEL \
+               $APP_PID "$TESTAPP_STATE_PATH" \
+               "$USERSPACE_PROBE_ELF_TESTAPP_BIN" "test_function"
+
+       kill -SIGUSR2 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+       validate_lttng_modules_present
+
+       start_lttng_sessiond_notap
+
+       test_kernel_userspace_probe_notification
+
+       stop_lttng_sessiond_notap
+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 "$TMPDIR"
index dae482ce3e5d19b4605d4d84fbbd9b9f05d0bd9d..f2cbf43a86e9eba9f54c2828a7eff9cd042e2e96 100755 (executable)
@@ -32,6 +32,7 @@ NUM_TEST_KERNEL=50
 NUM_TESTS=$(($NUM_TEST_UST + $NUM_TEST_KERNEL))
 
 source $TESTDIR/utils/utils.sh
 NUM_TESTS=$(($NUM_TEST_UST + $NUM_TEST_KERNEL))
 
 source $TESTDIR/utils/utils.sh
+source $CURDIR/util_event_generator.sh
 
 consumerd_pipe=()
 file_sync_after_first_event=$(mktemp -u)
 
 consumerd_pipe=()
 file_sync_after_first_event=$(mktemp -u)
@@ -43,54 +44,6 @@ print_test_banner "$TEST_DESC"
 
 app_pids=()
 
 
 app_pids=()
 
-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
-               fi
-       done
-}
-
-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
-       trap "exit" SIGTERM SIGINT
-       while (true); do
-               if [[ $ust_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
-                       taskset  -c 0 $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
-               fi
-       done
-}
-
 function start_client {
        local pid=-1
        local output_file=$1
 function start_client {
        local pid=-1
        local output_file=$1
@@ -300,12 +253,12 @@ function test_multi_app ()
 function test_multi_app_ust ()
 {
        diag "Multi client app UST notification"
 function test_multi_app_ust ()
 {
        diag "Multi client app UST notification"
-       ust_event_generator $TESTAPP_STATE_FILE &
+       ust_event_generator "$TESTAPP_BIN" "$TESTAPP_STATE_FILE" &
        local generator_pid=$!
 
        test_multi_app ust $generator_pid
 
        local generator_pid=$!
 
        test_multi_app ust $generator_pid
 
-       kill -s SIGTERM $generator_pid 2> /dev/null
+       kill -s SIGUSR2 $generator_pid 2> /dev/null
        wait $generator_pid 2> /dev/null
        rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
 }
        wait $generator_pid 2> /dev/null
        rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
 }
@@ -315,13 +268,13 @@ function test_multi_app_kernel ()
        diag "Multi client app kernel notification"
        modprobe lttng-test
 
        diag "Multi client app kernel notification"
        modprobe lttng-test
 
-       kernel_event_generator $TESTAPP_STATE_FILE &
+       kernel_event_generator generate_filter_events $TESTAPP_STATE_FILE &
        local generator_pid=$!
 
        test_multi_app kernel $generator_pid
 
 
        local generator_pid=$!
 
        test_multi_app kernel $generator_pid
 
 
-       kill -s SIGTERM $generator_pid 2> /dev/null
+       kill -s SIGUSR2 $generator_pid 2> /dev/null
        wait $generator_pid 2> /dev/null
        rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
 
        wait $generator_pid 2> /dev/null
        rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
 
@@ -333,12 +286,12 @@ function test_on_register_evaluation_ust ()
        diag "On register notification UST"
 
        # Start app in infinite loop
        diag "On register notification UST"
 
        # Start app in infinite loop
-       ust_event_generator $TESTAPP_STATE_FILE &
+       ust_event_generator "$TESTAPP_BIN" "$TESTAPP_STATE_FILE" &
        local generator_pid=$!
 
        test_on_register_evaluation ust $generator_pid
 
        local generator_pid=$!
 
        test_on_register_evaluation ust $generator_pid
 
-       kill -s SIGTERM $generator_pid 2> /dev/null
+       kill -s SIGUSR2 $generator_pid 2> /dev/null
        wait $generator_pid 2> /dev/null
        rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
 
        wait $generator_pid 2> /dev/null
        rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
 
@@ -350,13 +303,13 @@ function test_on_register_evaluation_kernel()
 
        modprobe lttng-test
 
 
        modprobe lttng-test
 
-       kernel_event_generator $TESTAPP_STATE_FILE &
+       kernel_event_generator generate_filter_events $TESTAPP_STATE_FILE &
        local generator_pid=$!
 
        test_on_register_evaluation kernel $generator_pid
 
 
        local generator_pid=$!
 
        test_on_register_evaluation kernel $generator_pid
 
 
-       kill -s SIGTERM $generator_pid 2> /dev/null
+       kill -s SIGUSR2 $generator_pid 2> /dev/null
        wait $generator_pid 2> /dev/null
        rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
 
        wait $generator_pid 2> /dev/null
        rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
 
@@ -447,7 +400,7 @@ function test_on_register_evaluation ()
        destroy_lttng_session_ok $SESSION_NAME
        stop_lttng_sessiond
 
        destroy_lttng_session_ok $SESSION_NAME
        stop_lttng_sessiond
 
-       kill -s SIGTERM $generator_pid 2> /dev/null
+       kill -s SIGUSR2 $generator_pid 2> /dev/null
        wait $generator_pid 2> /dev/null
 
        for pipe in "${consumerd_pipe[@]}"; do
        wait $generator_pid 2> /dev/null
 
        for pipe in "${consumerd_pipe[@]}"; do
diff --git a/tests/regression/tools/notification/test_notification_trigger_discarded_count b/tests/regression/tools/notification/test_notification_trigger_discarded_count
new file mode 100755 (executable)
index 0000000..241e6fc
--- /dev/null
@@ -0,0 +1,262 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+TESTAPP_NAME="gen-ust-events"
+TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
+
+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_sessiond.so")
+
+SH_TAP=1
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}"
+FULL_LTTNG_SESSIOND_BIN="${TESTDIR}/../src/bin/lttng-sessiond/lttng-sessiond"
+
+UST_NUM_TESTS=15
+KERNEL_NUM_TESTS=14
+NUM_TESTS=$(($UST_NUM_TESTS + $KERNEL_NUM_TESTS))
+
+plan_tests $NUM_TESTS
+
+function test_kernel_trigger_discarded_count
+{
+       local sessiond_pipe=()
+       local trigger_name="my_trigger"
+       local list_triggers_stdout=$(mktemp -t list_triggers_stdout.XXXXXX)
+
+       # Used on sessiond launch.
+       LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \
+               TRIGGER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \
+               LD_PRELOAD=${TESTPOINT}"
+
+       start_lttng_sessiond_notap
+
+       # This is needed since the testpoint create a pipe with the sessiond
+       # type suffixed.
+       for f in "$TESTPOINT_BASE_PATH"*; do
+               sessiond_pipe+=("$f")
+       done
+
+       "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" \
+               --condition on-event --kernel lttng_test_filter_event \
+               --action notify > /dev/null > /dev/null
+       ok $? "Adding \`on-event\` kernel trigger successful"
+
+       "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout"
+
+       # Confirm that the discarded notification line is present.
+       cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0"
+       ok $? "No discarded tracer notification"
+
+       # Stop consumption of trigger tracer notifications.
+       echo -n 1 > $sessiond_pipe
+
+       # The trigger ring buffer configuration is currently made of 16 4096
+       # bytes subbuffers. Each kernel notification is at least 42 bytes long.
+       # To fill it, we need to generate (16 * 4096)/42 = 1561 notifications.
+       # That number is a bit larger than what we need since some of the space
+       # is lost in subbuffer boundaries.
+       echo -n "200000" > /proc/lttng-test-filter-event
+
+       "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout"
+
+       # Confirm that the discarded notification line is present. To avoid
+       # false positive.
+       cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded"
+       ok $? "Tracer notification discarded line printed"
+
+       # Confirm that the number of tracer notifications discarded is not zero.
+       cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0"
+       isnt $? 0 "Discarded tracer notification number non-zero as expected"
+
+       "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" > /dev/null
+       ok $? "Removing \`on-event\` kernel trigger successful"
+
+       # Confirm that no trigger is enabled.
+       list_triggers_line_count=$("$FULL_LTTNG_BIN" list-triggers | wc -l)
+       is "$list_triggers_line_count" "0" "No \`on-event\` kernel trigger enabled as expected"
+
+       # Enable another trigger and list it to confirm the counter was cleared.
+       "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" \
+               --condition on-event --kernel lttng_test_filter_event \
+               --action notify > /dev/null
+       ok $? "Adding another \`on-event\` kernel trigger successful"
+
+       # Confirm that the discarded notification line is present.
+       "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout"
+       cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0"
+       ok $? "No discarded tracer notification"
+
+       "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" > /dev/null
+       ok $? "Removing \`on-event\` kernel trigger successful"
+
+
+       stop_lttng_sessiond_notap
+
+       unset LTTNG_SESSIOND_ENV_VARS
+
+       rm -f "$list_triggers_stdout"
+}
+
+function test_kernel_trigger_discarded_count_max_bucket
+{
+       "$FULL_LTTNG_SESSIOND_BIN" -d --trigger-error-number-of-bucket=3
+
+       for i in $(seq 3); do
+               "$FULL_LTTNG_BIN" add-trigger --id "$i" \
+                       --condition on-event --kernel my_event_that_doesnt_need_to_really_exist_$i \
+                       --action notify  > /dev/null
+               ok $? "Enabling \`on-event\` kernel trigger $i succeeds"
+       done
+
+       for i in $(seq 4 5); do
+               "$FULL_LTTNG_BIN" add-trigger --id "$i" \
+                       --condition on-event --kernel my_event_that_doesnt_need_to_really_exist_$i \
+                       --action notify  > /dev/null 2>&1
+               isnt $? 0 "Enabling \`on-event\` kernel trigger $i fails as expected"
+       done
+
+       stop_lttng_sessiond_notap
+}
+
+function test_ust_trigger_discarded_count
+{
+       local sessiond_pipe=()
+       local trigger_name="my_trigger"
+       local list_triggers_stdout=$(mktemp -t list_triggers_stdout.XXXXXX)
+       local NR_ITER=2000
+       local NR_USEC_WAIT=0
+
+
+       # Used on sessiond launch.
+       LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \
+               TRIGGER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \
+               LD_PRELOAD=${TESTPOINT}"
+
+       start_lttng_sessiond_notap
+
+       # This is needed since the testpoint create a pipe with the sessiond
+       # type suffixed.
+       for f in "$TESTPOINT_BASE_PATH"*; do
+               sessiond_pipe+=("$f")
+       done
+
+       "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" \
+               --condition on-event --userspace tp:tptest \
+               --action notify > /dev/null
+       ok $? "Adding \`on-event\` userspace trigger successful"
+
+       "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout"
+
+       # Confirm that the discarded notification line is present.
+       cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0"
+       ok $? "No discarded tracer notification"
+
+       # Stop consumption of trigger tracer notifications.
+       echo -n 1 > $sessiond_pipe
+
+       # The trigger ring buffer configuration is currently made of 16 4096
+       # bytes subbuffers. Each userspace notification is at least 42 bytes long.
+       # To fill it, we need to generate (16 * 4096)/42 = 1561 notifications.
+       # That number is a bit larger than what we need since some of the space
+       # is lost in subbuffer boundaries.
+       $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT
+       ok $? "Generating $NR_ITER tracer notifications"
+
+       "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout"
+
+       # Confirm that the discarded notification line is present. To avoid
+       # false positive.
+       cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded"
+       ok $? "Tracer notification discarded line printed"
+
+       # Confirm that the number of tracer notifications discarded is not zero.
+       cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0"
+       isnt $? 0 "Discarded tracer notification number non-zero as expected"
+
+       # Remove the trigger.
+       "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" > /dev/null
+       ok $? "Removing \`on-event\` userspace trigger successful"
+
+       # Confirm that no trigger is enabled.
+       list_triggers_line_count=$("$FULL_LTTNG_BIN" list-triggers | wc -l)
+       is "$list_triggers_line_count" "0" "No \`on-event\` userspace trigger enabled as expected"
+
+       # Enable another trigger and list it to confirm the counter was cleared.
+       "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" \
+               --condition on-event --userspace tp:tptest \
+               --action notify > /dev/null
+       ok $? "Adding another \`on-event\` userspace trigger successful"
+
+       # Confirm that the discarded notification line is present.
+       "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout"
+       cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0"
+       ok $? "No discarded tracer notification"
+
+       "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" > /dev/null
+       ok $? "Removing \`on-event\` userspace trigger successful"
+
+       stop_lttng_sessiond_notap
+
+       unset LTTNG_SESSIOND_ENV_VARS
+
+       rm -f "$list_triggers_stdout"
+}
+function test_ust_trigger_discarded_count_max_bucket
+{
+       "$FULL_LTTNG_SESSIOND_BIN" -d --trigger-error-number-of-bucket=3
+
+       for i in $(seq 3); do
+               "$FULL_LTTNG_BIN" add-trigger --id "$i" \
+                       --condition on-event --userspace my_event_that_doesnt_need_to_really_exist_$i \
+                       --action notify > /dev/null
+               ok $? "Enabling \`on-event\` userspace trigger $i succeeds"
+       done
+
+       for i in $(seq 4 5); do
+               "$FULL_LTTNG_BIN" add-trigger --id "$i" \
+                       --condition on-event --userspace my_event_that_doesnt_need_to_really_exist_$i \
+                       --action notify > /dev/null 2>&1
+               isnt $? 0 "Enabling \`on-event\` userspace trigger $i fails as expected"
+       done
+
+       stop_lttng_sessiond_notap
+}
+
+test_ust_trigger_discarded_count
+test_ust_trigger_discarded_count_max_bucket
+
+if [ "$(id -u)" == "0" ]; then
+
+       validate_lttng_modules_present
+
+       modprobe lttng-test
+
+       test_kernel_trigger_discarded_count
+
+       test_kernel_trigger_discarded_count_max_bucket
+
+       modprobe --remove lttng-test
+
+       rm -rf "${sessiond_pipe[@]}" 2> /dev/null
+else
+       # Kernel tests are skipped.
+       skip 0 "Root access is needed. Skipping all kernel notification tests." $KERNEL_NUM_TESTS
+fi
+
+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 57ae52c..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 Jonathan Rajotte <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..a8b96bb
--- /dev/null
@@ -0,0 +1,74 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte <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"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_buffer_usage_notification
+{
+       consumerd_pipe=()
+       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 -SIGUSR2 $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_capture b/tests/regression/tools/notification/test_notification_ust_capture
new file mode 100755 (executable)
index 0000000..5003c28
--- /dev/null
@@ -0,0 +1,40 @@
+#!/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)
+
+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"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_basic_error_path
+{
+       ust_event_generator_run_once_per_transition \
+               "$GEN_UST_EVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" 3 10 &
+       APP_PID=$!
+
+       "$CURDIR/notification" 7 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH"
+
+       kill -SIGUSR2 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+start_lttng_sessiond_notap
+
+test_basic_error_path
+
+stop_lttng_sessiond_notap
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..57de30d
--- /dev/null
@@ -0,0 +1,39 @@
+#!/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)
+
+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"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_basic_error_path
+{
+       ust_event_generator_run_once_per_transition "$GEN_UST_EVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" 5 5 &
+       APP_PID=$!
+
+       "$CURDIR/notification" 1 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH"
+
+       kill -SIGUSR2 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+start_lttng_sessiond_notap
+
+test_basic_error_path
+
+stop_lttng_sessiond_notap
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..12ae728
--- /dev/null
@@ -0,0 +1,41 @@
+#!/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)
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+
+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")
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_event_rule_condition_exclusion_notification
+{
+       ust_event_generator_run_once_per_transition "$GEN_UST_NEVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" 5 5 &
+       APP_PID=$!
+
+       "$CURDIR/notification" 3 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH"
+
+       kill -SIGUSR2 $APP_PID
+       wait $APP_PID 2> /dev/null
+}
+
+start_lttng_sessiond_notap
+
+test_event_rule_condition_exclusion_notification
+
+stop_lttng_sessiond_notap
+
+rm -rf "$TMPDIR"
diff --git a/tests/regression/tools/notification/util_event_generator.sh b/tests/regression/tools/notification/util_event_generator.sh
new file mode 100644 (file)
index 0000000..cafd064
--- /dev/null
@@ -0,0 +1,195 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+GENERATOR_CURDIR=$(dirname "$0")/
+GENERATOR_TESTDIR=$GENERATOR_CURDIR/../../../
+TESTAPP_PATH=${TESTAPP_PATH:-"$GENERATOR_TESTDIR/utils/testapp"}
+
+SYSCALL_TESTAPP_NAME=${SYSCALL_TESTAPP_NAME:-"gen-syscall-events"}
+SYSCALL_TESTAPP_BIN=${SYSCALL_TESTAPP_BIN:-"$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME"}
+
+USERSPACE_PROBE_ELF_TESTAPP_NAME=${USERSPACE_PROBE_ELF_TESTAPP_NAME:-"userspace-probe-elf-binary"}
+USERSPACE_PROBE_ELF_TESTAPP_BIN=${USERSPACE_PROBE_ELF_TESTAPP_BIN:-"$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME"}
+
+function generate_filter_events
+{
+       local nr=$1
+       /bin/echo -n "$nr" > /proc/lttng-test-filter-event 2> /dev/null
+}
+
+function generate_syscalls
+{
+       local nr=$1
+       shift
+
+       for i in $(seq 1 "$nr"); do
+               # Pass /dev/null so to generate the syscall right away.
+               $SYSCALL_TESTAPP_BIN /dev/null "$@"
+       done
+}
+
+function userspace_probe_testapp
+{
+       local nr=$1
+       shift 
+
+       for i in $(seq 1 "$nr"); do
+               $USERSPACE_PROBE_ELF_TESTAPP_BIN "$@"
+       done
+}
+
+function ust_event_generator_toggle_state
+{
+       ust_event_generator_suspended=$((ust_event_generator_suspended==0))
+}
+
+function generator_quit
+{
+       generator_quit=0
+}
+
+# Note: Only one generator can be used at a time per domain type
+function ust_event_generator_run_once_per_transition
+{
+       # Used by the signal trap
+       ust_event_generator_suspended=0
+       # Used by the signal trap for SIGUSR2 to end the generator
+       generator_quit=1
+
+       local test_app=$1
+       local state_file=$2
+       local nr_iter=$3
+       local nr_usec_wait=$4
+       local run=false
+
+       # Pass any of the remaining arguments to the generator.
+       shift 4
+
+       trap ust_event_generator_toggle_state SIGUSR1
+       trap generator_quit SIGUSR2
+
+       while [ $generator_quit -ne 0  ]; do
+               if [[ $ust_event_generator_suspended -eq "1" ]]; then
+                       touch "$state_file"
+                       # Reset the "run" state
+                       run=true
+                       sleep 0.5
+               elif [ "$run" = true ]; then
+                       taskset  -c 0 "$test_app" -i "$nr_iter" -w "$nr_usec_wait" "$@"> /dev/null 2>&1
+                       run=false;
+                       if [[ -f $state_file ]]; then
+                               rm -rf "$state_file" 2> /dev/null
+                       fi
+               else
+                       # Wait for a "suspend" to reset the run state
+                       sleep 0.1
+               fi
+       done
+}
+
+# Note: Only one generator can be used at a time per domain type
+function ust_event_generator
+{
+       # Used by the signal trap
+       ust_event_generator_suspended=0
+       # Used by the signal trap for SIGUSR2 to end the generator
+       generator_quit=1
+
+       local test_app=$1
+       local state_file=$2
+       local nr_iter=1000
+       local nr_usec_wait=5
+
+       # Pass any of the remaining arguments to the generator.
+       shift 2
+
+       trap ust_event_generator_toggle_state SIGUSR1
+       trap generator_quit SIGUSR2
+
+       while [ $generator_quit -ne 0 ]; do
+               if [[ $ust_event_generator_suspended -eq "1" ]]; then
+                       touch "$state_file"
+                       # Reset the "run" state
+                       sleep 0.5
+               else
+                       taskset  -c 0 "$test_app" -i $nr_iter -w $nr_usec_wait "$@" > /dev/null 2>&1
+                       if [[ -f $state_file ]]; then
+                               rm -rf "$state_file" 2> /dev/null
+                       fi
+               fi
+       done
+}
+
+function kernel_event_generator_toggle_state
+{
+       kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
+
+}
+
+function kernel_event_generator_run_once_per_transition
+{
+       # Used by the signal trap
+       kernel_event_generator_suspended=0
+       # Used by the signal trap for SIGUSR2 to end the generator
+       generator_quit=1
+
+       local generator=$1
+       local state_file=$2
+       local nr_iter=$3
+
+       # Pass any of the remaining arguments to the generator.
+       shift 3
+
+       local run=false
+       trap kernel_event_generator_toggle_state SIGUSR1
+       trap generator_quit SIGUSR2
+
+       while [ $generator_quit -ne 0 ]; do
+               if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+                       touch "$state_file"
+                       run=true
+                       sleep 0.5
+               elif [ "$run" = true ]; then
+                       $generator "$nr_iter" "$@"
+                       run=false
+                       if [[ -f $state_file ]]; then
+                               rm "$state_file" 2> /dev/null
+                       fi
+               else
+                       # Wait for a "suspend" to reset the run state
+                       sleep 0.1
+               fi
+       done
+}
+
+function kernel_event_generator
+{
+       # Used by the signal trap
+       kernel_event_generator_suspended=0
+       # Used by the signal trap for SIGUSR2 to end the generator
+       generator_quit=1
+
+       local generator=$1
+       local state_file=$2
+
+       # Pass any of the remaining arguments to the generator.
+       shift 2
+
+       trap kernel_event_generator_toggle_state SIGUSR1
+       trap generator_quit SIGUSR2
+
+       while [ $generator_quit -ne 0 ]; do
+               if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+                       touch "$state_file"
+                       sleep 0.5
+               else
+                       $generator "10" "$@"
+                       if [[ -f $state_file ]]; then
+                               rm "$state_file" 2> /dev/null
+                       fi
+               fi
+       done
+}
diff --git a/tests/regression/tools/trigger/Makefile.am b/tests/regression/tools/trigger/Makefile.am
new file mode 100644 (file)
index 0000000..11c546e
--- /dev/null
@@ -0,0 +1,56 @@
+AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_srcdir)/tests -I$(top_srcdir)/tests/utils/ -I$(srcdir)
+AM_LDFLAGS =
+
+SUBDIRS=utils start-stop
+
+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..df540d2
--- /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_add_exclusion(event_rule, exclusions[0]);
+       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/start-stop/Makefile.am b/tests/regression/tools/trigger/start-stop/Makefile.am
new file mode 100644 (file)
index 0000000..c28007d
--- /dev/null
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+noinst_SCRIPTS = test_start_stop
+
+EXTRA_DIST = test_start_stop
+
+all-local:
+       @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+               for script in $(EXTRA_DIST); do \
+                       cp -f $(srcdir)/$$script $(builddir); \
+               done; \
+       fi
+
+clean-local:
+       @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+               for script in $(EXTRA_DIST); do \
+                       rm -f $(builddir)/$$script; \
+               done; \
+       fi
diff --git a/tests/regression/tools/trigger/start-stop/test_start_stop b/tests/regression/tools/trigger/start-stop/test_start_stop
new file mode 100755 (executable)
index 0000000..935d630
--- /dev/null
@@ -0,0 +1,198 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+TEST_DESC="Triggers - Start and stop actions"
+
+CURDIR=$(dirname "$0")/
+TESTDIR=${CURDIR}/../../../..
+
+# shellcheck source=../../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events"
+GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME"
+FULL_LTTNG_BIN="$TESTDIR/../src/bin/lttng/$LTTNG_BIN"
+NOTIFICATION_CLIENT_BIN="$CURDIR/../utils/notification-client"
+NUM_TESTS=18
+
+NR_ITER=5
+NR_USEC_WAIT=5
+
+function lttng_add_trigger_ust()
+{
+       local expected_to_fail="$1"
+       local trigger_name="$2"
+       shift 2
+
+       "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" "$@" 1> /dev/null 2> /dev/null
+       ret=$?
+       if [[ $expected_to_fail -eq "1" ]]; then
+               test "$ret" -ne "0"
+               ok $? "Add trigger $trigger_name failed as expected"
+       else
+               ok $ret "Add trigger $trigger_name"
+       fi
+}
+
+function lttng_remove_trigger_ust()
+{
+       local expected_to_fail="$1"
+       local trigger_name="$2"
+
+       "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" 1> /dev/null 2> /dev/null
+       ret=$?
+       if [[ $expected_to_fail -eq "1" ]]; then
+               test "$ret" -ne "0"
+               ok $? "Remove trigger $trigger_name failed as expected"
+       else
+               ok $ret "Remove trigger $trigger_name"
+       fi
+}
+
+function lttng_add_trigger_ust_ok()
+{
+       lttng_add_trigger_ust 0 "$@"
+}
+
+function lttng_remove_trigger_ust_ok()
+{
+       lttng_remove_trigger_ust 0 "$@"
+}
+
+function lttng_session_is_active()
+{
+       local SESSION_NAME="$1"
+       "$FULL_LTTNG_BIN" list "$SESSION_NAME" | grep "Tracing session" | grep -q "\[active\]"
+
+       ok $ret "Session \"$SESSION_NAME\" is active"
+}
+
+function lttng_session_is_inactive()
+{
+       local SESSION_NAME="$1"
+       "$FULL_LTTNG_BIN" list "$SESSION_NAME" | grep "Tracing session" | grep -q "\[inactive\]"
+
+       ok $ret "Session \"$SESSION_NAME\" is inactive"
+}
+
+function test_start_session_action()
+{
+       local SESSION_NAME="my_triggered_session"
+       local TRIGGER_NAME="trigger1"
+       local TRACE_PATH=$(mktemp -d test-start-action-trace.XXXXXX)
+       local SYNC_AFTER_NOTIF_REGISTER_PATH=$(mktemp test-notif-register.XXXXXX)
+
+       diag "Start session action"
+
+       create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
+
+       enable_ust_lttng_event_ok $SESSION_NAME "tp:tptest"
+
+       lttng_session_is_inactive $SESSION_NAME
+
+       # Add `start-session` action to an event-rule condition _followed_ by
+       # a `notify` action.
+       lttng_add_trigger_ust_ok \
+               $TRIGGER_NAME \
+               --condition on-event -u "tp:tptest" \
+               --action start-session $SESSION_NAME \
+               --action notify
+
+       # Launch notification listener.
+       $NOTIFICATION_CLIENT_BIN \
+               --trigger $TRIGGER_NAME \
+               --sync-after-notif-register "$SYNC_AFTER_NOTIF_REGISTER_PATH"
+       notif_client_pid=$!
+
+       while [ ! -f "${SYNC_AFTER_NOTIF_REGISTER_PATH}" ]; do
+               sleep 0.5
+       done
+
+       # Artificially produce the desired event-rule condition.
+       $GEN_UST_EVENTS_TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
+
+       # notification-client will exit once it receives a notification.
+       wait $notif_client_pid
+       test "$?" -eq "0"
+       ok $? "notification client exited successfully"
+
+       # Test that the session as started.
+       lttng_session_is_active $SESSION_NAME
+
+       # Tearing down.
+       lttng_remove_trigger_ust_ok $TRIGGER_NAME
+       stop_lttng_tracing_ok $SESSION_NAME
+       destroy_lttng_session_ok $SESSION_NAME
+
+       rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH"
+       rm -rf "$TRACE_PATH"
+}
+
+function test_stop_session_action()
+{
+       local SESSION_NAME="my_triggered_session"
+       local TRIGGER_NAME="trigger1"
+       local TRACE_PATH=$(mktemp -d test-stop-action-trace.XXXXXX)
+       local SYNC_AFTER_NOTIF_REGISTER_PATH=$(mktemp test-notif-register.XXXXXX)
+
+       diag "Stop session action"
+       create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
+
+       enable_ust_lttng_event_ok $SESSION_NAME "tp:tptest"
+
+       start_lttng_tracing_ok $SESSION_NAME
+
+       lttng_session_is_active $SESSION_NAME
+
+       # Add `stop-session` action to an event-rule condition _followed_ by
+       # a `notify` action.
+       lttng_add_trigger_ust_ok \
+               $TRIGGER_NAME \
+               --condition on-event -u "tp:tptest" \
+               --action stop-session $SESSION_NAME \
+               --action notify
+
+       # Launch notification listener.
+       $NOTIFICATION_CLIENT_BIN \
+               --trigger $TRIGGER_NAME \
+               --sync-after-notif-register "$SYNC_AFTER_NOTIF_REGISTER_PATH"
+       notif_client_pid=$!
+
+       while [ ! -f "${SYNC_AFTER_NOTIF_REGISTER_PATH}" ]; do
+               sleep 0.5
+       done
+
+       # Artificially produce the desired event-rule condition.
+       $GEN_UST_EVENTS_TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
+
+       # notification-client will exit once it receives a notification.
+       wait $notif_client_pid
+       test "$?" -eq "0"
+       ok $? "notification client exited successfully"
+
+       # Test that the session as started.
+       lttng_session_is_inactive $SESSION_NAME
+
+       # Tearing down.
+       lttng_remove_trigger_ust_ok $TRIGGER_NAME
+       destroy_lttng_session_ok $SESSION_NAME
+
+       rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH"
+       rm -rf "$TRACE_PATH"
+}
+
+ # MUST set TESTDIR before calling those functions
+plan_tests $NUM_TESTS
+
+print_test_banner "$TEST_DESC"
+
+start_lttng_sessiond_notap
+
+test_start_session_action
+test_stop_session_action
+
+stop_lttng_sessiond_notap
diff --git a/tests/regression/tools/trigger/test_add_trigger_cli b/tests/regression/tools/trigger/test_add_trigger_cli
new file mode 100755 (executable)
index 0000000..467d942
--- /dev/null
@@ -0,0 +1,413 @@
+#!/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 216
+
+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/.libs/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" 9 || {
+       test_success "--condition on-event probe by symbol" \
+               --condition on-event -k --probe=lttng_channel_enable my_channel_enable \
+               --action notify
+
+       channel_enable_addr=$(grep ' t lttng_channel_enable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ')
+       channel_disable_addr=$(grep ' t lttng_channel_disable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ')
+
+       # We need to find a valid offset.
+       base_symbol=""
+       offset=0
+       if [[ 0x$channel_enable_addr -lt 0x$channel_disable_addr ]]; then
+               base_symbol="lttng_channel_enable"
+               offset=$(( 0x$channel_disable_addr - 0x$channel_enable_addr ))
+       else
+               base_symbol="lttng_channel_disable"
+               offset=$(( 0x$channel_enable_addr - 0x$channel_disable_addr ))
+       fi
+
+       offset_hex="0x$(printf '%x' $offset)"
+
+       test_success "--condition on-event probe by symbol with offset" \
+               --condition on-event -k --probe="${base_symbol}+${offset_hex}" my_$base_symbol \
+               --action notify
+
+       test_success "--condition on-event probe by address" \
+               --condition on-event -k "--probe=0x${channel_enable_addr}" my_channel_enable \
+               --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
+
+test_success "--action notify --capture foo" \
+       --condition on-event some-event-notify-foo -u \
+       --capture foo --action notify
+
+test_success "--action notify --capture foo[2]" \
+       --condition on-event some-event-notify-foo2 -u \
+       --capture 'foo[2]' --action notify
+
+test_success '--action notify --capture $ctx.foo' \
+       --condition on-event some-event-notify-ctx-foo -u \
+       --capture '$ctx.foo' --action notify
+
+test_success '--action notify --capture $ctx.foo[2]' \
+       --condition on-event some-event-notify-ctx-foo2 -u \
+       --capture '$ctx.foo[2]' --action notify
+
+test_success '--action notify --capture $app.prov:type' \
+       --condition on-event some-event-notify-app-prov-type -u \
+       --capture '$app.prov:type' --action notify
+
+test_success '--action notify --capture $app.prov:type[2]' \
+       --condition on-event some-event-notify-app-prov-type-2 -u \
+       --capture '$app.prov:type[2]' --action notify
+
+test_success '--action notify multiple captures' \
+       --condition on-event some-event-notify-multiple-captures -u \
+       --capture foo --capture '$app.hello:world' --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" "Error: Missing --condition."
+
+test_failure "unknown option" \
+       "Error: Unknown option \`--hello\`" \
+       --hello
+
+test_failure "missing --action" \
+       "Error: Need at least one --action." \
+       --condition on-event hello -u
+
+test_failure "two --condition" \
+       "Error: 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" \
+               "Error: Failed to parse \`123bob\` as an integer." \
+               --condition on-event -u -a --action notify \
+               --${cmd} 123bob
+
+       test_failure "invalid argument to --${cmd}: empty string" \
+               "Error: Failed to parse \`\` as an integer." \
+               --condition on-event -u -a --action notify \
+               --${cmd} ""
+done
+
+# `--condition` failures
+test_failure "missing args after --condition" \
+       "Error: Missing condition name." \
+       --condition
+test_failure "unknown --condition" \
+       "Error: 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
+
+test_failure "--condition on-event --capture: missing argument (end of arg list)" \
+       'Error: While parsing argument #3 (`--capture`): Missing required argument for option `--capture`' \
+       --action notify \
+       --condition on-event -u -a --capture
+
+test_failure "--condition on-event --capture: missing argument (before another option)" \
+       'Error: While parsing expression `--action`: Unary operators are not allowed in capture expressions.' \
+       --condition on-event -u -a --capture \
+       --action notify \
+
+test_failure "--condition on-event --capture: binary operator" \
+       'Error: While parsing expression `foo == 2`: Binary operators are not allowed in capture expressions.' \
+       --condition on-event -u -a \
+       --capture 'foo == 2' --action notify
+
+test_failure "--condition on-event --capture: unary operator" \
+       'Error: While parsing expression `!foo`: Unary operators are not allowed in capture expressions.' \
+       --condition on-event -u -a \
+       --capture '!foo' --action notify
+
+test_failure "--condition on-event --capture: logical operator" \
+       'Error: While parsing expression `foo || bar`: Logical operators are not allowed in capture expressions.' \
+       --condition on-event -u -a \
+       --capture 'foo || bar' --action notify
+
+test_failure "--condition on-event --capture: accessing a sub-field" \
+       'Error: While parsing expression `foo.bar`: Capturing subfields is not supported.' \
+       --condition on-event -u -a \
+       --capture 'foo.bar' --action notify
+
+test_failure "--condition on-event --capture: accessing the sub-field of an array element" \
+       'Error: While parsing expression `foo[3].bar`: Capturing subfields is not supported.' \
+       --condition on-event -u -a \
+       --capture 'foo[3].bar' --action notify
+
+test_failure "--condition on-event --capture: missing colon in app-specific context field" \
+       'Invalid app-specific context field name: missing colon in `foo`.' \
+       --condition on-event -u -a \
+       --capture '$app.foo' --action notify
+
+test_failure "--condition on-event --capture: missing colon in app-specific context field" \
+       'Invalid app-specific context field name: missing type name after colon in `foo:`.' \
+       --condition on-event -u -a \
+       --capture '$app.foo:' --action notify
+
+# `--action` failures
+test_failure "missing args after --action" \
+       "Error: Missing action name." \
+       --condition on-event -u -a \
+       --action
+
+# `--action notify` failures
+test_failure "extra arg after --action notify" \
+       "Error: Unexpected argument \`bob\`." \
+       --condition on-event -u -a \
+       --action notify bob
+
+# `--action start-session` failures
+test_failure "missing arg after --action start-session" \
+       "Error: Missing session name." \
+       --condition on-event some-event-start-session -u \
+       --action start-session
+test_failure "extra arg after --action start-session" \
+       "Error: 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" \
+       "Error: Missing session name." \
+       --condition on-event some-event-stop-session -u \
+       --action stop-session
+test_failure "extra arg after --action stop-session" \
+       "Error: 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" \
+       "Error: Missing session name." \
+       --condition on-event some-event-rotate-session -u \
+       --action rotate-session
+test_failure "extra arg after --action rotate-session" \
+       "Error: 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" \
+       "Error: Missing session name." \
+       --condition on-event some-event-snapshot-session -u \
+       --action snapshot-session
+test_failure "extra arg after --action snapshot-session" \
+       "Error: 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" \
+       "Error: 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" \
+       "Error: 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..78b89a1
--- /dev/null
@@ -0,0 +1,399 @@
+#!/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 44
+
+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/.libs/userspace-probe-elf-binary")
+
+uid=$(id --user)
+gid=$(id --group)
+
+if [ "$uid" == "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: T0
+         user id: ${uid}
+         firing policy: once after 123 occurences
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: test-fire-once-after (type: tracepoint, domain: ust)
+         actions:
+           notify
+       - id: T1
+         user id: ${uid}
+         firing policy: after every 124 occurences
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: test-fire-every (type: tracepoint, domain: ust)
+         actions:
+           notify
+       - id: hello
+         user id: ${uid}
+         tracer notifications discarded: 0
+         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
+       add_trigger --condition on-event -u capture-payload-field --capture a --action notify
+       add_trigger --condition on-event -u capture-array --capture 'a[2]' --capture '$ctx.tourlou[18]' --action notify
+       add_trigger --condition on-event -u capture-chan-ctx --capture '$ctx.vpid' --action notify
+       add_trigger --condition on-event -u capture-app-ctx --capture '$app.iga:active_clients' --action notify
+
+
+       cat > "${tmp_expected_stdout}" <<- EOF
+       - id: ABC
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: aaa (type: tracepoint, domain: ust, filter: p == 2)
+         actions:
+           notify
+       - id: BCD
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: gerboise (type: tracepoint, domain: ust, log level <= TRACE_INFO)
+         actions:
+           notify
+       - id: T0
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: * (type: tracepoint, domain: ust)
+         actions:
+           notify
+       - id: T1
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: hello* (type: tracepoint, domain: ust, exclusions: hello2,hello3,hello4)
+         actions:
+           notify
+       - id: T2
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: lemming (type: tracepoint, domain: ust, log level == TRACE_WARNING)
+         actions:
+           notify
+       - id: T3
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: capture-payload-field (type: tracepoint, domain: ust)
+           captures:
+             - a
+         actions:
+           notify
+       - id: T4
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: capture-array (type: tracepoint, domain: ust)
+           captures:
+             - a[2]
+             - \$ctx.tourlou[18]
+         actions:
+           notify
+       - id: T5
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: capture-chan-ctx (type: tracepoint, domain: ust)
+           captures:
+             - \$ctx.vpid
+         actions:
+           notify
+       - id: T6
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: capture-app-ctx (type: tracepoint, domain: ust)
+           captures:
+             - \$app.iga:active_clients
+         actions:
+           notify
+       EOF
+
+       list_triggers "on-event, tracepoint event rule" "${tmp_expected_stdout}"
+
+       stop_lttng_sessiond_notap
+}
+
+test_on_event_probe ()
+{
+       local channel_enable_addr
+       local channel_disable_addr
+
+       # shellcheck disable=SC2119
+       start_lttng_sessiond_notap
+
+       channel_enable_addr=$(grep ' t lttng_channel_enable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ')
+       channel_disable_addr=$(grep ' t lttng_channel_disable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ')
+
+       # We need to find a valid offset.
+       base_symbol=""
+       offset=0
+       if [[ 0x$channel_enable_addr -lt 0x$channel_disable_addr ]]; then
+               base_symbol="lttng_channel_enable"
+               offset=$(( 0x$channel_disable_addr - 0x$channel_enable_addr ))
+       else
+               base_symbol="lttng_channel_disable"
+               offset=$(( 0x$channel_enable_addr - 0x$channel_disable_addr ))
+       fi
+
+       offset_hex="0x$(printf '%x' $offset)"
+
+       add_trigger --condition on-event -k --probe=lttng_channel_enable my_channel_enable --action notify
+       add_trigger --condition on-event -k --probe="${base_symbol}+${offset_hex}" my_channel_enable --action notify
+       add_trigger --condition on-event -k --probe="0x${channel_enable_addr}" my_channel_enable --action notify
+
+       cat > "${tmp_expected_stdout}" <<- EOF
+       - id: T0
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: my_channel_enable (type: probe, location: lttng_channel_enable)
+         actions:
+           notify
+       - id: T1
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: my_channel_enable (type: probe, location: ${base_symbol}+${offset_hex})
+         actions:
+           notify
+       - id: T2
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: my_channel_enable (type: probe, location: 0x${channel_enable_addr})
+         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: T0
+         user id: ${uid}
+         tracer notifications discarded: 0
+         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: T0
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+         - rule: open (type: syscall)
+         actions:
+           notify
+       - id: T1
+         user id: ${uid}
+         tracer notifications discarded: 0
+         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: T0
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`
+       - id: T1
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, path: /some/path
+       - id: T2
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, path: /some/other/path
+       - id: T3
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, url: net://1.2.3.4
+       - id: T4
+         user id: ${uid}
+         tracer notifications discarded: 0
+         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: T5
+         user id: ${uid}
+         tracer notifications discarded: 0
+         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: T6
+         user id: ${uid}
+         tracer notifications discarded: 0
+         condition: event rule hit
+           rule: some-event (type: tracepoint, domain: ust)
+         actions:
+           snapshot session \`ze-session\`, path: /some/path, max size: 1234
+       - id: T7
+         user id: ${uid}
+         tracer notifications discarded: 0
+         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" 6 || 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..2c15561
--- /dev/null
@@ -0,0 +1,119 @@
+#!/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)
+
+uid=$(id --user)
+gid=$(id --group)
+
+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
+  user id: ${uid}
+  tracer notifications discarded: 0
+  condition: event rule hit
+    rule: aaa (type: tracepoint, domain: ust, filter: p == 2)
+  actions:
+    notify
+- id: T0
+  user id: ${uid}
+  tracer notifications discarded: 0
+  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: T0
+  user id: ${uid}
+  tracer notifications discarded: 0
+  condition: event rule hit
+    rule: * (type: tracepoint, domain: ust)
+  actions:
+    notify
+EOF
+list_triggers "one trigger left" "${tmp_expected_stdout}"
+
+remove_trigger "T0"
+
+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();
+}
+
diff --git a/tests/regression/tools/trigger/utils/Makefile.am b/tests/regression/tools/trigger/utils/Makefile.am
new file mode 100644 (file)
index 0000000..4b716ca
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+AM_CFLAGS += -I$(srcdir) -I$(top_srcdir)/tests/utils
+LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la
+
+noinst_PROGRAMS = notification-client
+notification_client_SOURCES = notification-client.c
+notification_client_LDADD = $(LIBLTTNG_CTL) \
+               $(top_builddir)/tests/utils/libtestutils.la
diff --git a/tests/regression/tools/trigger/utils/notification-client.c b/tests/regression/tools/trigger/utils/notification-client.c
new file mode 100644 (file)
index 0000000..aecc478
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <lttng/condition/event-rule.h>
+#include <lttng/lttng.h>
+
+#include "utils.h"
+
+static struct option long_options[] =
+{
+       /* These options set a flag. */
+       {"trigger", required_argument, 0, 'i'},
+       {"sync-after-notif-register", required_argument, 0, 'a'},
+       {0, 0, 0, 0}
+};
+
+static bool action_group_contains_notify(
+               const struct lttng_action *action_group)
+{
+       unsigned int i, count;
+       enum lttng_action_status status =
+                       lttng_action_group_get_count(action_group, &count);
+
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               printf("Failed to get action count from action group\n");
+               exit(1);
+       }
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_action *action =
+                               lttng_action_group_get_at_index(
+                                               action_group, i);
+               const enum lttng_action_type action_type =
+                               lttng_action_get_type(action);
+
+               if (action_type == LTTNG_ACTION_TYPE_NOTIFY) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+static bool is_expected_trigger_name(const char *expected_trigger_name,
+               struct lttng_notification *notification)
+{
+       int ret = false;
+       const struct lttng_evaluation *evaluation =
+                       lttng_notification_get_evaluation(notification);
+       const enum lttng_condition_type type =
+                       lttng_evaluation_get_type(evaluation);
+
+       switch (type) {
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+       {
+               const char *trigger_name;
+               enum lttng_evaluation_status evaluation_status;
+
+               evaluation_status =
+                               lttng_evaluation_event_rule_get_trigger_name(
+                                               evaluation, &trigger_name);
+               if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) {
+                       fprintf(stderr, "Failed to get trigger name of event rule notification\n");
+                       ret = -1;
+                       break;
+               }
+
+               ret = true;
+               break;
+       }
+       default:
+               fprintf(stderr, "Unknown notification type (%d)\n", type);
+       }
+
+       return ret;
+}
+
+int main(int argc, char **argv)
+{
+       int ret;
+       int option;
+       int option_index;
+       const char *expected_trigger_name = NULL;
+       struct lttng_triggers *triggers = NULL;
+       unsigned int count, i, subcription_count = 0;
+       enum lttng_trigger_status trigger_status;
+       char *after_notif_register_file_path = NULL;
+       struct lttng_notification_channel *notification_channel = NULL;
+
+       while ((option = getopt_long(argc, argv, "a:t:",
+                       long_options, &option_index)) != -1) {
+               switch (option) {
+               case 'a':
+                       after_notif_register_file_path = strdup(optarg);
+                       break;
+               case 't':
+                       expected_trigger_name = strdup(optarg);
+                       break;
+               case '?':
+                       /* getopt_long already printed an error message. */
+               default:
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       if (optind != argc) {
+               ret = -1;
+               goto end;
+       }
+
+
+       notification_channel = lttng_notification_channel_create(
+                       lttng_session_daemon_notification_endpoint);
+       if (!notification_channel) {
+               fprintf(stderr, "Failed to create notification channel\n");
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_list_triggers(&triggers);
+       if (ret != LTTNG_OK) {
+               fprintf(stderr, "Failed to list triggers\n");
+               goto end;
+       }
+
+       trigger_status = lttng_triggers_get_count(triggers, &count);
+       if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+               fprintf(stderr, "Failed to get trigger count\n");
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_trigger *trigger =
+                               lttng_triggers_get_at_index(triggers, i);
+               const struct lttng_condition *condition =
+                               lttng_trigger_get_const_condition(trigger);
+               const struct lttng_action *action =
+                               lttng_trigger_get_const_action(trigger);
+               const enum lttng_action_type action_type =
+                               lttng_action_get_type(action);
+               enum lttng_notification_channel_status channel_status;
+               const char *trigger_name = NULL;
+
+               lttng_trigger_get_name(trigger, &trigger_name);
+               if (strcmp(trigger_name, expected_trigger_name)) {
+                       continue;
+               }
+
+               if (!((action_type == LTTNG_ACTION_TYPE_GROUP &&
+                                     action_group_contains_notify(action)) ||
+                                   action_type == LTTNG_ACTION_TYPE_NOTIFY)) {
+                       /* "The action of trigger is not notify, skipping. */
+                       continue;
+               }
+
+               channel_status = lttng_notification_channel_subscribe(
+                               notification_channel, condition);
+               if (channel_status) {
+                       fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n",
+                                       trigger_name);
+                       ret = -1;
+                       goto end;
+               }
+
+               subcription_count++;
+       }
+
+       if (subcription_count == 0) {
+               printf("No matching trigger with a notify action found.\n");
+               ret = 0;
+               goto end;
+       }
+
+
+       /*
+        * We registered to the notification of our target trigger. We can now
+        * create the sync file to signify that we are ready.
+        */
+       ret = create_file(after_notif_register_file_path);
+       if (ret != 0) {
+               goto end;
+       }
+
+       for (;;) {
+               struct lttng_notification *notification;
+               enum lttng_notification_channel_status channel_status;
+
+               channel_status =
+                               lttng_notification_channel_get_next_notification(
+                                               notification_channel,
+                                               &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 = is_expected_trigger_name(expected_trigger_name,
+                               notification);
+               lttng_notification_destroy(notification);
+               if (ret) {
+                       ret = 0;
+                       goto end;
+               }
+       }
+end:
+       lttng_triggers_destroy(triggers);
+       lttng_notification_channel_destroy(notification_channel);
+       return !!ret;
+}
index 8dc4748a4eaf265094cac3c92855a74a9f05a941..ca41a42f420ef5c0f660ea640e7ae7d50d9fb468 100644 (file)
@@ -24,6 +24,7 @@ TESTS = test_kernel_data \
        test_fd_tracker \
        test_uuid \
        test_buffer_view \
        test_fd_tracker \
        test_uuid \
        test_buffer_view \
+       test_event_expr_to_bytecode \
        test_payload \
        test_unix_socket \
        test_kernel_probe
        test_payload \
        test_unix_socket \
        test_kernel_probe
@@ -49,6 +50,8 @@ noinst_PROGRAMS = test_uri test_session test_kernel_data \
                   test_payload \
                   test_unix_socket \
                   test_kernel_probe \
                   test_payload \
                   test_unix_socket \
                   test_kernel_probe \
+                  test_condition \
+                 test_event_expr_to_bytecode \
                   test_event_rule 
 
 if HAVE_LIBLTTNG_UST_CTL
                   test_event_rule 
 
 if HAVE_LIBLTTNG_UST_CTL
@@ -63,9 +66,9 @@ test_uri_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBHASHTABLE) $(DL_LIBS)
 # Sessiond objects
 SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/cmd.$(OBJEXT) \
 # Sessiond objects
 SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/cmd.$(OBJEXT) \
+        $(top_builddir)/src/bin/lttng-sessiond/condition-internal.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/save.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/notification-thread-commands.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/save.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/notification-thread-commands.$(OBJEXT) \
-        $(top_builddir)/src/bin/lttng-sessiond/shm.$(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/kernel.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/ht-cleanup.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/notification-thread.$(OBJEXT) \
@@ -83,6 +86,7 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT)
         $(top_builddir)/src/bin/lttng-sessiond/notification-thread-events.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/event.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/timer.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/notification-thread-events.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/event.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/timer.$(OBJEXT) \
+        $(top_builddir)/src/bin/lttng-sessiond/trigger-error-accounting.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/snapshot.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/sessiond-config.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/rotate.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/snapshot.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/sessiond-config.$(OBJEXT) \
         $(top_builddir)/src/bin/lttng-sessiond/rotate.$(OBJEXT) \
@@ -198,6 +202,10 @@ test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS)
 test_event_rule_SOURCES = test_event_rule.c
 test_event_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS)
 
 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
 test_relayd_backward_compat_group_by_session_LDADD = $(LIBTAP) $(LIBCOMMON) $(RELAYD_OBJS)
 # relayd backward compat for groou-by-session utilities
 test_relayd_backward_compat_group_by_session_SOURCES = test_relayd_backward_compat_group_by_session.c
 test_relayd_backward_compat_group_by_session_LDADD = $(LIBTAP) $(LIBCOMMON) $(RELAYD_OBJS)
@@ -226,3 +234,7 @@ test_unix_socket_LDADD = $(LIBTAP) $(LIBSESSIOND_COMM) $(LIBCOMMON)
 # Kernel probe location api test
 test_kernel_probe_SOURCES = test_kernel_probe.c
 test_kernel_probe_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS)
 # Kernel probe location api test
 test_kernel_probe_SOURCES = test_kernel_probe.c
 test_kernel_probe_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS)
+#
+# Event expression to bytecode test
+test_event_expr_to_bytecode_SOURCES = test_event_expr_to_bytecode.c
+test_event_expr_to_bytecode_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(LIBCOMMON)
diff --git a/tests/unit/test_condition.c b/tests/unit/test_condition.c
new file mode 100644 (file)
index 0000000..a5fcf78
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * test_condition.c
+ *
+ * Unit tests for the condition API.
+ *
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#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 11
+
+static
+void test_condition_event_rule(void)
+{
+       int ret, i;
+       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_payload buffer;
+
+       lttng_payload_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_log_level_range_lower_bound(tracepoint, LTTNG_LOGLEVEL_WARNING);
+       ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel");
+
+       for (i=0; i < 3; i++) {
+               status = lttng_event_rule_tracepoint_add_exclusion(tracepoint, exclusions[i]);
+               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);
+       ok(ret == 0, "Condition serialized");
+
+       {
+       struct lttng_payload_view view = lttng_payload_view_from_payload(&buffer, 0, -1);
+       (void) lttng_condition_create_from_payload(&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_payload_reset(&buffer);
+       lttng_event_rule_destroy(tracepoint);
+       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();
+}
diff --git a/tests/unit/test_event_expr_to_bytecode.c b/tests/unit/test_event_expr_to_bytecode.c
new file mode 100644 (file)
index 0000000..dd26e66
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include <lttng/event-expr.h>
+#include <common/event-expr-to-bytecode.h>
+#include <common/bytecode/bytecode.h>
+#include <tap/tap.h>
+
+#define NR_TESTS 4
+
+static
+void test_event_payload_field(void)
+{
+       struct lttng_event_expr *event_expr;
+       struct lttng_bytecode *bytecode = NULL;
+       int ret;
+
+       event_expr = lttng_event_expr_event_payload_field_create("tourlou");
+       ret = lttng_event_expr_to_bytecode(event_expr, &bytecode);
+
+       ok(ret == 0, "event payload field");
+
+       lttng_event_expr_destroy(event_expr);
+       free(bytecode);
+}
+
+static
+void test_channel_context_field(void)
+{
+       struct lttng_event_expr *event_expr;
+       struct lttng_bytecode *bytecode = NULL;
+       int ret;
+
+       event_expr = lttng_event_expr_channel_context_field_create("tourlou");
+       ret = lttng_event_expr_to_bytecode(event_expr, &bytecode);
+
+       ok(ret == 0, "channel context field");
+
+       lttng_event_expr_destroy(event_expr);
+       free(bytecode);
+}
+
+static
+void test_app_specific_context_field(void)
+{
+       struct lttng_event_expr *event_expr;
+       struct lttng_bytecode *bytecode = NULL;
+       int ret;
+
+       event_expr = lttng_event_expr_app_specific_context_field_create("Bob", "Leponge");
+       ret = lttng_event_expr_to_bytecode(event_expr, &bytecode);
+
+       ok(ret == 0, "app-specific context field");
+
+       lttng_event_expr_destroy(event_expr);
+       free(bytecode);
+}
+
+static
+void test_array_field_element(void)
+{
+       struct lttng_event_expr *event_expr;
+       struct lttng_bytecode *bytecode = NULL;
+       int ret;
+
+       event_expr = lttng_event_expr_event_payload_field_create("allo");
+       event_expr = lttng_event_expr_array_field_element_create(event_expr, 168);
+       ret = lttng_event_expr_to_bytecode(event_expr, &bytecode);
+
+       ok(ret == 0, "array field element");
+
+       lttng_event_expr_destroy(event_expr);
+       free(bytecode);
+}
+
+int main(void)
+{
+       plan_tests(NR_TESTS);
+
+       test_event_payload_field();
+       test_channel_context_field();
+       test_app_specific_context_field();
+       test_array_field_element();
+
+       return exit_status();
+}
index ead6fe3870be9b31595d9f4c55d0deed08b0f39c..45390c1cc05057a0ae993e8ef7987d9d64a40f21 100644 (file)
 #include "utils.h"
 
 #define MAX_LEN 16
 #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
 /*
  * 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
  */
 int main(int argc, char **argv)
 {
  */
 int main(int argc, char **argv)
 {
-       int fd, ret;
-       char buf[MAX_LEN];
-       char *start_file;
+       int ret;
+       const char *start_file, *path1, *path2;
 
 
-       if (argc != 2) {
+       if (argc != 4) {
                fprintf(stderr, "Error: Missing argument\n");
                fprintf(stderr, "Error: Missing argument\n");
+               fprintf(stderr, "USAGE: %s PATH_WAIT_FILE PATH1_TO_OPEN PATH2_TO_OPEN\n", argv[0]);
                fprintf(stderr, "USAGE: %s PATH_WAIT_FILE\n", argv[0]);
                ret = -1;
                goto error;
        }
 
        start_file = argv[1];
                fprintf(stderr, "USAGE: %s PATH_WAIT_FILE\n", argv[0]);
                ret = -1;
                goto error;
        }
 
        start_file = argv[1];
+       path1 = argv[2];
+       path2 = argv[3];
 
        /*
         * Wait for the start_file to be created by an external process
 
        /*
         * Wait for the start_file to be created by an external process
@@ -47,23 +84,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).
         */
         * 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(path1);
+       if (ret == -1) {
                ret = -1;
                goto error;
        }
 
                ret = -1;
                goto error;
        }
 
-       ret = syscall(SYS_close, fd);
+       ret = open_read_close(path2);
        if (ret == -1) {
        if (ret == -1) {
-               perror("close");
                ret = -1;
                goto error;
        }
                ret = -1;
                goto error;
        }
index 7ce86bcaeb874de64a7a282d65a928d68b171e69..df1e58e417fa5f464b50a01f52fc2e3a97755881 100644 (file)
@@ -48,8 +48,10 @@ int main(int argc, char **argv)
        int option;
        long values[] = { 1, 2, 3 };
        char text[10] = "test";
        int option;
        long values[] = { 1, 2, 3 };
        char text[10] = "test";
+       char escape[10] = "\\*";
        double dbl = 2.0;
        float flt = 2222.0;
        double dbl = 2.0;
        float flt = 2222.0;
+       uint32_t net_values[] = { 1, 2, 3 };
        int nr_iter = 100, ret = 0, first_event_file_created = 0;
        useconds_t nr_usec = 0;
        char *after_first_event_file_path = NULL;
        int nr_iter = 100, ret = 0, first_event_file_created = 0;
        useconds_t nr_usec = 0;
        char *after_first_event_file_path = NULL;
@@ -64,6 +66,10 @@ int main(int argc, char **argv)
        /* Wait on file before exiting */
        char *before_exit_file_path = NULL;
 
        /* Wait on file before exiting */
        char *before_exit_file_path = NULL;
 
+       for (i = 0; i < 3; i++) {
+               net_values[i] = htonl(net_values[i]);
+       }
+
        while ((option = getopt_long(argc, argv, "i:w:a:b:c:d:",
                        long_options, &option_index)) != -1) {
                switch (option) {
        while ((option = getopt_long(argc, argv, "i:w:a:b:c:d:",
                        long_options, &option_index)) != -1) {
                switch (option) {
@@ -141,7 +147,7 @@ int main(int argc, char **argv)
                }
                netint = htonl(i);
                tracepoint(tp, tptest, i, netint, values, text,
                }
                netint = htonl(i);
                tracepoint(tp, tptest, i, netint, values, text,
-                       strlen(text), dbl, flt);
+                       strlen(text), escape, net_values, dbl, flt);
 
                /*
                 * First loop we create the file if asked to indicate
 
                /*
                 * First loop we create the file if asked to indicate
index bc1949417118dd54b4d2d618571eb4af163329f0..132f9ba4eb03930e0d732d2c29ffbdcae06853bc 100644 (file)
 #define _TRACEPOINT_TP_H
 
 #include <lttng/tracepoint.h>
 #define _TRACEPOINT_TP_H
 
 #include <lttng/tracepoint.h>
+#include <stdint.h>
+
+TRACEPOINT_ENUM(
+       tp, tptest_enum,
+       TP_ENUM_VALUES(
+               ctf_enum_auto("AUTO: EXPECT 0")
+               ctf_enum_value("VALUE: 23", 23)
+               ctf_enum_value("VALUE: 27", 27)
+               ctf_enum_auto("AUTO: EXPECT 28")
+               ctf_enum_range("RANGE: 101 TO 303", 101, 303)
+               ctf_enum_auto("AUTO: EXPECT 304")
+               ctf_enum_value("VALUE: -1", -1)
+       )
+)
 
 TRACEPOINT_EVENT(tp, tptest,
        TP_ARGS(int, anint, int, netint, long *, values,
                char *, text, size_t, textlen,
 
 TRACEPOINT_EVENT(tp, tptest,
        TP_ARGS(int, anint, int, netint, long *, values,
                char *, text, size_t, textlen,
+               char *, etext, uint32_t * , net_values,
                double, doublearg, float, floatarg),
        TP_FIELDS(
                ctf_integer(int, intfield, anint)
                ctf_integer_hex(int, intfield2, anint)
                ctf_integer(long, longfield, anint)
                double, doublearg, float, floatarg),
        TP_FIELDS(
                ctf_integer(int, intfield, anint)
                ctf_integer_hex(int, intfield2, anint)
                ctf_integer(long, longfield, anint)
+               ctf_integer(int, signedfield, -1)
                ctf_integer_network(int, netintfield, netint)
                ctf_integer_network_hex(int, netintfieldhex, netint)
                ctf_array(long, arrfield1, values, 3)
                ctf_array_text(char, arrfield2, text, 10)
                ctf_integer_network(int, netintfield, netint)
                ctf_integer_network_hex(int, netintfieldhex, netint)
                ctf_array(long, arrfield1, values, 3)
                ctf_array_text(char, arrfield2, text, 10)
+               ctf_array_network(uint32_t, arrfield3, net_values, 3)
                ctf_sequence(char, seqfield1, text, size_t, textlen)
                ctf_sequence_text(char, seqfield2, text, size_t, textlen)
                ctf_sequence(char, seqfield1, text, size_t, textlen)
                ctf_sequence_text(char, seqfield2, text, size_t, textlen)
+               ctf_sequence_network(uint32_t, seqfield3, net_values, size_t, 3)
+               ctf_sequence(long, seqfield4, values, size_t, 3)
                ctf_string(stringfield, text)
                ctf_string(stringfield, text)
+               ctf_string(stringfield2, etext)
                ctf_float(float, floatfield, floatarg)
                ctf_float(double, doublefield, doublearg)
                ctf_float(float, floatfield, floatarg)
                ctf_float(double, doublefield, doublearg)
+               ctf_enum(tp, tptest_enum, int, enum0, 0)
+               ctf_enum(tp, tptest_enum, int, enum23, 23)
+               ctf_enum(tp, tptest_enum, int, enum27, 27)
+               ctf_enum(tp, tptest_enum, int, enum28, 28)
+               ctf_enum(tp, tptest_enum, int, enum202, 202)
+               ctf_enum(tp, tptest_enum, int, enum304, 304)
+               ctf_enum(tp, tptest_enum, int, enumnegative, -1)
        )
 )
 
        )
 )
 
This page took 0.515662 seconds and 5 git commands to generate.