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_condition
 /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/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
@@ -163,6 +166,9 @@ health_check
 
 # 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/
 
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/BSL-1.0 \
             version
index 1b6f3fcb0d429c0c478caa96943173620f33a76d..0c458e5d01bded4e33d76374e88b99e88a18784a 100644 (file)
@@ -1121,6 +1121,8 @@ AC_CONFIG_FILES([
        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
@@ -1145,6 +1147,8 @@ AC_CONFIG_FILES([
        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
@@ -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/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
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-clear
+       lttng-clear \
+       lttng-add-trigger \
+       lttng-remove-trigger \
+       lttng-list-triggers
+
 MAN3_NAMES =
 MAN8_NAMES = lttng-sessiond lttng-relayd
 MAN1_NO_ASCIIDOC_NAMES =
diff --git a/doc/man/lttng-add-trigger.1.txt b/doc/man/lttng-add-trigger.1.txt
new file mode 100644 (file)
index 0000000..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/event-expr.h \
+       lttng/event-field-value.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 \
+       lttng/condition/event-rule.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/condition/event-rule-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/domain-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 \
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);
 
-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);
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(
-               struct lttng_action *action);
+               const struct lttng_action *action);
 
 /*
  * 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);
 
+LTTNG_HIDDEN
+const char *lttng_condition_type_str(enum lttng_condition_type type);
+
 #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_EVENT_RULE_HIT = 105,
 };
 
 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_UNSUPPORTED = -4,
 };
 
 /*
index 15ae4af4d05d7205c434e8772f88c3acd4b06406..eaef721fb40b7030930f3fa18dd4cc50b2750d30 100644 (file)
@@ -9,6 +9,7 @@
 #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>
@@ -38,7 +39,9 @@ void lttng_evaluation_init(struct lttng_evaluation *evaluation,
                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
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);
 
+// 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 */
index 9fb115caf670bd5b924c112be6369ec003361911..b0f8a76fc92bbfd37e3678da80e6a3c06ff3fd50 100644 (file)
@@ -10,6 +10,7 @@
 
 #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>
@@ -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);
-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);
+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;
@@ -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_generate_lttng_event_cb generate_lttng_event;
 };
 
 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
-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);
 
 /*
@@ -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);
 
+/*
+ * 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 */
index e99b69ae44ec78874f93c0a40756b92712a1bf51..b72274f5b15841c1955d6e5ca6e77b7d88800603 100644 (file)
@@ -21,7 +21,7 @@ struct lttng_event_rule_syscall {
        /* Internal use only. */
        struct {
                char *filter;
-               struct lttng_filter_bytecode *bytecode;
+               struct lttng_bytecode *bytecode;
        } internal_filter;
 };
 
index 039b0a6cf2adddda2a838aee9c6625f27ce03149..227fe6de92aa1f885e9cdf1160f2c577aabe0211 100644 (file)
@@ -39,7 +39,7 @@ struct lttng_event_rule_tracepoint {
        /* internal use only. */
        struct {
                char *filter;
-               struct lttng_filter_bytecode *bytecode;
+               struct lttng_bytecode *bytecode;
        } 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_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,
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/event-rule.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-expr.h>
+#include <lttng/event-field-value.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. */
-uint32_t lttng_snapshot_output_get_id(struct lttng_snapshot_output *output);
+uint32_t lttng_snapshot_output_get_id(const struct lttng_snapshot_output *output);
 /* Return maximum size of a snapshot. */
-uint64_t lttng_snapshot_output_get_maxsize(struct lttng_snapshot_output *output);
+uint64_t lttng_snapshot_output_get_maxsize(const struct lttng_snapshot_output *output);
 /* Return snapshot name. */
-const char *lttng_snapshot_output_get_name(struct lttng_snapshot_output *output);
+const char *lttng_snapshot_output_get_name(const struct lttng_snapshot_output *output);
 /* Return snapshot control URL in a text format. */
-const char *lttng_snapshot_output_get_ctrl_url(struct lttng_snapshot_output *output);
+const char *lttng_snapshot_output_get_ctrl_url(const struct lttng_snapshot_output *output);
 /* Return snapshot data URL in a text format. */
-const char *lttng_snapshot_output_get_data_url(struct lttng_snapshot_output *output);
+const char *lttng_snapshot_output_get_data_url(const struct lttng_snapshot_output *output);
 
 /*
  * Snapshot output setter family functions.
index 4b031137c6473153fc005321bb4a226c4c51fb3f..2be0093c03105512b6cc82ac1ca73d3083265d4c 100644 (file)
@@ -10,6 +10,7 @@
 
 #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>
@@ -26,34 +27,100 @@ struct lttng_trigger {
 
        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. */
+       uint32_t name_length /* Includes '\0' */;
        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;
 
+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
-int lttng_trigger_serialize(struct lttng_trigger *trigger,
+int lttng_trigger_serialize(const struct lttng_trigger *trigger,
                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
-const struct lttng_action *lttng_trigger_get_const_action(
+uint64_t lttng_trigger_get_error_counter_index(
                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);
@@ -61,13 +128,95 @@ void lttng_trigger_get(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
-void lttng_trigger_set_credentials(
-               struct lttng_trigger *trigger,
+void lttng_trigger_set_credentials(struct lttng_trigger *trigger,
                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 */
index feffc6a8f9fb0f1c05c8b1f19b9cb1f308988a7e..e7ade81668d339463935c4e559bc25ea178a2b4f 100644 (file)
@@ -8,9 +8,14 @@
 #ifndef LTTNG_TRIGGER_H
 #define LTTNG_TRIGGER_H
 
+#include <sys/types.h>
+#include <inttypes.h>
+
 struct lttng_action;
 struct lttng_condition;
 struct lttng_trigger;
+/* A set of triggers */
+struct lttng_triggers;
 
 #ifdef __cplusplus
 extern "C" {
@@ -21,6 +26,21 @@ enum lttng_register_trigger_status {
        LTTNG_REGISTER_TRIGGER_STATUS_INVALID = -1,
 };
 
+enum lttng_trigger_status {
+       LTTNG_TRIGGER_STATUS_OK = 0,
+       LTTNG_TRIGGER_STATUS_ERROR = -1,
+       LTTNG_TRIGGER_STATUS_UNKNOWN = -2,
+       LTTNG_TRIGGER_STATUS_INVALID = -3,
+       LTTNG_TRIGGER_STATUS_UNSET = -4,
+       LTTNG_TRIGGER_STATUS_UNSUPPORTED = -5,
+       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.
  *
@@ -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.
  *
+ * 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.
  *
@@ -42,6 +65,28 @@ enum lttng_register_trigger_status {
 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.
  *
@@ -52,6 +97,9 @@ extern struct lttng_trigger *lttng_trigger_create(
 extern struct lttng_condition *lttng_trigger_get_condition(
                struct lttng_trigger *trigger);
 
+const struct lttng_condition *lttng_trigger_get_const_condition(
+               const struct lttng_trigger *trigger);
+
 /*
  * Get the action of a trigger.
  *
@@ -62,6 +110,68 @@ extern struct lttng_condition *lttng_trigger_get_condition(
 extern struct lttng_action *lttng_trigger_get_action(
                struct lttng_trigger *trigger);
 
+const struct lttng_action *lttng_trigger_get_const_action(
+               const struct lttng_trigger *trigger);
+
+/*
+ * Get the name of a trigger.
+ *
+ * The caller does not assume the ownership of the returned name.
+ * The name shall only only be used for the duration of the trigger's
+ * lifetime, or 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.
  */
@@ -83,7 +193,46 @@ extern int lttng_register_trigger(struct lttng_trigger *trigger);
  *
  * Return 0 on success, a negative LTTng error code on error.
  */
-extern int lttng_unregister_trigger(struct lttng_trigger *trigger);
+extern int lttng_unregister_trigger(const struct lttng_trigger *trigger);
+
+/*
+ * List current triggers.
+ *
+ * On success, 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
 }
index fce97f3d14c37abd7b5d43081012c043013fa71b..a4f99905dd720fd272424d0935b76e4a5297a9be 100644 (file)
@@ -1,3 +1,3 @@
 # 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 \
+                       condition-internal.c condition-internal.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 \
@@ -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 \
+                       trigger-error-accounting.c trigger-error-accounting.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 \
index 2e6e27a8ce3febcfd0db7cbb100369ff7d974ca4..8df560e0fb1b69d07753db639e15b8ab38413dac 100644 (file)
 #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/condition/event-rule-internal.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)
 {
-       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. */
@@ -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 = {
-               .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);
 
-       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) {
-               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,
-                               (long int) trigger_creds->uid,
-                               (long int) trigger_creds->gid);
+                               (long int) lttng_credentials_get_uid(trigger_creds));
        }
 
        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,
@@ -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_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,
@@ -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) {
-               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),
-                   work_item->trigger);
+                               get_trigger_name(work_item->trigger));
                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:
-               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:
-               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:
-               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;
        }
@@ -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) {
-               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),
-                   work_item->trigger);
+                               get_trigger_name(work_item->trigger));
                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:
-               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:
-               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:
-               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;
        }
@@ -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) {
-               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),
-                   work_item->trigger);
+                               get_trigger_name(work_item->trigger));
                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:
-               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:
-               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:
-               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:
-               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;
        }
@@ -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:
-               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:
-               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;
        }
@@ -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) {
-                       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;
                }
        }
@@ -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)
 {
-       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),
-                       work_item->trigger,
+                       get_trigger_name(work_item->trigger),
                        work_item->id);
 
-       return action_executors[lttng_action_get_type_const(action)](
+       return action_executors[lttng_action_get_type(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);
 
-       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);
-       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;
 }
 
@@ -541,7 +554,7 @@ static void *action_executor_thread(void *_data)
                        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);
@@ -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
-                               " 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);
        }
@@ -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. */
-               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;
        }
@@ -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`",
-                               trigger);
+                               get_trigger_name(trigger));
                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,
-                       trigger, work_item_id);
+                       get_trigger_name(trigger), work_item_id);
        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 agent *trigger_agent;
+       struct lttng_ht_iter iter;
 
        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);
        }
+
+       /* 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 <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule.h>
+
 #include <common/common.h>
 #include <common/sessiond-comm/agent.h>
 
@@ -101,7 +107,8 @@ no_match:
 }
 
 /*
- * Match function for the events hash table lookup by name and loglevel.
+ * Match function for the events hash table lookup by name, loglevel and
+ * filter_expression.
  */
 static int ht_match_event(struct cds_lfht_node *node,
                const void *_key)
@@ -682,6 +689,7 @@ int agent_enable_event(struct agent_event *event,
        }
 
        event->enabled = 1;
+       event->user_refcount++;
        ret = LTTNG_OK;
 
 error:
@@ -791,6 +799,17 @@ int agent_disable_event(struct agent_event *event,
                goto end;
        }
 
+       if (event->user_refcount - 1 != 0) {
+               /*
+                * Disable the agent event only when all users (trigger etc.)
+                * have disabled it.
+                */
+
+               event->user_refcount--;
+               ret = LTTNG_OK;
+               goto end;
+       }
+
        rcu_read_lock();
 
        cds_lfht_for_each_entry(agent_apps_ht_by_sock->ht, &iter.iter, app,
@@ -806,6 +825,7 @@ int agent_disable_event(struct agent_event *event,
                }
        }
 
+       event->user_refcount = 0;
        event->enabled = 0;
 
 error:
@@ -1109,7 +1129,7 @@ error:
  */
 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;
 
@@ -1206,6 +1226,66 @@ void agent_find_events_by_name(const char *name, struct agent *agt,
                        ht_match_event_by_name, &key, &iter->iter);
 }
 
+/*
+ * Find the agent event matching the trigger.
+ *
+ * RCU read side lock MUST be acquired. It must be kept for as long as
+ * the returned agent_event is used.
+ *
+ * Return object if found else NULL.
+ */
+struct agent_event *agent_find_event_by_trigger(
+               const struct lttng_trigger *trigger, struct agent *agt)
+{
+       enum lttng_condition_status c_status;
+       enum lttng_event_rule_status er_status;
+       enum lttng_domain_type d_type;
+       const struct lttng_condition *condition;
+       const struct lttng_event_rule *rule;
+       const char *name;
+       const char *filter_expression;
+       /* TODO validate if this is the unset value or no */
+       int loglevel_value = 0;
+       enum lttng_loglevel_type loglevel_type;
+
+       assert(agt);
+       assert(agt->events);
+
+       condition = lttng_trigger_get_const_condition(trigger);
+
+       assert(lttng_condition_get_type(condition) ==
+                       LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+       c_status = lttng_condition_event_rule_get_rule(condition, &rule);
+       assert(c_status == LTTNG_CONDITION_STATUS_OK);
+
+       assert(lttng_event_rule_get_type(rule) ==
+                       LTTNG_EVENT_RULE_TYPE_TRACEPOINT);
+
+       d_type = lttng_event_rule_get_domain_type(rule);
+       assert(d_type == LTTNG_DOMAIN_JUL || d_type == LTTNG_DOMAIN_LOG4J ||
+                       d_type == LTTNG_DOMAIN_PYTHON);
+
+       /* Get the name (aka pattern) */
+       er_status = lttng_event_rule_tracepoint_get_pattern(rule, &name);
+       assert(er_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       /* Get the internal filter_expression */
+       filter_expression = lttng_event_rule_get_filter(rule);
+
+       er_status = lttng_event_rule_tracepoint_get_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.
@@ -1233,8 +1313,10 @@ void agent_event_next_duplicate(const char *name,
  * Return object if found else NULL.
  */
 struct agent_event *agent_find_event(const char *name,
-               enum lttng_loglevel_type loglevel_type, int loglevel_value,
-               char *filter_expression, struct agent *agt)
+               enum lttng_loglevel_type loglevel_type,
+               int loglevel_value,
+               const char *filter_expression,
+               struct agent *agt)
 {
        struct lttng_ht_node_str *node;
        struct lttng_ht_iter iter;
@@ -1347,6 +1429,21 @@ int agent_app_ht_alloc(void)
        return ret;
 }
 
+/*
+ * Allocate agent_apps_ht_by_sock.
+ */
+int trigger_agent_ht_alloc(void)
+{
+       int ret = 0;
+
+       trigger_agents_ht_by_domain = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+       if (!trigger_agents_ht_by_domain) {
+               ret = -1;
+       }
+
+       return ret;
+}
+
 /*
  * Destroy a agent application by socket.
  */
@@ -1396,6 +1493,32 @@ void agent_app_ht_clean(void)
        lttng_ht_destroy(agent_apps_ht_by_sock);
 }
 
+/*
+ * Clean-up the trigger agent hash table and destroy it.
+ */
+void trigger_agent_ht_clean(void)
+{
+       struct lttng_ht_node_u64 *node;
+       struct lttng_ht_iter iter;
+
+       if (!trigger_agents_ht_by_domain) {
+               return;
+       }
+       rcu_read_lock();
+       cds_lfht_for_each_entry (trigger_agents_ht_by_domain->ht, &iter.iter,
+                       node, node) {
+               struct agent *agent;
+
+               (void) lttng_ht_del(trigger_agents_ht_by_domain, &iter);
+
+               agent = caa_container_of(node, struct agent, node);
+               agent_destroy(agent);
+       }
+       rcu_read_unlock();
+
+       lttng_ht_destroy(trigger_agents_ht_by_domain);
+}
+
 /*
  * Update a agent application (given socket) using the given agent.
  *
@@ -1447,3 +1570,27 @@ void agent_update(const struct agent *agt, const struct agent_app *app)
 
        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;
 
+/*
+ * Hash table that contains the trigger agents by domain */
+extern struct lttng_ht *trigger_agents_ht_by_domain;
+
 struct agent_ht_key {
        const char *name;
        int loglevel_value;
        enum lttng_loglevel_type loglevel_type;
-       char *filter_expression;
+       const char *filter_expression;
 };
 
 /*
@@ -84,9 +88,15 @@ struct agent_event {
        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;
+
+       /*
+        * 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,
-               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,
-               enum lttng_loglevel_type loglevel_type, int loglevel_value,
-               char *filter_expression, struct agent *agt);
+               enum lttng_loglevel_type loglevel_type,
+               int loglevel_value,
+               const char *filter_expression,
+               struct agent *agt);
 void agent_find_events_by_name(const char *name, struct agent *agt,
                struct lttng_ht_iter* iter);
 void agent_event_next_duplicate(const char *name,
@@ -167,4 +179,12 @@ void agent_update(const struct agent *agt, const struct agent_app *app);
 int agent_list_events(struct lttng_event **events,
                enum lttng_domain_type domain);
 
+struct agent_event *agent_find_event_by_trigger(
+               const struct lttng_trigger *trigger, struct agent *agt);
+
+/* todo: find a better place for this */
+struct agent *trigger_find_agent(enum lttng_domain_type domain_type);
+void trigger_agent_ht_clean(void);
+int trigger_agent_ht_alloc(void);
+
 #endif /* LTTNG_SESSIOND_AGENT_H */
index 8a2ef85b7345c33d89cd2914134bd075a71b4e9e..b1c8cee9f8d1ecd5377e6a04814bcccbd63802e5 100644 (file)
@@ -42,6 +42,7 @@
 #include "utils.h"
 #include "manage-consumer.h"
 #include "clear.h"
+#include "agent-thread.h"
 
 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 need_consumerd = 1;
 
        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_REGISTER_TRIGGER:
-       case LTTNG_UNREGISTER_TRIGGER:
        case LTTNG_ROTATE_SESSION:
        case LTTNG_ROTATION_GET_INFO:
        case LTTNG_ROTATION_SET_SCHEDULE:
        case LTTNG_SESSION_LIST_ROTATION_SCHEDULES:
        case LTTNG_CLEAR_SESSION:
+       case LTTNG_LIST_TRIGGERS:
                need_domain = 0;
                break;
        default:
                need_domain = 1;
        }
 
+       /* Needs a functioning consumerd */
+       switch (cmd_ctx->lsm.cmd_type) {
+       case LTTNG_REGISTER_TRIGGER:
+       case LTTNG_UNREGISTER_TRIGGER:
+               need_consumerd = 0;
+               break;
+       default:
+               need_consumerd = 1;
+               break;
+       }
+
        if (config.no_kernel && need_domain
                        && cmd_ctx->lsm.domain.type == LTTNG_DOMAIN_KERNEL) {
                if (!is_root) {
@@ -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_REGISTER_TRIGGER:
+       case LTTNG_LIST_TRIGGERS:
                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_LIST_TRIGGERS:
                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 */
-               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;
                }
@@ -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:
+               if (!agent_tracing_is_enabled()) {
+                       ret = LTTNG_ERR_AGENT_TRACING_DISABLED;
+                       goto error;
+               }
+               /* Fallthrough */
        case LTTNG_DOMAIN_UST:
        {
                if (!ust_app_supported()) {
                        ret = LTTNG_ERR_NO_UST;
                        goto error;
                }
+
                /* Consumer is in an ERROR state. Report back to client */
-               if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) {
+               if (need_consumerd && uatomic_read(&ust_consumerd_state) ==
+                                                     CONSUMER_ERROR) {
                        ret = LTTNG_ERR_NO_USTCONSUMERD;
                        goto error;
                }
@@ -1445,7 +1468,7 @@ error_add_context:
        {
                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. */
@@ -2024,8 +2047,39 @@ error_add_context:
        }
        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,
-                               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:
@@ -2138,6 +2192,42 @@ error_add_context:
                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;
index dc442035a3fe21e6b4c487a1007c9f05d1b38c56..65553a538a1e62a6e570a5edbf127d741ef4c40e 100644 (file)
 #include <lttng/location-internal.h>
 #include <lttng/trigger/trigger-internal.h>
 #include <lttng/condition/condition.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
+#include <lttng/event-rule/tracepoint.h>
 #include <lttng/action/action.h>
 #include <lttng/channel.h>
 #include <lttng/channel-internal.h>
@@ -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,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                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,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                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;
-                       struct lttng_filter_bytecode *filter_a = NULL;
+                       struct lttng_bytecode *filter_a = NULL;
 
                        /*
                         * 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;
-                       struct lttng_filter_bytecode *filter_copy = NULL;
+                       struct lttng_bytecode *filter_copy = NULL;
 
                        if (filter) {
                                const size_t filter_size = sizeof(
-                                               struct lttng_filter_bytecode)
+                                               struct lttng_bytecode)
                                                + 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,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                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,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                int wpipe)
 {
@@ -4256,8 +4263,59 @@ end:
        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,
-               struct notification_thread_handle *notification_thread)
+               struct notification_thread_handle *notification_thread,
+               struct lttng_trigger **return_trigger)
 {
        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 = {
-               .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);
@@ -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);
-       /* 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);
@@ -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 = {
-               .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);
@@ -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);
+
+       /* 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;
 }
 
+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.
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,
-               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,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                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,
-               struct notification_thread_handle *notification_thread_handle);
+               struct notification_thread_handle *notification_thread_handle,
+               struct lttng_trigger **return_trigger);
 int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock,
                struct notification_thread_handle *notification_thread_handle);
 
+int cmd_list_triggers(struct command_ctx *cmd_ctx,
+               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,
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);
        }
-       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;
@@ -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 =
-                               chunk_credentials.uid;
+                               lttng_credentials_get_uid(&chunk_credentials);
                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;
        }
 
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 ust_app *app;
 
        /* Consumer is in an ERROR state. Stop any application update. */
        if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) {
@@ -43,10 +44,25 @@ static void update_ust_app(int app_sock)
                return;
        }
 
+       rcu_read_lock();
+       assert(app_sock >= 0);
+       app = ust_app_find_by_sock(app_sock);
+       if (app == NULL) {
+               /*
+                * Application can be unregistered before so
+                * this is possible hence simply stopping the
+                * update.
+                */
+               DBG3("UST app update failed to find app sock %d",
+                       app_sock);
+               goto unlock_rcu;
+       }
+
+       /* Update all tokens for the app */
+       ust_app_global_update_tokens(app);
+
        /* For all tracing session(s) */
        cds_list_for_each_entry_safe(sess, stmp, &session_list->head, list) {
-               struct ust_app *app;
-
                if (!session_get(sess)) {
                        continue;
                }
@@ -55,26 +71,15 @@ static void update_ust_app(int app_sock)
                        goto unlock_session;
                }
 
-               rcu_read_lock();
-               assert(app_sock >= 0);
-               app = ust_app_find_by_sock(app_sock);
-               if (app == NULL) {
-                       /*
-                        * Application can be unregistered before so
-                        * this is possible hence simply stopping the
-                        * update.
-                        */
-                       DBG3("UST app update failed to find app sock %d",
-                               app_sock);
-                       goto unlock_rcu;
-               }
                ust_app_global_update(sess->ust_session, app);
-       unlock_rcu:
-               rcu_read_unlock();
        unlock_session:
                session_unlock(sess);
                session_put(sess);
        }
+
+unlock_rcu:
+       rcu_read_unlock();
+
 }
 
 /*
@@ -386,6 +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);
 
+                               (void) ust_app_setup_trigger_group(app);
+
                                /* Send notify socket through the notify pipe. */
                                ret = send_socket_to_thread(
                                                notifiers->apps_cmd_notify_pipe_write_fd,
index 189236beb1171a05ba21e89b3ecb51216a7b5e47..28f296a037989bfa75f70f7b1444e85a4f5ffd8c 100644 (file)
 #include <string.h>
 
 #include <lttng/lttng.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <common/bytecode/bytecode.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 "utils.h"
 
 /*
  * 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;
-       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;
@@ -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,
-               struct lttng_filter_bytecode *filter)
+               struct lttng_bytecode *filter)
 {
        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,
-               struct lttng_filter_bytecode *filter,
+               struct lttng_bytecode *filter,
                struct lttng_event_exclusion *exclusion,
                bool internal_event)
 {
@@ -366,6 +372,20 @@ error:
        return ret;
 }
 
+static void agent_enable_all(struct agent *agt)
+{
+       struct agent_event *aevent;
+       struct lttng_ht_iter iter;
+
+       /* Flag every event that they are now enabled. */
+       rcu_read_lock();
+       cds_lfht_for_each_entry (
+                       agt->events->ht, &iter.iter, aevent, node.node) {
+               aevent->enabled = 1;
+       }
+       rcu_read_unlock();
+}
+
 /*
  * Enable all agent event for a given UST session.
  *
@@ -373,11 +393,9 @@ error:
  */
 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;
-       struct agent_event *aevent;
-       struct lttng_ht_iter iter;
 
        assert(usess);
 
@@ -389,13 +407,7 @@ int event_agent_enable_all(struct ltt_ust_session *usess,
                goto error;
        }
 
-       /* Flag every event that they are now enabled. */
-       rcu_read_lock();
-       cds_lfht_for_each_entry(agt->events->ht, &iter.iter, aevent,
-                       node.node) {
-               aevent->enabled = 1;
-       }
-       rcu_read_unlock();
+       agent_enable_all(agt);
 
        ret = LTTNG_OK;
 
@@ -410,7 +422,7 @@ error:
  * 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;
@@ -467,28 +479,17 @@ end:
        return ret;
 }
 
-/*
- * Enable a single agent event for a given UST session.
- *
- * Return LTTNG_OK on success or else a LTTNG_ERR* code.
- */
-int event_agent_enable(struct ltt_ust_session *usess,
-               struct agent *agt, struct lttng_event *event,
-               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;
 
-       assert(usess);
        assert(event);
        assert(agt);
 
-       DBG("Event agent enabling %s for session %" PRIu64 " with loglevel type %d "
-                       ", loglevel %d and filter \"%s\"", event->name,
-                       usess->id, event->loglevel_type, event->loglevel,
-                       filter_expression ? filter_expression : "NULL");
-
        aevent = agent_find_event(event->name, event->loglevel_type,
                        event->loglevel, filter_expression, agt);
        if (!aevent) {
@@ -542,6 +543,112 @@ end:
        return ret;
 }
 
+/*
+ * Enable a single agent event for a given UST session.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int event_agent_enable(struct ltt_ust_session *usess,
+               struct agent *agt,
+               struct lttng_event *event,
+               struct lttng_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.
@@ -567,6 +674,43 @@ const char *event_get_default_agent_ust_name(enum lttng_domain_type domain)
        return default_event_name;
 }
 
+static int trigger_agent_disable_one(const struct lttng_trigger *trigger,
+               struct agent *agt,
+               struct agent_event *aevent)
+
+{
+       int ret;
+
+       assert(agt);
+       assert(trigger);
+       assert(aevent);
+
+       /*
+        * Actual ust event un-registration happens on the trigger
+        * un-registration at that point.
+        */
+
+       DBG("Event agent disabling %s (loglevel type %d, loglevel value %d) for trigger %" PRIu64,
+                       aevent->name, aevent->loglevel_type,
+                       aevent->loglevel_value, lttng_trigger_get_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.
  *
@@ -659,6 +803,44 @@ error:
        return ret;
 }
 
+/*
+ * Disable agent event matching a given trigger.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int trigger_agent_disable(
+               const struct lttng_trigger *trigger, struct agent *agt)
+{
+       int ret = LTTNG_OK;
+       struct agent_event *aevent;
+
+       assert(trigger);
+       assert(agt);
+
+       DBG("Event agent disabling for trigger %" PRIu64,
+                       lttng_trigger_get_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.
  *
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,
-               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,
-               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,
@@ -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 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,
-               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);
 
+int trigger_agent_enable(
+               const struct lttng_trigger *trigger, struct agent *agt);
+int trigger_agent_disable(
+               const struct lttng_trigger *trigger, struct agent *agt);
+
 const char *event_get_default_agent_ust_name(enum lttng_domain_type domain);
 
 #endif /* _LTT_EVENT_H */
index 20aa790a7540d4dcfd0adbb8897a7da81ff47582..8681d5d90749d84c5745015d52334f4c65a1bb18 100644 (file)
@@ -20,8 +20,10 @@ long page_size;
 struct health_app *health_sessiond;
 
 struct notification_thread_handle *notification_thread_handle;
+pthread_mutex_t notification_trigger_tokens_ht_lock = PTHREAD_MUTEX_INITIALIZER;
 
 struct lttng_ht *agent_apps_ht_by_sock = NULL;
+struct lttng_ht *trigger_agents_ht_by_domain = NULL;
 
 struct lttng_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 <common/hashtable/utils.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/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 "condition-internal.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 "trigger-error-accounting.h"
+#include "notification-thread-commands.h"
 
 /*
  * 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 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.
  *
@@ -219,6 +233,44 @@ error:
        return -1;
 }
 
+/*
+ * Create a kernel channel, register it to the kernel tracer and add it to the
+ * kernel session.
+ */
+static
+int kernel_create_trigger_group(int *trigger_group_fd)
+{
+       int ret;
+       int local_fd = -1;
+
+       assert(trigger_group_fd);
+
+       /* Kernel tracer channel creation */
+       ret = kernctl_create_trigger_group(kernel_tracer_fd);
+       if (ret < 0) {
+               PERROR("ioctl kernel create trigger group");
+               ret = -1;
+               goto error;
+       }
+
+       /* Store locally */
+       local_fd = ret;
+
+       /* Prevent fd duplication after execlp() */
+       ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC);
+       if (ret < 0) {
+               PERROR("fcntl session fd");
+       }
+
+       DBG("Kernel trigger group created (fd: %d)",
+                       local_fd);
+       ret = 0;
+
+error:
+       *trigger_group_fd = local_fd;
+       return ret;
+}
+
 /*
  * Compute the offset of the instrumentation byte in the binary based on the
  * function probe location using the ELF lookup method.
@@ -230,7 +282,7 @@ error:
 static
 int extract_userspace_probe_offset_function_elf(
                const struct lttng_userspace_probe_location *probe_location,
-               struct ltt_kernel_session *session, uint64_t *offset)
+               uid_t uid, gid_t gid, uint64_t *offset)
 {
        int fd;
        int ret = 0;
@@ -267,8 +319,7 @@ int extract_userspace_probe_offset_function_elf(
                goto end;
        }
 
-       ret = run_as_extract_elf_symbol_offset(fd, symbol, session->uid,
-                       session->gid, offset);
+       ret = run_as_extract_elf_symbol_offset(fd, symbol, uid, gid, offset);
        if (ret < 0) {
                DBG("userspace probe offset calculation failed for "
                                "function %s", symbol);
@@ -292,7 +343,7 @@ end:
 static
 int extract_userspace_probe_offset_tracepoint_sdt(
                const struct lttng_userspace_probe_location *probe_location,
-               struct ltt_kernel_session *session, uint64_t **offsets,
+               uid_t uid, gid_t gid, uint64_t **offsets,
                uint32_t *offsets_count)
 {
        enum lttng_userspace_probe_location_lookup_method_type lookup_method_type;
@@ -338,7 +389,7 @@ int extract_userspace_probe_offset_tracepoint_sdt(
        }
 
        ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name,
-                       session->uid, session->gid, offsets, offsets_count);
+                       uid, gid, offsets, offsets_count);
        if (ret < 0) {
                DBG("userspace probe offset calculation failed for sdt "
                                "probe %s:%s", provider_name, probe_name);
@@ -359,29 +410,16 @@ end:
        return ret;
 }
 
-/*
- * Extract the offsets of the instrumentation point for the different lookup
- * methods.
- */
 static
-int userspace_probe_add_callsites(struct lttng_event *ev,
-                       struct ltt_kernel_session *session, int fd)
+int userspace_probe_add_callsite(
+               const struct lttng_userspace_probe_location *location,
+               uid_t uid, gid_t gid, int fd)
 {
        const struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
        enum lttng_userspace_probe_location_lookup_method_type type;
-       const struct lttng_userspace_probe_location *location = NULL;
        int ret;
 
-       assert(ev);
-       assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE);
-
-       location = lttng_event_get_userspace_probe_location(ev);
-       if (!location) {
-               ret = -1;
-               goto end;
-       }
-       lookup_method =
-                       lttng_userspace_probe_location_get_lookup_method(location);
+       lookup_method = lttng_userspace_probe_location_get_lookup_method(location);
        if (!lookup_method) {
                ret = -1;
                goto end;
@@ -394,7 +432,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev,
                struct lttng_kernel_event_callsite callsite;
                uint64_t offset;
 
-               ret = extract_userspace_probe_offset_function_elf(location, session, &offset);
+               ret = extract_userspace_probe_offset_function_elf(location,
+                               uid, gid, &offset);
                if (ret) {
                        ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
                        goto end;
@@ -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) {
-                       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;
                }
@@ -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
                 */
-               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;
@@ -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) {
-                               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;
@@ -449,6 +487,71 @@ end:
        return ret;
 }
 
+/*
+ * Extract the offsets of the instrumentation point for the different lookup
+ * methods.
+ */
+static
+int userspace_probe_event_add_callsites(struct lttng_event *ev,
+                       struct ltt_kernel_session *session, int fd)
+{
+       const struct lttng_userspace_probe_location *location = NULL;
+       int ret;
+
+       assert(ev);
+       assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE);
+
+       location = lttng_event_get_userspace_probe_location(ev);
+       if (!location) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = userspace_probe_add_callsite(location, session->uid, session->gid,
+               fd);
+       if (ret) {
+               WARN("Adding callsite to userspace probe event \"%s\" "
+                       "failed.", ev->name);
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Extract the offsets of the instrumentation point for the different lookup
+ * methods.
+ */
+static int userspace_probe_event_rule_add_callsites(
+               const struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds,
+               int fd)
+{
+       const struct lttng_userspace_probe_location *location = NULL;
+       enum lttng_event_rule_status status;
+       int ret;
+
+       assert(rule);
+       assert(creds);
+       assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE);
+
+       status = lttng_event_rule_uprobe_get_location(rule, &location);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = userspace_probe_add_callsite(
+                       location, 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.
@@ -457,7 +560,7 @@ end:
 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;
@@ -518,7 +621,8 @@ int kernel_create_event(struct lttng_event *ev,
        }
 
        if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) {
-               ret = userspace_probe_add_callsites(ev, channel->session, event->fd);
+               ret = userspace_probe_event_add_callsites(ev, channel->session,
+                       event->fd);
                if (ret) {
                        goto add_callsite_error;
                }
@@ -675,6 +779,42 @@ error:
        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,
@@ -1796,6 +1936,7 @@ LTTNG_HIDDEN
 int init_kernel_tracer(void)
 {
        int ret;
+       enum lttng_error_code error_code_ret;
        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 < 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 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.");
        }
+
        return 0;
 
 error_version:
@@ -1875,6 +2044,40 @@ LTTNG_HIDDEN
 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) {
@@ -1884,6 +2087,7 @@ void cleanup_kernel_tracer(void)
                }
                kernel_tracer_fd = -1;
        }
+
        DBG("Unloading kernel modules");
        modprobe_remove_lttng_all();
        free(syscall_table);
@@ -1974,3 +2178,322 @@ end:
        rcu_read_unlock();
        return status;
 }
+
+enum lttng_error_code kernel_create_trigger_group_notification_fd(
+               int *trigger_group_notification_fd)
+{
+       enum lttng_error_code 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,
-               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);
@@ -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_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