From 1831ae68b70dece8e9b847081526495adbbf05e5 Mon Sep 17 00:00:00 2001 From: Francis Deslauriers Date: Tue, 11 Feb 2020 15:51:52 -0500 Subject: [PATCH] SoW-2019-0002: Dynamic Snapshot Revision 1 --- .gitignore | 14 +- configure.ac | 40 +- doc/examples/trigger-on-event/README.md | 56 + doc/examples/trigger-on-event/demo.sh | 20 + .../trigger-on-event/instrumented-app.c | 36 + .../trigger-on-event/notification-client.c | 220 ++ doc/examples/trigger-on-event/tp.c | 10 + doc/examples/trigger-on-event/tp.h | 28 + doc/man/Makefile.am | 6 +- doc/man/lttng-add-trigger.1.txt | 134 + doc/man/lttng-list-triggers.1.txt | 37 + doc/man/lttng-remove-trigger.1.txt | 38 + extras/bindings/swig/python/lttng.i.in | 3 + include/Makefile.am | 23 +- include/lttng/action/action-internal.h | 29 + include/lttng/action/action.h | 15 +- include/lttng/action/group-internal.h | 39 + include/lttng/action/group.h | 55 + include/lttng/action/notify-internal.h | 5 + .../lttng/action/rotate-session-internal.h | 39 + include/lttng/action/rotate-session.h | 53 + .../lttng/action/snapshot-session-internal.h | 39 + include/lttng/action/snapshot-session.h | 73 + include/lttng/action/start-session-internal.h | 39 + include/lttng/action/start-session.h | 53 + include/lttng/action/stop-session-internal.h | 39 + include/lttng/action/stop-session.h | 53 + include/lttng/condition/condition-internal.h | 9 +- include/lttng/condition/condition.h | 1 + include/lttng/condition/event-rule-internal.h | 69 + include/lttng/condition/event-rule.h | 83 + include/lttng/domain-internal.h | 35 + include/lttng/event-internal.h | 15 + .../lttng/event-rule/event-rule-internal.h | 152 ++ include/lttng/event-rule/event-rule.h | 77 + include/lttng/event-rule/kprobe-internal.h | 63 + include/lttng/event-rule/kprobe.h | 72 + include/lttng/event-rule/kretprobe-internal.h | 60 + include/lttng/event-rule/kretprobe.h | 72 + include/lttng/event-rule/syscall-internal.h | 50 + include/lttng/event-rule/syscall.h | 88 + .../lttng/event-rule/tracepoint-internal.h | 81 + include/lttng/event-rule/tracepoint.h | 199 ++ include/lttng/event-rule/uprobe-internal.h | 49 + include/lttng/event-rule/uprobe.h | 88 + include/lttng/lttng.h | 12 + include/lttng/snapshot-internal.h | 2 +- include/lttng/snapshot.h | 40 +- include/lttng/trigger/trigger-internal.h | 121 +- include/lttng/trigger/trigger.h | 110 +- include/lttng/userspace-probe-internal.h | 14 + m4/ax_append_compile_flags.m4 | 46 + m4/ax_append_flag.m4 | 50 + m4/ax_check_compile_flag.m4 | 53 + m4/ax_require_defined.m4 | 37 + rename.sh | 13 + src/bin/lttng-crash/lttng-crash.c | 8 +- src/bin/lttng-relayd/cmd-2-2.c | 2 + src/bin/lttng-sessiond/Makefile.am | 3 +- src/bin/lttng-sessiond/action-executor.c | 654 +++++ src/bin/lttng-sessiond/action-executor.h | 33 + src/bin/lttng-sessiond/agent-thread.c | 10 + src/bin/lttng-sessiond/agent.c | 153 +- src/bin/lttng-sessiond/agent.h | 26 +- src/bin/lttng-sessiond/client.c | 119 +- src/bin/lttng-sessiond/cmd.c | 279 ++- src/bin/lttng-sessiond/cmd.h | 7 +- src/bin/lttng-sessiond/dispatch.c | 40 +- src/bin/lttng-sessiond/event.c | 226 +- src/bin/lttng-sessiond/event.h | 5 + src/bin/lttng-sessiond/globals.c | 4 + src/bin/lttng-sessiond/health-sessiond.h | 1 + src/bin/lttng-sessiond/kernel.c | 512 +++- src/bin/lttng-sessiond/kernel.h | 7 + src/bin/lttng-sessiond/lttng-sessiond.h | 1 + src/bin/lttng-sessiond/main.c | 12 +- src/bin/lttng-sessiond/modprobe.c | 101 +- .../notification-thread-commands.c | 175 +- .../notification-thread-commands.h | 48 + .../notification-thread-events.c | 2146 +++++++++++++---- .../notification-thread-events.h | 4 + .../notification-thread-internal.h | 152 +- src/bin/lttng-sessiond/notification-thread.c | 137 +- src/bin/lttng-sessiond/notification-thread.h | 75 +- src/bin/lttng-sessiond/rotate.c | 3 + src/bin/lttng-sessiond/sessiond-config.c | 2 +- src/bin/lttng-sessiond/thread.c | 23 +- src/bin/lttng-sessiond/trace-kernel.c | 218 +- src/bin/lttng-sessiond/trace-kernel.h | 28 + src/bin/lttng-sessiond/trace-ust.c | 2 + src/bin/lttng-sessiond/ust-abi-internal.h | 26 + src/bin/lttng-sessiond/ust-app.c | 631 ++++- src/bin/lttng-sessiond/ust-app.h | 36 + src/bin/lttng-sessiond/ust-ctl-internal.h | 8 + src/bin/lttng-sessiond/utils.c | 23 + src/bin/lttng-sessiond/utils.h | 2 + src/bin/lttng/Makefile.am | 8 +- src/bin/lttng/command.h | 3 + src/bin/lttng/commands/add_context.c | 84 +- src/bin/lttng/commands/add_trigger.c | 1727 +++++++++++++ src/bin/lttng/commands/create.c | 4 +- src/bin/lttng/commands/disable_events.c | 2 +- src/bin/lttng/commands/enable_events.c | 417 +--- src/bin/lttng/commands/list_triggers.c | 541 +++++ src/bin/lttng/commands/remove_trigger.c | 131 + src/bin/lttng/commands/track-untrack.c | 2 +- src/bin/lttng/commands/view.c | 4 +- src/bin/lttng/conf.c | 2 +- src/bin/lttng/lttng.c | 5 +- src/bin/lttng/uprobe.c | 387 +++ src/bin/lttng/uprobe.h | 19 + src/bin/lttng/utils.c | 19 + src/bin/lttng/utils.h | 2 + src/common/Makefile.am | 40 +- src/common/action.c | 111 - src/common/actions/action.c | 254 ++ src/common/actions/group.c | 345 +++ src/common/{ => actions}/notify.c | 32 +- src/common/actions/rotate-session.c | 265 ++ src/common/actions/snapshot-session.c | 417 ++++ src/common/actions/start-session.c | 266 ++ src/common/actions/stop-session.c | 266 ++ src/common/argpar/Makefile.am | 3 + src/common/argpar/argpar.c | 746 ++++++ src/common/argpar/argpar.h | 330 +++ src/common/buffer-view.c | 42 + src/common/buffer-view.h | 21 +- src/common/{ => conditions}/buffer-usage.c | 8 +- src/common/{ => conditions}/condition.c | 39 +- src/common/conditions/event-rule.c | 409 ++++ .../{ => conditions}/session-consumed-size.c | 8 +- .../{ => conditions}/session-rotation.c | 11 +- src/common/credentials.c | 31 + src/common/credentials.h | 5 + src/common/domain.c | 46 + src/common/dynamic-array.h | 17 + src/common/error.c | 30 +- src/common/error.h | 58 +- src/common/evaluation.c | 8 + src/common/event-rule-kprobe.c | 522 ++++ src/common/event-rule-kretprobe.c | 164 ++ src/common/event-rule-syscall.c | 492 ++++ src/common/event-rule-tracepoint.c | 1125 +++++++++ src/common/event-rule-uprobe.c | 420 ++++ src/common/event-rule.c | 333 +++ .../lttng-ctl => common}/filter/Makefile.am | 0 .../lttng-ctl => common}/filter/filter-ast.h | 6 +- .../filter/filter-bytecode.h | 0 .../filter/filter-grammar-test.c | 0 .../lttng-ctl => common}/filter/filter-ir.h | 0 .../filter/filter-lexer.l | 0 .../filter/filter-parser.y | 141 +- .../filter/filter-symbols.h | 0 .../filter/filter-visitor-generate-bytecode.c | 0 .../filter/filter-visitor-generate-ir.c | 4 +- ...ilter-visitor-ir-check-binary-comparator.c | 0 ...ilter-visitor-ir-check-binary-op-nesting.c | 0 ...ilter-visitor-ir-normalize-glob-patterns.c | 0 .../filter-visitor-ir-validate-globbing.c | 0 .../filter-visitor-ir-validate-string.c | 0 .../filter/filter-visitor-xml.c | 0 .../lttng-ctl => common}/filter/memstream.h | 0 src/common/kernel-ctl/kernel-ctl.c | 17 +- src/common/kernel-ctl/kernel-ctl.h | 8 +- src/common/kernel-ctl/kernel-ioctl.h | 7 + src/common/lttng-elf.c | 2 +- src/common/lttng-kernel.h | 18 + src/common/notification.c | 3 +- src/common/relayd/relayd.c | 2 +- src/common/runas.c | 121 + src/common/runas.h | 6 + src/common/sessiond-comm/sessiond-comm.h | 1 + src/common/snapshot.c | 184 ++ src/common/snapshot.h | 47 + src/common/trigger.c | 608 ++++- src/common/unix.c | 18 +- src/common/userspace-probe.c | 141 ++ src/common/utils.c | 37 + src/common/utils.h | 12 + src/lib/lttng-ctl/Makefile.am | 5 +- src/lib/lttng-ctl/channel.c | 2 +- src/lib/lttng-ctl/lttng-ctl-health.c | 1 + src/lib/lttng-ctl/lttng-ctl-helper.h | 17 + src/lib/lttng-ctl/lttng-ctl.c | 291 ++- src/lib/lttng-ctl/snapshot.c | 133 +- tests/regression/Makefile.am | 15 +- tests/regression/kernel/select_poll_epoll.c | 53 +- tests/regression/tools/Makefile.am | 2 +- .../regression/tools/exclusion/test_exclusion | 51 +- tests/regression/tools/health/health_exit.c | 19 + tests/regression/tools/health/health_fail.c | 19 + tests/regression/tools/health/health_stall.c | 19 + tests/regression/tools/live/live_test.c | 16 +- .../regression/tools/notification/Makefile.am | 10 +- .../tools/notification/base_client.c | 3 +- .../tools/notification/consumer_testpoints.c | 2 + .../tools/notification/notification.c | 1348 +++++++++-- .../test_notification_kernel_buffer_usage | 126 + .../test_notification_kernel_error | 115 + .../test_notification_kernel_instrumentation | 125 + .../test_notification_kernel_syscall | 125 + .../test_notification_kernel_userspace_probe | 116 + .../test_notification_ust_buffer_usage | 106 + .../notification/test_notification_ust_error | 85 + ...ication_ust_event_rule_condition_exclusion | 85 + .../regression/tools/rotation/schedule_api.c | 22 +- tests/regression/tools/snapshots/ust_test | 2 +- tests/regression/tools/trigger/Makefile.am | 55 + tests/regression/tools/trigger/base_client.c | 252 ++ .../tools/trigger/consumer_testpoints.c | 147 ++ .../tools/trigger/test_add_trigger_cli | 330 +++ .../tools/trigger/test_list_triggers_cli | 301 +++ .../tools/trigger/test_remove_trigger_cli | 110 + .../test_trigger_kernel} | 20 +- .../test_trigger_ust} | 22 +- tests/regression/tools/trigger/trigger.c | 729 ++++++ .../lttng-ust-clock-override-test.c | 1 + tests/regression/ust/fork/fork.c | 2 +- .../lttng-ust-getcpu-override-test.c | 3 +- tests/regression/ust/high-throughput/main.c | 4 +- tests/regression/ust/low-throughput/tp.h | 2 +- tests/regression/ust/multi-lib/callsites.c | 4 +- .../regression/ust/multi-lib/multi-lib-test.c | 15 +- tests/unit/Makefile.am | 20 +- tests/unit/test_buffer_view.c | 50 + tests/unit/test_condition.c | 108 + tests/unit/test_directory_handle.c | 3 +- tests/unit/test_event_rule.c | 152 ++ tests/unit/test_fd_tracker.c | 2 +- ..._relayd_backward_compat_group_by_session.c | 12 +- tests/unit/test_session.c | 4 +- tests/unit/test_utils_expand_path.c | 14 +- tests/unit/test_utils_parse_size_suffix.c | 4 +- tests/unit/test_utils_parse_time_suffix.c | 4 +- tests/utils/tap/tap.c | 10 +- tests/utils/tap/tap.h | 6 +- .../gen-syscall-events-callstack/Makefile.am | 2 +- .../gen-syscall-events/gen-syscall-events.c | 53 +- .../testapp/gen-ust-nevents/gen-ust-nevents.c | 37 +- .../testapp/gen-ust-tracef/gen-ust-tracef.c | 2 +- .../userspace-probe-elf-binary/Makefile.am | 2 +- tests/utils/utils.sh | 28 +- 242 files changed, 23940 insertions(+), 1986 deletions(-) create mode 100644 doc/examples/trigger-on-event/README.md create mode 100755 doc/examples/trigger-on-event/demo.sh create mode 100644 doc/examples/trigger-on-event/instrumented-app.c create mode 100644 doc/examples/trigger-on-event/notification-client.c create mode 100644 doc/examples/trigger-on-event/tp.c create mode 100644 doc/examples/trigger-on-event/tp.h create mode 100644 doc/man/lttng-add-trigger.1.txt create mode 100644 doc/man/lttng-list-triggers.1.txt create mode 100644 doc/man/lttng-remove-trigger.1.txt create mode 100644 include/lttng/action/group-internal.h create mode 100644 include/lttng/action/group.h create mode 100644 include/lttng/action/rotate-session-internal.h create mode 100644 include/lttng/action/rotate-session.h create mode 100644 include/lttng/action/snapshot-session-internal.h create mode 100644 include/lttng/action/snapshot-session.h create mode 100644 include/lttng/action/start-session-internal.h create mode 100644 include/lttng/action/start-session.h create mode 100644 include/lttng/action/stop-session-internal.h create mode 100644 include/lttng/action/stop-session.h create mode 100644 include/lttng/condition/event-rule-internal.h create mode 100644 include/lttng/condition/event-rule.h create mode 100644 include/lttng/domain-internal.h create mode 100644 include/lttng/event-rule/event-rule-internal.h create mode 100644 include/lttng/event-rule/event-rule.h create mode 100644 include/lttng/event-rule/kprobe-internal.h create mode 100644 include/lttng/event-rule/kprobe.h create mode 100644 include/lttng/event-rule/kretprobe-internal.h create mode 100644 include/lttng/event-rule/kretprobe.h create mode 100644 include/lttng/event-rule/syscall-internal.h create mode 100644 include/lttng/event-rule/syscall.h create mode 100644 include/lttng/event-rule/tracepoint-internal.h create mode 100644 include/lttng/event-rule/tracepoint.h create mode 100644 include/lttng/event-rule/uprobe-internal.h create mode 100644 include/lttng/event-rule/uprobe.h create mode 100644 m4/ax_append_compile_flags.m4 create mode 100644 m4/ax_append_flag.m4 create mode 100644 m4/ax_check_compile_flag.m4 create mode 100644 m4/ax_require_defined.m4 create mode 100644 rename.sh create mode 100644 src/bin/lttng-sessiond/action-executor.c create mode 100644 src/bin/lttng-sessiond/action-executor.h create mode 100644 src/bin/lttng/commands/add_trigger.c create mode 100644 src/bin/lttng/commands/list_triggers.c create mode 100644 src/bin/lttng/commands/remove_trigger.c create mode 100644 src/bin/lttng/uprobe.c create mode 100644 src/bin/lttng/uprobe.h delete mode 100644 src/common/action.c create mode 100644 src/common/actions/action.c create mode 100644 src/common/actions/group.c rename src/common/{ => actions}/notify.c (52%) create mode 100644 src/common/actions/rotate-session.c create mode 100644 src/common/actions/snapshot-session.c create mode 100644 src/common/actions/start-session.c create mode 100644 src/common/actions/stop-session.c create mode 100644 src/common/argpar/Makefile.am create mode 100644 src/common/argpar/argpar.c create mode 100644 src/common/argpar/argpar.h rename src/common/{ => conditions}/buffer-usage.c (99%) rename src/common/{ => conditions}/condition.c (79%) create mode 100644 src/common/conditions/event-rule.c rename src/common/{ => conditions}/session-consumed-size.c (99%) rename src/common/{ => conditions}/session-rotation.c (98%) create mode 100644 src/common/credentials.c create mode 100644 src/common/domain.c create mode 100644 src/common/event-rule-kprobe.c create mode 100644 src/common/event-rule-kretprobe.c create mode 100644 src/common/event-rule-syscall.c create mode 100644 src/common/event-rule-tracepoint.c create mode 100644 src/common/event-rule-uprobe.c create mode 100644 src/common/event-rule.c rename src/{lib/lttng-ctl => common}/filter/Makefile.am (100%) rename src/{lib/lttng-ctl => common}/filter/filter-ast.h (95%) rename src/{lib/lttng-ctl => common}/filter/filter-bytecode.h (100%) rename src/{lib/lttng-ctl => common}/filter/filter-grammar-test.c (100%) rename src/{lib/lttng-ctl => common}/filter/filter-ir.h (100%) rename src/{lib/lttng-ctl => common}/filter/filter-lexer.l (100%) rename src/{lib/lttng-ctl => common}/filter/filter-parser.y (82%) rename src/{lib/lttng-ctl => common}/filter/filter-symbols.h (100%) rename src/{lib/lttng-ctl => common}/filter/filter-visitor-generate-bytecode.c (100%) rename src/{lib/lttng-ctl => common}/filter/filter-visitor-generate-ir.c (99%) rename src/{lib/lttng-ctl => common}/filter/filter-visitor-ir-check-binary-comparator.c (100%) rename src/{lib/lttng-ctl => common}/filter/filter-visitor-ir-check-binary-op-nesting.c (100%) rename src/{lib/lttng-ctl => common}/filter/filter-visitor-ir-normalize-glob-patterns.c (100%) rename src/{lib/lttng-ctl => common}/filter/filter-visitor-ir-validate-globbing.c (100%) rename src/{lib/lttng-ctl => common}/filter/filter-visitor-ir-validate-string.c (100%) rename src/{lib/lttng-ctl => common}/filter/filter-visitor-xml.c (100%) rename src/{lib/lttng-ctl => common}/filter/memstream.h (100%) create mode 100644 src/common/snapshot.c create mode 100644 src/common/snapshot.h create mode 100755 tests/regression/tools/notification/test_notification_kernel_buffer_usage create mode 100755 tests/regression/tools/notification/test_notification_kernel_error create mode 100755 tests/regression/tools/notification/test_notification_kernel_instrumentation create mode 100755 tests/regression/tools/notification/test_notification_kernel_syscall create mode 100755 tests/regression/tools/notification/test_notification_kernel_userspace_probe create mode 100755 tests/regression/tools/notification/test_notification_ust_buffer_usage create mode 100755 tests/regression/tools/notification/test_notification_ust_error create mode 100755 tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion create mode 100644 tests/regression/tools/trigger/Makefile.am create mode 100644 tests/regression/tools/trigger/base_client.c create mode 100644 tests/regression/tools/trigger/consumer_testpoints.c create mode 100755 tests/regression/tools/trigger/test_add_trigger_cli create mode 100755 tests/regression/tools/trigger/test_list_triggers_cli create mode 100755 tests/regression/tools/trigger/test_remove_trigger_cli rename tests/regression/tools/{notification/test_notification_kernel => trigger/test_trigger_kernel} (72%) rename tests/regression/tools/{notification/test_notification_ust => trigger/test_trigger_ust} (67%) create mode 100644 tests/regression/tools/trigger/trigger.c create mode 100644 tests/unit/test_buffer_view.c create mode 100644 tests/unit/test_condition.c create mode 100644 tests/unit/test_event_rule.c diff --git a/.gitignore b/.gitignore index 34f133a25..b92dac27c 100644 --- a/.gitignore +++ b/.gitignore @@ -61,11 +61,11 @@ compile_commands.json /src/bin/lttng-crash/lttng-crash /src/bin/lttng-relayd/lttng-relayd /src/lib/lttng-ctl/lttng-ctl.pc -/src/lib/lttng-ctl/filter/filter-grammar-test -/src/lib/lttng-ctl/filter/filter-lexer.c -/src/lib/lttng-ctl/filter/filter-parser.c -/src/lib/lttng-ctl/filter/filter-parser.h -/src/lib/lttng-ctl/filter/filter-parser.output +/src/common/filter/filter-grammar-test +/src/common/filter/filter-lexer.c +/src/common/filter/filter-parser.c +/src/common/filter/filter-parser.h +/src/common/filter/filter-parser.output /extras/bindings/swig/python/lttng.i /extras/bindings/swig/python/lttng.py @@ -88,6 +88,8 @@ compile_commands.json /tests/unit/test_directory_handle /tests/unit/test_relayd_backward_compat_group_by_session /tests/unit/test_fd_tracker +/tests/unit/test_event_rule +/tests/unit/test_condition kernel_all_events_basic kernel_event_basic ust_global_event_wildcard @@ -105,6 +107,8 @@ health_check /tests/regression/tools/notification/notification /tests/regression/tools/rotation/schedule_api /tests/regression/tools/notification/rotation +/tests/regression/tools/trigger/base_client +/tests/regression/tools/trigger/trigger /tests/regression/ust/overlap/demo/demo /tests/regression/ust/linking/demo_builtin /tests/regression/ust/linking/demo_static diff --git a/configure.ac b/configure.ac index 852c61f0e..2ea9571ff 100644 --- a/configure.ac +++ b/configure.ac @@ -26,6 +26,31 @@ AC_PROG_CXX RW_PROG_CXX_WORKS AM_CONDITIONAL([CXX_WORKS], [test "x$rw_cv_prog_cxx_works" = "xyes"]) +# Detect warning flags supported by the compiler, append them to WARN_CFLAGS. +# +# Pass -Werror as an extra flag during the test: this is needed to make the +# -Wunknown-warning-option diagnostic fatal with clang. +AX_APPEND_COMPILE_FLAGS([ dnl + -Wall dnl + dnl We currently get this warning when building with Clang: + dnl + dnl /usr/include/setjmp.h:54:12: error: declaration of built-in function '__sigsetjmp' requires the declaration of the 'jmp_buf' type, commonly provided in the header . [-Werror,-Wincomplete-setjmp-declaration] + dnl extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __THROWNL; + dnl ^ + -Wno-incomplete-setjmp-declaration dnl + -Wdiscarded-qualifiers dnl + ], + [WARN_CFLAGS], + [-Werror]) + +# When given, add -Werror to WARN_CFLAGS. +AC_ARG_ENABLE([Werror], + [AS_HELP_STRING([--enable-Werror], [Treat compiler warnings as errors.])] +) +AS_IF([test "x$enable_Werror" = "xyes"], + [WARN_CFLAGS="${WARN_CFLAGS} -Werror"] +) + # Checks for programs. AC_PROG_GREP AC_PROG_MAKE_SET @@ -1038,16 +1063,20 @@ AM_CONDITIONAL([BUILD_LIB_SESSIOND_COMM], [test x$build_lib_sessiond_comm = xyes AM_CONDITIONAL([BUILD_LIB_TESTPOINT], [test x$build_lib_testpoint = xyes]) AM_CONDITIONAL([BUILD_LIB_UST_CONSUMER], [test x$build_lib_ust_consumer = xyes]) -AM_CFLAGS="-Wall -fno-strict-aliasing $PTHREAD_CFLAGS" +AM_CFLAGS="${WARN_CFLAGS} -fno-strict-aliasing $PTHREAD_CFLAGS" AC_SUBST(AM_CFLAGS) +# This is set even though it is empty, so Makefiles can do "AM_LDFLAGS += ...". +AM_LDFLAGS="" +AC_SUBST(AM_LDFLAGS) + # The order in which the include folders are searched is important. # The top_builddir should always be searched first in the event that a build # time generated file is included. An example of this is the "version.i" file. # In a scenario where lttng-tools is built from a distribution tarball and in a # out-of-tree manner, the generated "version.i" has priority on the one from # the source (distribution tarball) and must be found first. -AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -I\$(top_srcdir)/src -include config.h $AM_CPPFLAGS" +AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -I\$(top_builddir)/src -I\$(top_srcdir)/src -include config.h $AM_CPPFLAGS" AC_SUBST(AM_CPPFLAGS) lttngincludedir="${includedir}/lttng" @@ -1065,6 +1094,9 @@ AC_SUBST(lttngnotificationincludedir) lttngtriggerincludedir="${includedir}/lttng/trigger" AC_SUBST(lttngtriggerincludedir) +lttngeventruleincludedir="${includedir}/lttng/event-rule" +AC_SUBST(lttngeventruleincludedir) + lttnglibexecdir="${libdir}/lttng/libexec" AC_SUBST(lttnglibexecdir) @@ -1081,6 +1113,7 @@ AC_CONFIG_FILES([ extras/core-handler/Makefile src/Makefile src/common/Makefile + src/common/argpar/Makefile src/common/kernel-ctl/Makefile src/common/kernel-consumer/Makefile src/common/consumer/Makefile @@ -1095,9 +1128,9 @@ AC_CONFIG_FILES([ src/common/config/Makefile src/common/string-utils/Makefile src/common/fd-tracker/Makefile + src/common/filter/Makefile src/lib/Makefile src/lib/lttng-ctl/Makefile - src/lib/lttng-ctl/filter/Makefile src/lib/lttng-ctl/lttng-ctl.pc src/bin/Makefile src/bin/lttng-consumerd/Makefile @@ -1132,6 +1165,7 @@ AC_CONFIG_FILES([ tests/regression/tools/working-directory/Makefile tests/regression/tools/relayd-grouping/Makefile tests/regression/tools/clear/Makefile + tests/regression/tools/trigger/Makefile tests/regression/ust/Makefile tests/regression/ust/nprocesses/Makefile tests/regression/ust/high-throughput/Makefile diff --git a/doc/examples/trigger-on-event/README.md b/doc/examples/trigger-on-event/README.md new file mode 100644 index 000000000..2c184f727 --- /dev/null +++ b/doc/examples/trigger-on-event/README.md @@ -0,0 +1,56 @@ +# Trigger notification exemple + +## Description +This exemple is made-up of three executables. + +### `notification-client` + +``` +Usage: notification-client TRIGGER_NAME +``` + +A simple client that subscribes to the notifications emitted by the `TRIGGER_NAME` trigger. + +### `instrumented-app` + +An application that emits the `trigger_exemple:my_event` event every 2 seconds. + +### `demo.sh` + +This script adds a trigger named `demo_trigger` which emits a notification when the user-space `trigger_exemple:my_event` event occurs. + +Once the trigger has been setup, the notification-client is launched to print all notifications emitted by the `demo_trigger` trigger. + +## Building + +Simply run the included Makefile. + +## Running the exemple + +1) Launch a session daemon using: + ``` + $ lttng-sessiond + ``` +2) Launch the `demo.sh` script +3) Launch the `instrumented-app` + +The following output should be produced: + +``` +$ ./demo.sh +Registering a notification trigger named "demo_trigger" for the trigger_exemple:my_event user-space event +Trigger registered successfully. +Subscribed to notifications of trigger "demo_trigger" +[02-14-2020] 18:13:34.779766 - Received notification of event rule trigger "demo_trigger" +[02-14-2020] 18:13:36.779888 - Received notification of event rule trigger "demo_trigger" +[02-14-2020] 18:13:38.780514 - Received notification of event rule trigger "demo_trigger" +[02-14-2020] 18:13:40.780656 - Received notification of event rule trigger "demo_trigger" +``` + +``` +$ ./instrumented-app +[02-14-2020] 18:13:34.779433 - Tracing event "trigger_exemple:my_event" +[02-14-2020] 18:13:36.779693 - Tracing event "trigger_exemple:my_event" +[02-14-2020] 18:13:38.780010 - Tracing event "trigger_exemple:my_event" +[02-14-2020] 18:13:40.780286 - Tracing event "trigger_exemple:my_event" +``` diff --git a/doc/examples/trigger-on-event/demo.sh b/doc/examples/trigger-on-event/demo.sh new file mode 100755 index 000000000..61623fbb8 --- /dev/null +++ b/doc/examples/trigger-on-event/demo.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (C) 2020 Jérémie Galarneau +# +# SPDX-License-Identifier: MIT + +EVENT_NAME=trigger_exemple:my_event +TRIGGER_NAME=demo_trigger + +lttng list > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "Could not connect to session daemon, are you sure it is running?" + exit 1 +fi + +echo "Registering a notification trigger named \"$TRIGGER_NAME\" for the $EVENT_NAME user-space event" +lttng add-trigger --id $TRIGGER_NAME --condition on-event --userspace $EVENT_NAME --action notify + +./notification-client $TRIGGER_NAME + diff --git a/doc/examples/trigger-on-event/instrumented-app.c b/doc/examples/trigger-on-event/instrumented-app.c new file mode 100644 index 000000000..24900567d --- /dev/null +++ b/doc/examples/trigger-on-event/instrumented-app.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include "tp.h" + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + uint64_t i; + + for (i = 0; i < UINT64_MAX; i++) { + char time_str[64]; + struct timeval tv; + time_t the_time; + + gettimeofday(&tv, NULL); + the_time = tv.tv_sec; + + strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T", + localtime(&the_time)); + printf("%s.%ld - Tracing event \"trigger_exemple:my_event\"\n", time_str, tv.tv_usec); + + tracepoint(trigger_exemple, my_event, i); + sleep(2); + } + return 0; +} diff --git a/doc/examples/trigger-on-event/notification-client.c b/doc/examples/trigger-on-event/notification-client.c new file mode 100644 index 000000000..217b5c7d0 --- /dev/null +++ b/doc/examples/trigger-on-event/notification-client.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +bool action_group_contains_notify(const struct lttng_action *action_group) +{ + unsigned int i, count; + enum lttng_action_status status = + lttng_action_group_get_count(action_group, &count); + + if (status != LTTNG_ACTION_STATUS_OK) { + printf("Failed to get action count from action group\n"); + exit(1); + } + + for (i = 0; i < count; i++) { + const struct lttng_action *action = + lttng_action_group_get_at_index_const( + action_group, i); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + + if (action_type == LTTNG_ACTION_TYPE_NOTIFY) { + return true; + } + } + return false; +} + +int print_notification(struct lttng_notification *notification) +{ + int ret = 0; + const struct lttng_condition *condition = + lttng_notification_get_condition(notification); + const struct lttng_evaluation *evaluation = + lttng_notification_get_evaluation(notification); + const enum lttng_condition_type type = + lttng_evaluation_get_type(evaluation); + + switch (type) { + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + printf("Received consumed size notification\n"); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + printf("Received buffer usage notification\n"); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + printf("Received session rotation ongoing notification\n"); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + printf("Received session rotation completed notification\n"); + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + { + const char *trigger_name; + enum lttng_evaluation_status evaluation_status; + char time_str[64]; + struct timeval tv; + time_t the_time; + + gettimeofday(&tv, NULL); + the_time = tv.tv_sec; + + strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T", + localtime(&the_time)); + printf("%s.%ld - ", time_str, tv.tv_usec); + + evaluation_status = + lttng_evaluation_event_rule_get_trigger_name( + evaluation, &trigger_name); + if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { + fprintf(stderr, "Failed to get trigger name of event rule notification\n"); + ret = -1; + break; + } + + printf("Received notification of event rule trigger \"%s\"\n", + trigger_name); + break; + } + default: + fprintf(stderr, "Unknown notification type (%d)\n", type); + } + + return ret; +} + +int main(int argc, char **argv) +{ + int ret; + struct lttng_triggers *triggers = NULL; + unsigned int count, i, subcription_count = 0; + enum lttng_trigger_status trigger_status; + struct lttng_notification_channel *notification_channel = NULL; + + if (argc != 2) { + fprintf(stderr, "Missing trigger name\n"); + fprintf(stderr, "Usage: notification-client TRIGGER_NAME\n"); + goto end; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + if (!notification_channel) { + fprintf(stderr, "Failed to create notification channel\n"); + ret = -1; + goto end; + } + + ret = lttng_list_triggers(&triggers); + if (ret) { + fprintf(stderr, "Failed to list triggers\n"); + goto end; + } + + trigger_status = lttng_triggers_get_count(triggers, &count); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to get trigger count\n"); + ret = -1; + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_trigger *trigger = + lttng_triggers_get_at_index(triggers, i); + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const struct lttng_action *action = + lttng_trigger_get_const_action(trigger); + const enum lttng_action_type action_type = + lttng_action_get_type(action); + enum lttng_notification_channel_status channel_status; + const char *trigger_name = NULL; + enum lttng_trigger_status trigger_status; + + lttng_trigger_get_name(trigger, &trigger_name); + if (strcmp(trigger_name, argv[1])) { + continue; + } + + if (!((action_type == LTTNG_ACTION_TYPE_GROUP && + action_group_contains_notify(action)) || + action_type == LTTNG_ACTION_TYPE_NOTIFY)) { + printf("The action of trigger \"%s\" is not \"notify\", skipping.\n", + trigger_name); + continue; + } + + channel_status = lttng_notification_channel_subscribe( + notification_channel, condition); + if (channel_status) { + fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n", + trigger_name); + ret = -1; + goto end; + } + + printf("Subscribed to notifications of trigger \"%s\"\n", + trigger_name); + subcription_count++; + } + + if (subcription_count == 0) { + printf("No matching trigger with a notify action found.\n"); + ret = 0; + goto end; + } + + for (;;) { + struct lttng_notification *notification; + enum lttng_notification_channel_status channel_status; + + channel_status = + lttng_notification_channel_get_next_notification( + notification_channel, + ¬ification); + switch (channel_status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + printf("Dropped notification\n"); + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED: + ret = 0; + goto end; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: + printf("Notification channel was closed by peer.\n"); + break; + default: + fprintf(stderr, "A communication error occurred on the notification channel.\n"); + ret = -1; + goto end; + } + + ret = print_notification(notification); + lttng_notification_destroy(notification); + if (ret) { + goto end; + } + } +end: + lttng_triggers_destroy(triggers); + lttng_notification_channel_destroy(notification_channel); + return !!ret; +} diff --git a/doc/examples/trigger-on-event/tp.c b/doc/examples/trigger-on-event/tp.c new file mode 100644 index 000000000..e1745ed61 --- /dev/null +++ b/doc/examples/trigger-on-event/tp.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES +#include "tp.h" diff --git a/doc/examples/trigger-on-event/tp.h b/doc/examples/trigger-on-event/tp.h new file mode 100644 index 000000000..ae4593a46 --- /dev/null +++ b/doc/examples/trigger-on-event/tp.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER trigger_exemple + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./tp.h" + +#if !defined(_TRACEPOINT_TRIGGER_EXEMPLE_H) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_TRIGGER_EXEMPLE_H + +#include + +TRACEPOINT_EVENT(trigger_exemple, my_event, + TP_ARGS(int, iteration), + TP_FIELDS( + ctf_integer(uint64_t, iteration, iteration) + ) +) + +#endif /* _TRACEPOINT_TRIGGER_EXEMPLE_H */ + +#include diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 80bedbadf..5ae6ffbe3 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -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 index 000000000..393e8c920 --- /dev/null +++ b/doc/man/lttng-add-trigger.1.txt @@ -0,0 +1,134 @@ +lttng-add-trigger(1) +===================== +:revdate: 17 January 2020 + + +NAME +---- +lttng-add-trigger - Create LTTng triggers + + +SYNOPSIS +-------- + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *add-trigger* [--id ID] + [--fire-every N] [--fire-once-after N] + --condition CONDITION-NAME CONDITION-ARGS + --action ACTION-NAME ACTION-ARGS + [--action ACTION-NAME ACTION-ARGS]... + + +DESCRIPTION +----------- + +The `lttng add-trigger` command is used to create triggers. A +trigger is an association between a *condition* and one or more +*actions*. When the condition associated to a trigger is met, the +actions associated to that trigger are executed. The tracing does not +have to be active for the conditions to be met, and triggers are +independent from tracing sessions. + +By default, a trigger fires every time its condition is met. The +'--fire-every' and '--fire-once-after' options can be used to change +this mode. + +The syntax by which conditions and actions are specified is described +below. + +[[conditions]] +Conditions +~~~~~~~~~~ + +Conditions are specified with the `--condition` option, followed by a +condition name, and possibly some more arguments, depending on the +specific condition. There must be exactly one condition given in the +`lttng add-trigger` invocation. + +The available conditions are: + +Event rule: `on-event [event rule arguments]`:: + This type of condition is met when the tracer encounters an event + matching the given even rule. The arguments describing the event + rule are the same as those describing the event rules of the + man:lttng-enable-event(1) command, with these exceptions: + + - It is not possible to use filter operands that use values from + the context. + +[[actions]] +Actions +~~~~~~~ + +Actions are specified with the `--action` option, followed by an action +name, and possibly some more arguments, depending on the specific +action. There must be at least one action given in the +`lttng add-trigger` invocation. + +The available actions are: + +Notify: *notify*:: + This action causes the LTTng session daemon to send a notification, + through its notification mechanism (see man:lttng-sessiond(8)). + Some details about the condition evaluation are sent along with the + notification. + +Start session: *start-session* session-name:: + This action causes the LTTng session daemon to start tracing for the + session with the given name. If no session with the given name exist + at the time the condition is met, nothing is done. + +Stop session: *stop-session* session-name:: + This action causes the LTTng session daemon to stop tracing for the + session with the given name. If no session with the given name exist + at the time the condition is met, nothing is done. + +Rotate session: *rotate-session* session-name:: + This action causes the LTTng session daemon to rotate the session + with the given name. See man:lttng-rotate(1) for more information + about the session rotation concept. If no session with the given + name exist at the time the condition is met, nothing is done. + +Snapshot session: *snapshot-session* session-name:: + This action causes the LTTng session daemon to take a snapshot of the + session with the given name. See man:lttng-snapshot(1) for more + information about the session snapshot concept. If no session with + the given name exist at the time the condition is met, nothing is + done. + +OPTIONS +------- + +option:--condition:: + Define the condition for the trigger. See the + <> section for more details. + +option:--action:: + Define an action for the trigger. See the <> + 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 index 000000000..2ab3085cd --- /dev/null +++ b/doc/man/lttng-list-triggers.1.txt @@ -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 index 000000000..645ef152f --- /dev/null +++ b/doc/man/lttng-remove-trigger.1.txt @@ -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) diff --git a/extras/bindings/swig/python/lttng.i.in b/extras/bindings/swig/python/lttng.i.in index e99c9aecf..b6b6f960e 100644 --- a/extras/bindings/swig/python/lttng.i.in +++ b/extras/bindings/swig/python/lttng.i.in @@ -29,6 +29,9 @@ multiple concurrent processes and threads. Tracing across multiple systems is al // This makes the typemap code useable with both Python 2 and 3. #define PyInt_AsSsize_t PyLong_AsSsize_t #endif + +// Avoid -Wmissing-declarations warning. +PyObject *SWIG_init(void); %} typedef unsigned int uint32_t; diff --git a/include/Makefile.am b/include/Makefile.am index ccd0d87d6..4fdd78c7d 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -121,11 +121,17 @@ lttnginclude_HEADERS = \ lttngactioninclude_HEADERS= \ lttng/action/action.h \ - lttng/action/notify.h + lttng/action/group.h \ + lttng/action/notify.h \ + lttng/action/rotate-session.h \ + lttng/action/snapshot-session.h \ + lttng/action/start-session.h \ + lttng/action/stop-session.h lttngconditioninclude_HEADERS= \ lttng/condition/condition.h \ lttng/condition/buffer-usage.h \ + lttng/condition/event-rule.h \ lttng/condition/session-consumed-size.h \ lttng/condition/session-rotation.h \ lttng/condition/evaluation.h @@ -137,6 +143,14 @@ lttngnotificationinclude_HEADERS= \ lttngtriggerinclude_HEADERS= \ lttng/trigger/trigger.h +lttngeventruleinclude_HEADERS= \ + lttng/event-rule/event-rule.h \ + lttng/event-rule/kprobe.h \ + lttng/event-rule/kretprobe.h \ + lttng/event-rule/syscall.h \ + lttng/event-rule/tracepoint.h \ + lttng/event-rule/uprobe.h + noinst_HEADERS = \ lttng/snapshot-internal.h \ lttng/health-internal.h \ @@ -146,6 +160,7 @@ noinst_HEADERS = \ lttng/action/notify-internal.h \ lttng/condition/condition-internal.h \ lttng/condition/buffer-usage-internal.h \ + lttng/condition/event-rule-internal.h \ lttng/condition/session-consumed-size-internal.h \ lttng/condition/evaluation-internal.h \ lttng/condition/session-rotation-internal.h \ @@ -162,5 +177,11 @@ noinst_HEADERS = \ lttng/session-internal.h \ lttng/session-descriptor-internal.h \ lttng/tracker-internal.h \ + lttng/event-rule/event-rule-internal.h \ + lttng/event-rule/kprobe-internal.h \ + lttng/event-rule/kretprobe-internal.h \ + lttng/event-rule/uprobe-internal.h \ + lttng/event-rule/syscall-internal.h \ + lttng/event-rule/tracepoint-internal.h \ version.h \ version.i diff --git a/include/lttng/action/action-internal.h b/include/lttng/action/action-internal.h index 787e9087a..af52a07ff 100644 --- a/include/lttng/action/action-internal.h +++ b/include/lttng/action/action-internal.h @@ -14,16 +14,24 @@ #include #include #include +#include typedef bool (*action_validate_cb)(struct lttng_action *action); typedef void (*action_destroy_cb)(struct lttng_action *action); typedef int (*action_serialize_cb)(struct lttng_action *action, struct lttng_dynamic_buffer *buf); +typedef bool (*action_equal_cb)(const struct lttng_action *a, + const struct lttng_action *b); +typedef ssize_t (*action_create_from_buffer_cb)( + const struct lttng_buffer_view *view, + struct lttng_action **action); struct lttng_action { + struct urcu_ref ref; enum lttng_action_type type; action_validate_cb validate; action_serialize_cb serialize; + action_equal_cb equal; action_destroy_cb destroy; }; @@ -32,6 +40,14 @@ struct lttng_action_comm { int8_t action_type; } LTTNG_PACKED; +LTTNG_HIDDEN +void lttng_action_init(struct lttng_action *action, + enum lttng_action_type type, + action_validate_cb validate, + action_serialize_cb serialize, + action_equal_cb equal, + action_destroy_cb destroy); + LTTNG_HIDDEN bool lttng_action_validate(struct lttng_action *action); @@ -47,4 +63,17 @@ LTTNG_HIDDEN enum lttng_action_type lttng_action_get_type_const( const struct lttng_action *action); +LTTNG_HIDDEN +bool lttng_action_is_equal(const struct lttng_action *a, + const struct lttng_action *b); + +LTTNG_HIDDEN +void lttng_action_get(struct lttng_action *action); + +LTTNG_HIDDEN +void lttng_action_put(struct lttng_action *action); + +LTTNG_HIDDEN +const char* lttng_action_type_string(enum lttng_action_type action_type); + #endif /* LTTNG_ACTION_INTERNAL_H */ diff --git a/include/lttng/action/action.h b/include/lttng/action/action.h index f3f8d9dee..be7e397d0 100644 --- a/include/lttng/action/action.h +++ b/include/lttng/action/action.h @@ -17,6 +17,19 @@ extern "C" { enum lttng_action_type { LTTNG_ACTION_TYPE_UNKNOWN = -1, LTTNG_ACTION_TYPE_NOTIFY = 0, + LTTNG_ACTION_TYPE_START_SESSION = 1, + LTTNG_ACTION_TYPE_STOP_SESSION = 2, + LTTNG_ACTION_TYPE_ROTATE_SESSION = 3, + LTTNG_ACTION_TYPE_SNAPSHOT_SESSION = 4, + LTTNG_ACTION_TYPE_GROUP = 5, +}; + +enum lttng_action_status { + LTTNG_ACTION_STATUS_OK = 0, + LTTNG_ACTION_STATUS_ERROR = -1, + LTTNG_ACTION_STATUS_UNKNOWN = -2, + LTTNG_ACTION_STATUS_INVALID = -3, + LTTNG_ACTION_STATUS_UNSET = -4, }; /* @@ -25,7 +38,7 @@ enum lttng_action_type { * Returns the type of an action on success, LTTNG_ACTION_TYPE_UNKNOWN on error. */ extern enum lttng_action_type lttng_action_get_type( - struct lttng_action *action); + const struct lttng_action *action); /* * Destroy (frees) an action object. diff --git a/include/lttng/action/group-internal.h b/include/lttng/action/group-internal.h new file mode 100644 index 000000000..42847953f --- /dev/null +++ b/include/lttng/action/group-internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_GROUP_INTERNAL_H +#define LTTNG_ACTION_GROUP_INTERNAL_H + +#include + +#include + +struct lttng_action; +struct lttng_buffer_view; + +/* + * Create an action group from a buffer view. + * + * On success, return the number of bytes consumed from `view`, and the created + * group in `*group`. On failure, return -1. + */ +LTTNG_HIDDEN +extern ssize_t lttng_action_group_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **group); + +#endif /* LTTNG_ACTION_GROUP_INTERNAL_H */ diff --git a/include/lttng/action/group.h b/include/lttng/action/group.h new file mode 100644 index 000000000..a49660d27 --- /dev/null +++ b/include/lttng/action/group.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 EfficiOS, inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_GROUP_H +#define LTTNG_ACTION_GROUP_H + +struct lttng_action; +struct lttng_action_group; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Create a newly allocated action group object. + * + * Returns a new action group on success, NULL on failure. This action group + * must be destroyed using lttng_action_group_destroy(). + */ +extern struct lttng_action *lttng_action_group_create(void); + +/* + * Add an action to an lttng_action object of type LTTNG_ACTION_GROUP. + * + * The group takes ownership of the action. + */ +extern enum lttng_action_status lttng_action_group_add_action( + struct lttng_action *group, struct lttng_action *action); + +extern enum lttng_action_status lttng_action_group_get_count( + const struct lttng_action *group, unsigned int *count); + +extern const struct lttng_action *lttng_action_group_get_at_index_const( + const struct lttng_action *group, + unsigned int index); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_GROUP_H */ diff --git a/include/lttng/action/notify-internal.h b/include/lttng/action/notify-internal.h index e6cb174ce..a9d50612d 100644 --- a/include/lttng/action/notify-internal.h +++ b/include/lttng/action/notify-internal.h @@ -15,4 +15,9 @@ struct lttng_action_notify { struct lttng_action parent; }; +LTTNG_HIDDEN +ssize_t lttng_action_notify_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **action); + #endif /* LTTNG_ACTION_NOTIFY_INTERNAL_H */ diff --git a/include/lttng/action/rotate-session-internal.h b/include/lttng/action/rotate-session-internal.h new file mode 100644 index 000000000..b3389f729 --- /dev/null +++ b/include/lttng/action/rotate-session-internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_ROTATE_SESSION_INTERNAL_H +#define LTTNG_ACTION_ROTATE_SESSION_INTERNAL_H + +#include + +#include + +struct lttng_action; +struct lttng_buffer_view; + +/* + * Create a "rotate session" action from a buffer view. + * + * On success, return the number of bytes consumed from `view`, and the created + * action in `*action`. On failure, return -1. + */ +LTTNG_HIDDEN +extern ssize_t lttng_action_rotate_session_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **action); + +#endif /* LTTNG_ACTION_ROTATE_SESSION_INTERNAL_H */ diff --git a/include/lttng/action/rotate-session.h b/include/lttng/action/rotate-session.h new file mode 100644 index 000000000..c7112bba8 --- /dev/null +++ b/include/lttng/action/rotate-session.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 EfficiOS, inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_ROTATE_SESSION_H +#define LTTNG_ACTION_ROTATE_SESSION_H + +struct lttng_action; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Create a newly allocated rotate-session action object. + * + * Returns a new action on success, NULL on failure. This action must be + * destroyed using lttng_action_destroy(). + */ +extern struct lttng_action *lttng_action_rotate_session_create(void); + +/* + * Set the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_ROTATE_SESSION. + */ +extern enum lttng_action_status lttng_action_rotate_session_set_session_name( + struct lttng_action *action, const char *session_name); + +/* + * Get the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_ROTATE_SESSION. + */ +extern enum lttng_action_status lttng_action_rotate_session_get_session_name( + const struct lttng_action *action, const char **session_name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_ROTATE_SESSION_H */ diff --git a/include/lttng/action/snapshot-session-internal.h b/include/lttng/action/snapshot-session-internal.h new file mode 100644 index 000000000..0a3e0e96c --- /dev/null +++ b/include/lttng/action/snapshot-session-internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H +#define LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H + +#include + +#include + +struct lttng_action; +struct lttng_buffer_view; + +/* + * Create a "snapshot session" action from a buffer view. + * + * On success, return the number of bytes consumed from `view`, and the created + * action in `*action`. On failure, return -1. + */ +LTTNG_HIDDEN +extern ssize_t lttng_action_snapshot_session_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **action); + +#endif /* LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H */ diff --git a/include/lttng/action/snapshot-session.h b/include/lttng/action/snapshot-session.h new file mode 100644 index 000000000..eeb75e6e3 --- /dev/null +++ b/include/lttng/action/snapshot-session.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 EfficiOS, inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_SNAPSHOT_SESSION_H +#define LTTNG_ACTION_SNAPSHOT_SESSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_action; +struct lttng_snapshot_output; + +/* + * Create a newly allocated snapshot-session action object. + * + * Returns a new action on success, NULL on failure. This action must be + * destroyed using lttng_action_destroy(). + */ +extern struct lttng_action *lttng_action_snapshot_session_create(void); + +/* + * Set the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_SNAPSHOT_SESSION. + */ +extern enum lttng_action_status lttng_action_snapshot_session_set_session_name( + struct lttng_action *action, const char *session_name); + +/* + * Get the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_SNAPSHOT_SESSION. + */ +extern enum lttng_action_status lttng_action_snapshot_session_get_session_name( + const struct lttng_action *action, const char **session_name); + +/* + * Set an explicit snapshot output for this snapshot session action. + * + * The given snapshot output will be used instead of the session's + * default snapshot output. + * + * This function takes ownership of the given snapshot output. + */ +extern enum lttng_action_status lttng_action_snapshot_session_set_output( + struct lttng_action *action, + struct lttng_snapshot_output *output); + +/* + * Get the explicit snapshot output for this snapshot session action. + */ +extern enum lttng_action_status lttng_action_snapshot_session_get_output_const( + const struct lttng_action *action, + const struct lttng_snapshot_output **output); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_SNAPSHOT_SESSION_H */ diff --git a/include/lttng/action/start-session-internal.h b/include/lttng/action/start-session-internal.h new file mode 100644 index 000000000..496bee28c --- /dev/null +++ b/include/lttng/action/start-session-internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_START_SESSION_INTERNAL_H +#define LTTNG_ACTION_START_SESSION_INTERNAL_H + +#include + +#include + +struct lttng_action; +struct lttng_buffer_view; + +/* + * Create a "start session" action from a buffer view. + * + * On success, return the number of bytes consumed from `view`, and the created + * action in `*action`. On failure, return -1. + */ +LTTNG_HIDDEN +extern ssize_t lttng_action_start_session_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **action); + +#endif /* LTTNG_ACTION_START_SESSION_INTERNAL_H */ diff --git a/include/lttng/action/start-session.h b/include/lttng/action/start-session.h new file mode 100644 index 000000000..b9eda708c --- /dev/null +++ b/include/lttng/action/start-session.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 EfficiOS, inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_START_SESSION_H +#define LTTNG_ACTION_START_SESSION_H + +struct lttng_action; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Create a newly allocated start-session action object. + * + * Returns a new action on success, NULL on failure. This action must be + * destroyed using lttng_action_destroy(). + */ +extern struct lttng_action *lttng_action_start_session_create(void); + +/* + * Set the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_START_SESSION. + */ +extern enum lttng_action_status lttng_action_start_session_set_session_name( + struct lttng_action *action, const char *session_name); + +/* + * Get the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_START_SESSION. + */ +extern enum lttng_action_status lttng_action_start_session_get_session_name( + const struct lttng_action *action, const char **session_name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_START_SESSION_H */ diff --git a/include/lttng/action/stop-session-internal.h b/include/lttng/action/stop-session-internal.h new file mode 100644 index 000000000..7a453ea06 --- /dev/null +++ b/include/lttng/action/stop-session-internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_STOP_SESSION_INTERNAL_H +#define LTTNG_ACTION_STOP_SESSION_INTERNAL_H + +#include + +#include + +struct lttng_action; +struct lttng_buffer_view; + +/* + * Create a "stop session" action from a buffer view. + * + * On success, return the number of bytes consumed from `view`, and the created + * action in `*action`. On failure, return -1. + */ +LTTNG_HIDDEN +extern ssize_t lttng_action_stop_session_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **action); + +#endif /* LTTNG_ACTION_STOP_SESSION_INTERNAL_H */ diff --git a/include/lttng/action/stop-session.h b/include/lttng/action/stop-session.h new file mode 100644 index 000000000..cf9eaab83 --- /dev/null +++ b/include/lttng/action/stop-session.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 EfficiOS, inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_STOP_SESSION_H +#define LTTNG_ACTION_STOP_SESSION_H + +struct lttng_action; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Create a newly allocated stop-session action object. + * + * Returns a new action on success, NULL on failure. This action must be + * destroyed using lttng_action_destroy(). + */ +extern struct lttng_action *lttng_action_stop_session_create(void); + +/* + * Set the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_STOP_SESSION. + */ +extern enum lttng_action_status lttng_action_stop_session_set_session_name( + struct lttng_action *action, const char *session_name); + +/* + * Get the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_STOP_SESSION. + */ +extern enum lttng_action_status lttng_action_stop_session_get_session_name( + const struct lttng_action *action, const char **session_name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_STOP_SESSION_H */ diff --git a/include/lttng/condition/condition-internal.h b/include/lttng/condition/condition-internal.h index 60686d29f..2eaf9292f 100644 --- a/include/lttng/condition/condition-internal.h +++ b/include/lttng/condition/condition-internal.h @@ -21,7 +21,8 @@ typedef void (*condition_destroy_cb)(struct lttng_condition *condition); typedef bool (*condition_validate_cb)(const struct lttng_condition *condition); typedef int (*condition_serialize_cb)( const struct lttng_condition *condition, - struct lttng_dynamic_buffer *buf); + struct lttng_dynamic_buffer *buf, + int *fd_to_send); typedef bool (*condition_equal_cb)(const struct lttng_condition *a, const struct lttng_condition *b); typedef ssize_t (*condition_create_from_buffer_cb)( @@ -56,10 +57,14 @@ ssize_t lttng_condition_create_from_buffer( LTTNG_HIDDEN int lttng_condition_serialize(const struct lttng_condition *condition, - struct lttng_dynamic_buffer *buf); + struct lttng_dynamic_buffer *buf, + int *fd_to_send); LTTNG_HIDDEN bool lttng_condition_is_equal(const struct lttng_condition *a, const struct lttng_condition *b); +LTTNG_HIDDEN +const char *lttng_condition_type_str(enum lttng_condition_type type); + #endif /* LTTNG_CONDITION_INTERNAL_H */ diff --git a/include/lttng/condition/condition.h b/include/lttng/condition/condition.h index 877ccd1a3..78a206df3 100644 --- a/include/lttng/condition/condition.h +++ b/include/lttng/condition/condition.h @@ -21,6 +21,7 @@ enum lttng_condition_type { LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW = 102, LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING = 103, LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED = 104, + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT = 105, }; enum lttng_condition_status { diff --git a/include/lttng/condition/event-rule-internal.h b/include/lttng/condition/event-rule-internal.h new file mode 100644 index 000000000..ce1004e61 --- /dev/null +++ b/include/lttng/condition/event-rule-internal.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_CONDITION_event_rule_INTERNAL_H +#define LTTNG_CONDITION_event_rule_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_condition_event_rule { + struct lttng_condition parent; + struct lttng_event_rule *rule; +}; + +struct lttng_condition_event_rule_comm { + /* length excludes its own length */ + uint32_t length; + /* rule */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_evaluation_event_rule { + struct lttng_evaluation parent; + char *name; +}; + +struct lttng_evaluation_event_rule_comm { + uint32_t trigger_name_length; + /* Trigger name */ + char payload[]; +} LTTNG_PACKED; + + +LTTNG_HIDDEN +ssize_t lttng_condition_event_rule_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_condition **condition); + +LTTNG_HIDDEN +enum lttng_condition_status +lttng_condition_event_rule_get_rule_no_const( + const struct lttng_condition *condition, + struct lttng_event_rule **rule); + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_event_rule_create(const char* trigger_name); + +LTTNG_HIDDEN +ssize_t lttng_evaluation_event_rule_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_evaluation **_evaluation); + +#endif /* LTTNG_CONDITION_event_rule_INTERNAL_H */ diff --git a/include/lttng/condition/event-rule.h b/include/lttng/condition/event-rule.h new file mode 100644 index 000000000..2a536d34d --- /dev/null +++ b/include/lttng/condition/event-rule.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_CONDITION_EVENT_RULE_H +#define LTTNG_CONDITION_EVENT_RULE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * TODO: overall desciption of an event rule condition. + */ + +/* + * Create a newly allocated event rule condition. + * + * Rule will be copied internally. + * + * Returns a new condition on success, NULL on failure. This condition must be + * destroyed using lttng_condition_destroy(). + */ +extern struct lttng_condition *lttng_condition_event_rule_create( + struct lttng_event_rule *rule); + +/* + * Get the rule property of a event rule condition. + * + * The caller does not assume the ownership of the returned rule. The + * rule shall only be used for the duration of the condition's + * lifetime. + * + * Returns LTTNG_CONDITION_STATUS_OK and a pointer to the condition's rule + * on success, LTTNG_CONDITION_STATUS_INVALID if an invalid + * parameter is passed. */ +extern enum lttng_condition_status lttng_condition_event_rule_get_rule( + const struct lttng_condition *condition, const struct lttng_event_rule **rule); + +/** + * lttng_evaluation_event_rule_hit are specialised lttng_evaluations which + * allow users to query a number of properties resulting from the evaluation + * of a condition which evaluated to true. + * + * The evaluation of a event rule hit yields two different results: + * TEMPORARY - The name of the triggers associated with the condition. + * TODO - The captured event payload if any + */ + +/* + * Get the trigger name property of a event rule hit evaluation. + * + * Returns LTTNG_EVALUATION_STATUS_OK on success and a trigger name + * or LTTNG_EVALUATION_STATUS_INVALID if + * an invalid parameter is passed. + */ +extern enum lttng_evaluation_status +lttng_evaluation_event_rule_get_trigger_name( + const struct lttng_evaluation *evaluation, + const char **name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_CONDITION_EVENT_RULE_H */ diff --git a/include/lttng/domain-internal.h b/include/lttng/domain-internal.h new file mode 100644 index 000000000..f34a33bcf --- /dev/null +++ b/include/lttng/domain-internal.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 - EfficiOS, inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_DOMAIN_INTERNAL_H +#define LTTNG_DOMAIN_INTERNAL_H + +#include "lttng/domain.h" +#include "common/macros.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LTTNG_HIDDEN +const char *lttng_domain_type_str(enum lttng_domain_type domain_type); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_DOMAIN_INTERNAL_H */ diff --git a/include/lttng/event-internal.h b/include/lttng/event-internal.h index b3df4c9c2..01e9b8088 100644 --- a/include/lttng/event-internal.h +++ b/include/lttng/event-internal.h @@ -36,4 +36,19 @@ struct lttng_event_extended { LTTNG_HIDDEN struct lttng_event *lttng_event_copy(const struct lttng_event *event); +// FIXME: the implementation of these should be moved to some common file, +// they should not be in the enable_events.c file. + +LTTNG_HIDDEN +int loglevel_str_to_value(const char *inputstr); + +LTTNG_HIDDEN +int loglevel_log4j_str_to_value(const char *inputstr); + +LTTNG_HIDDEN +int loglevel_jul_str_to_value(const char *inputstr); + +LTTNG_HIDDEN +int loglevel_python_str_to_value(const char *inputstr); + #endif /* LTTNG_EVENT_INTERNAL_H */ diff --git a/include/lttng/event-rule/event-rule-internal.h b/include/lttng/event-rule/event-rule-internal.h new file mode 100644 index 000000000..265286ab1 --- /dev/null +++ b/include/lttng/event-rule/event-rule-internal.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_INTERNAL_H +#define LTTNG_EVENT_RULE_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*event_rule_destroy_cb)(struct lttng_event_rule *event_rule); +typedef bool (*event_rule_validate_cb)( + const struct lttng_event_rule *event_rule); +typedef int (*event_rule_serialize_cb)( + const struct lttng_event_rule *event_rule, + struct lttng_dynamic_buffer *buf, + int *fd_to_send); +typedef bool (*event_rule_equal_cb)(const struct lttng_event_rule *a, + const struct lttng_event_rule *b); +typedef ssize_t (*event_rule_create_from_buffer_cb)( + const struct lttng_buffer_view *view, + struct lttng_event_rule **event_rule); +typedef enum lttng_error_code (*event_rule_populate_cb)( + struct lttng_event_rule *event_rule, uid_t uid, gid_t gid); +typedef const char *(*event_rule_get_filter_cb)( + const struct lttng_event_rule *event_rule); +typedef const struct lttng_filter_bytecode *( + *event_rule_get_filter_bytecode_cb)( + const struct lttng_event_rule *event_rule); +typedef struct lttng_event_exclusion *(*event_rule_generate_exclusions_cb)( + struct lttng_event_rule *event_rule); +typedef struct lttng_event *(*event_rule_generate_lttng_event_cb)( + const struct lttng_event_rule *event_rule); + +struct lttng_event_rule { + struct urcu_ref ref; + enum lttng_event_rule_type type; + event_rule_validate_cb validate; + event_rule_serialize_cb serialize; + event_rule_equal_cb equal; + event_rule_destroy_cb destroy; + event_rule_populate_cb populate; + /* Optional */ + event_rule_get_filter_cb get_filter; + event_rule_get_filter_bytecode_cb get_filter_bytecode; + event_rule_generate_exclusions_cb generate_exclusions; + event_rule_generate_lttng_event_cb generate_lttng_event; +}; + +struct lttng_event_rule_comm { + /* enum lttng_event_rule_type */ + int8_t event_rule_type; + char payload[]; +}; + +LTTNG_HIDDEN +void lttng_event_rule_init(struct lttng_event_rule *event_rule, + enum lttng_event_rule_type type); + +LTTNG_HIDDEN +bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule); + +LTTNG_HIDDEN +ssize_t lttng_event_rule_create_from_buffer( + const struct lttng_buffer_view *buffer, + struct lttng_event_rule **event_rule); + +LTTNG_HIDDEN +int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule, + struct lttng_dynamic_buffer *buf, + int *fd_to_send); + +LTTNG_HIDDEN +bool lttng_event_rule_is_equal(const struct lttng_event_rule *a, + const struct lttng_event_rule *b); + +LTTNG_HIDDEN +bool lttng_event_rule_get(struct lttng_event_rule *rule); + +LTTNG_HIDDEN +void lttng_event_rule_put(struct lttng_event_rule *rule); + +LTTNG_HIDDEN +enum lttng_domain_type lttng_event_rule_get_domain_type( + const struct lttng_event_rule *rule); + +LTTNG_HIDDEN +enum lttng_error_code lttng_event_rule_populate( + struct lttng_event_rule *rule, uid_t uid, gid_t gid); + +/* If not present return NULL + * Caller DO NOT own the returned object + */ +LTTNG_HIDDEN +const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule); + +/* If not present return NULL + * Caller DO NOT own the returned object + */ +LTTNG_HIDDEN +const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode( + const struct lttng_event_rule *rule); + +/* + * If not present return NULL + * Caller OWN the returned object + * TODO: should this be done another way? + */ +LTTNG_HIDDEN +struct lttng_event_exclusion *lttng_event_rule_generate_exclusions( + struct lttng_event_rule *rule); + +/* + * This is compatibility helper, allowing us to generate a sessiond side (not + * communication) struct lttng_event object. This facilitate integration with + * current code. + * Caller OWN the returned object + */ +LTTNG_HIDDEN +struct lttng_event *lttng_event_rule_generate_lttng_event( + const struct lttng_event_rule *rule); + +/* Quick helper to test if the event rule apply to an agent domain */ +LTTNG_HIDDEN +bool lttng_event_rule_is_agent(const struct lttng_event_rule *rule); + +LTTNG_HIDDEN +const char *lttng_event_rule_type_str(enum lttng_event_rule_type type); + +#endif /* LTTNG_EVENT_RULE_INTERNAL_H */ diff --git a/include/lttng/event-rule/event-rule.h b/include/lttng/event-rule/event-rule.h new file mode 100644 index 000000000..71678b70b --- /dev/null +++ b/include/lttng/event-rule/event-rule.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_H +#define LTTNG_EVENT_RULE_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_event_rule; + +enum lttng_event_rule_type { + LTTNG_EVENT_RULE_TYPE_UNKNOWN = -1, + LTTNG_EVENT_RULE_TYPE_TRACEPOINT = 100, + LTTNG_EVENT_RULE_TYPE_SYSCALL = 101, + LTTNG_EVENT_RULE_TYPE_KPROBE = 102, + LTTNG_EVENT_RULE_TYPE_KRETPROBE = 103, + LTTNG_EVENT_RULE_TYPE_UPROBE = 104, +}; + + +enum lttng_event_rule_status { + LTTNG_EVENT_RULE_STATUS_OK = 0, + LTTNG_EVENT_RULE_STATUS_ERROR = -1, + LTTNG_EVENT_RULE_STATUS_UNKNOWN = -2, + LTTNG_EVENT_RULE_STATUS_INVALID = -3, + LTTNG_EVENT_RULE_STATUS_UNSET = -4, + LTTNG_EVENT_RULE_STATUS_UNSUPPORTED = -5, +}; + +/** + * Event rule describe a set of criteria to be used as a discriminant in regards + * to a set of events. + * + * Event rule have the following base properties: + * - the instrumentation type for the event rule, + * - tracepoint + * - syscall + * - probe + * - userspace-probe + * - the domain the event rule covers. + */ + +/* + * Get the event rule type. + * + * Returns the type of an event rule on success, LTTNG_EVENT_RULE_UNKNOWN on + * error. + */ +extern enum lttng_event_rule_type lttng_event_rule_get_type( + const struct lttng_event_rule *event_rule); + +/* + * Destroy (release) a event_rule object. + */ +extern void lttng_event_rule_destroy(struct lttng_event_rule *rule); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_H */ diff --git a/include/lttng/event-rule/kprobe-internal.h b/include/lttng/event-rule/kprobe-internal.h new file mode 100644 index 000000000..f3df8e495 --- /dev/null +++ b/include/lttng/event-rule/kprobe-internal.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_KPROBE_INTERNAL_H +#define LTTNG_EVENT_RULE_KPROBE_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_rule_kprobe { + struct lttng_event_rule parent; + char *name; + struct { + uint64_t address; + uint64_t offset; + char *symbol_name; + bool set; + } probe; +}; + +struct lttng_event_rule_kprobe_comm { + uint32_t name_len; + uint32_t probe_symbol_name_len; + uint64_t probe_address; + uint64_t probe_offset; + /* name, source symbol_name */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kprobe_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **rule); + +LTTNG_HIDDEN +uint64_t lttng_event_rule_kprobe_get_address( + const struct lttng_event_rule *rule); + +LTTNG_HIDDEN +uint64_t lttng_event_rule_kprobe_get_offset( + const struct lttng_event_rule *rule); + +LTTNG_HIDDEN +const char *lttng_event_rule_kprobe_get_symbol_name( + const struct lttng_event_rule *rule); + +#endif /* LTTNG_EVENT_RULE_KPROBE_INTERNAL_H */ diff --git a/include/lttng/event-rule/kprobe.h b/include/lttng/event-rule/kprobe.h new file mode 100644 index 000000000..16f748821 --- /dev/null +++ b/include/lttng/event-rule/kprobe.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_KPROBE_H +#define LTTNG_EVENT_RULE_KPROBE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * TODO: + */ +extern struct lttng_event_rule *lttng_event_rule_kprobe_create(void); + +/* + * Set the source of a kprobe event rule. + * + * TODO: list possible format + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_source( + struct lttng_event_rule *rule, const char *source); + +/* + * Set the name of a kprobe event rule. + * + * The name is copied. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( + struct lttng_event_rule *rule, const char *name); + +/* + * Get the name of a kprobe event rule. + * + * The caller does not assume the ownership of the returned name. + * The name shall only only be used for the duration of the event + * rule's lifetime, or before a different name is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on + * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed, + * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_kprobe_get_name( + const struct lttng_event_rule *rule, const char **name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_KPROBE_H */ diff --git a/include/lttng/event-rule/kretprobe-internal.h b/include/lttng/event-rule/kretprobe-internal.h new file mode 100644 index 000000000..bf90906ca --- /dev/null +++ b/include/lttng/event-rule/kretprobe-internal.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_KRETPROBE_INTERNAL_H +#define LTTNG_EVENT_RULE_KRETPROBE_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_rule_kretprobe { + struct lttng_event_rule parent; + char *name; + struct { + uint64_t address; + uint64_t offset; + char *symbol_name; + } probe; +}; + +struct lttng_event_rule_kretprobe_comm { + uint32_t name_len; + uint32_t probe_symbol_len; + /*name, probe symbol_name */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kretprobe_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **rule); + +LTTNG_HIDDEN +uint64_t lttng_event_rule_kretprobe_get_address( + const struct lttng_event_rule *rule); + +LTTNG_HIDDEN +uint64_t lttng_event_rule_kretprobe_get_offset( + const struct lttng_event_rule *rule); + +LTTNG_HIDDEN +const char *lttng_event_rule_kretprobe_get_symbol_name( + const struct lttng_event_rule *rule); + +#endif /* LTTNG_EVENT_RULE_KRETPROBE_INTERNAL_H */ diff --git a/include/lttng/event-rule/kretprobe.h b/include/lttng/event-rule/kretprobe.h new file mode 100644 index 000000000..5ed08529a --- /dev/null +++ b/include/lttng/event-rule/kretprobe.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_KRETPROBE_H +#define LTTNG_EVENT_RULE_KRETPROBE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * TODO: + */ +extern struct lttng_event_rule *lttng_event_rule_kretprobe_create(void); + +/* + * Set the source of a kretprobe event rule. + * + * TODO: list possible format + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_kretprobe_set_source( + struct lttng_event_rule *rule, const char *source); + +/* + * Set the name of a kretprobe event rule. + * + * The name is copied. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_kretprobe_set_name( + struct lttng_event_rule *rule, const char *name); + +/* + * Get the name of a kretprobe event rule. + * + * The caller does not assume the ownership of the returned name. + * The name shall only only be used for the duration of the event + * rule's lifetime, or before a different name is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on + * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed, + * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_kretprobe_get_name( + const struct lttng_event_rule *rule, const char **name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_KRETPROBE_H */ diff --git a/include/lttng/event-rule/syscall-internal.h b/include/lttng/event-rule/syscall-internal.h new file mode 100644 index 000000000..47e8e0f70 --- /dev/null +++ b/include/lttng/event-rule/syscall-internal.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H +#define LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_rule_syscall { + struct lttng_event_rule parent; + char *pattern; + char *filter_expression; + + /* internal use only */ + struct { + char *filter; + struct lttng_filter_bytecode *bytecode; + } internal_filter; +}; + +struct lttng_event_rule_syscall_comm { + uint32_t pattern_len; + uint32_t filter_expression_len; + /* pattern, filter expression */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_syscall_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **rule); + +#endif /* LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H */ diff --git a/include/lttng/event-rule/syscall.h b/include/lttng/event-rule/syscall.h new file mode 100644 index 000000000..7d3bd1764 --- /dev/null +++ b/include/lttng/event-rule/syscall.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_SYSCALL_H +#define LTTNG_EVENT_RULE_SYSCALL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * TODO: + */ +extern struct lttng_event_rule *lttng_event_rule_syscall_create(void); + +/* + * Set the pattern of a syscall event rule. + * + * Pattern can contains wildcard '*'. See man lttng-enable-event. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_syscall_set_pattern( + struct lttng_event_rule *rule, const char *pattern); + +/* + * Get the pattern of a tracecpoint event rule. + * + * The caller does not assume the ownership of the returned pattern. The + * pattern shall only only be used for the duration of the event rule's + * lifetime, or before a different pattern is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's pattern + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid + * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern + * was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_syscall_get_pattern( + const struct lttng_event_rule *rule, const char **pattern); + +/* + * Set the filter expression of a syscall event rule. + * + * The expression is copied. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_syscall_set_filter( + struct lttng_event_rule *rule, const char *expression); + +/* + * Get the filter expression of a syscall event rule. + * + * The caller does not assume the ownership of the returned filter expression. + * The filter expression shall only only be used for the duration of the event + * rule's lifetime, or before a different filter expression is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's filter + * expression on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid + * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a filter expression + * was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_syscall_get_filter( + const struct lttng_event_rule *rule, const char **expression); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_H */ diff --git a/include/lttng/event-rule/tracepoint-internal.h b/include/lttng/event-rule/tracepoint-internal.h new file mode 100644 index 000000000..e2432dde5 --- /dev/null +++ b/include/lttng/event-rule/tracepoint-internal.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H +#define LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H + +#include +#include +#include +#include +#include +#include + +struct lttng_event_rule_tracepoint { + struct lttng_event_rule parent; + + /* Domain */ + enum lttng_domain_type domain; + + /* Name pattern */ + char *pattern; + + /* Filter */ + char *filter_expression; + + /* Loglevel */ + struct { + enum lttng_loglevel_type type; + int value; + } loglevel; + + /* Exclusions */ + + struct { + char **values; + unsigned int count; + } exclusions; + + /* internal use only */ + struct { + char *filter; + struct lttng_filter_bytecode *bytecode; + } internal_filter; +}; + +struct lttng_event_rule_tracepoint_comm { + /* enum lttng_domain_type */ + int8_t domain_type; + /* enum lttng_event_logleven_type */ + int8_t loglevel_type; + int32_t loglevel_value; + uint32_t pattern_len; + uint32_t filter_expression_len; + uint32_t exclusions_count; + uint32_t exclusions_len; + /* + * pattern, filter expression and exclusions each terminating with '\0' + */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_tracepoint_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **rule); + +#endif /* LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H */ diff --git a/include/lttng/event-rule/tracepoint.h b/include/lttng/event-rule/tracepoint.h new file mode 100644 index 000000000..8868828a3 --- /dev/null +++ b/include/lttng/event-rule/tracepoint.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_TRACEPOINT_H +#define LTTNG_EVENT_RULE_TRACEPOINT_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * TODO: + */ +extern struct lttng_event_rule *lttng_event_rule_tracepoint_create( + enum lttng_domain_type domain); + +/* + * Set the pattern of a tracepoint event rule. + * + * Pattern can contains wildcard '*'. See man lttng-enable-event. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_pattern( + struct lttng_event_rule *rule, const char *pattern); + +/* + * Get the pattern of a tracepoint event rule. + * + * The caller does not assume the ownership of the returned pattern. The + * pattern shall only only be used for the duration of the event rule's + * lifetime, or before a different pattern is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's pattern + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid + * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern + * was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_pattern( + const struct lttng_event_rule *rule, const char **pattern); + +/* + * Set the domain type of a tracepoint event rule. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter( + struct lttng_event_rule *rule, const char *expression); + +/* + * Get the domain type of a tracecpoint event rule. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the domain type output parameter + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is + * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern was not set prior to + * this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_domain_type( + const struct lttng_event_rule *rule, + enum lttng_domain_type *type); + +/* + * Set the filter expression of a tracepoint event rule. + * + * The expression is copied. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter( + struct lttng_event_rule *rule, const char *expression); + +/* + * Get the filter expression of a tracepoint event rule. + * + * The caller does not assume the ownership of the returned filter expression. + * The filter expression shall only only be used for the duration of the event + * rule's lifetime, or before a different filter expression is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's filter + * expression on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid + * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a filter expression + * was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter( + const struct lttng_event_rule *rule, const char **expression); + +/* + * Set the single loglevel of a tracepoint event rule. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel( + struct lttng_event_rule *rule, int level); + +/* + * Set the loglevel range of a tracepoint event rule. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status +lttng_event_rule_tracepoint_set_loglevel_range( + struct lttng_event_rule *rule, int level); + +/* + * Set the loglevel to all of a tracepoint event rule. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status +lttng_event_rule_tracepoint_set_loglevel_all(struct lttng_event_rule *rule); + +/* + * Get the loglevel type of a tracepoint event rule. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the loglevel type output + * parameter on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter + * is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a loglevel was not set prior + * to this call. + */ +extern enum lttng_event_rule_status +lttng_event_rule_tracepoint_get_loglevel_type( + const struct lttng_event_rule *rule, enum lttng_loglevel_type *type); + +/* + * Get the loglevel of a tracepoint event rule. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the loglevel output parameter + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is + * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a loglevel was not set prior to + * this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_loglevel( + const struct lttng_event_rule *rule, int *level); + +/* + * Set the exclusions property of a event rule. + * + * The passed exclusions will be copied to the event_rule. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK on success, + * LTTNG_EVENT_RULE_STATUS_INVALID if invalid parameters are passed, or + * LTTNG_EVENT_RULE_STATUS_UNSUPPORTED if this property is not supported by the + * domain. + */ +extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_exclusions( + struct lttng_event_rule *rule, + unsigned int count, + const char **exclusions); + +/* + * Get the exclusions property count of a event rule. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the count output parameter + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is + * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a domain type was not set prior + * to this call. + */ +extern enum lttng_event_rule_status +lttng_event_rule_tracepoint_get_exclusions_count( + const struct lttng_event_rule *rule, unsigned int *count); + +/* + * TODO: + * */ +extern enum lttng_event_rule_status +lttng_event_rule_tracepoint_get_exclusion_at_index( + const struct lttng_event_rule *rule, + unsigned int index, + const char **exclusion); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_TRACEPOINT_H */ diff --git a/include/lttng/event-rule/uprobe-internal.h b/include/lttng/event-rule/uprobe-internal.h new file mode 100644 index 000000000..c26c8c725 --- /dev/null +++ b/include/lttng/event-rule/uprobe-internal.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_UPROBE_INTERNAL_H +#define LTTNG_EVENT_RULE_UPROBE_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_rule_uprobe { + struct lttng_event_rule parent; + char *name; + struct lttng_userspace_probe_location *location; +}; + +struct lttng_event_rule_uprobe_comm { + uint32_t name_len; + uint32_t location_len; + /*name, location object */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_uprobe_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **rule); + +LTTNG_HIDDEN +struct lttng_userspace_probe_location * +lttng_event_rule_uprobe_get_location_no_const( + const struct lttng_event_rule *rule); + +#endif /* LTTNG_EVENT_RULE_UPROBE_INTERNAL_H */ diff --git a/include/lttng/event-rule/uprobe.h b/include/lttng/event-rule/uprobe.h new file mode 100644 index 000000000..dbe07bb8b --- /dev/null +++ b/include/lttng/event-rule/uprobe.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVENT_RULE_UPROBE_H +#define LTTNG_EVENT_RULE_UPROBE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * TODO: + */ +extern struct lttng_event_rule *lttng_event_rule_uprobe_create(void); + +/* + * Set the location of a uprobe event rule. + * + * The location is copied internally. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_location( + struct lttng_event_rule *rule, const struct lttng_userspace_probe_location *location); + +/* + * Get the location of a uprobe event rule. + * + * The caller does not assume the ownership of the returned location. + * The location shall only be used for the duration of the event + * rule's lifetime, or before a different location is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's location + * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed, + * or LTTNG_EVENT_RULE_STATUS_UNSET if a location was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( + const struct lttng_event_rule *rule, + const struct lttng_userspace_probe_location **location); + +/* + * Set the name of a uprobe event rule. + * + * The name is copied. + * + * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( + struct lttng_event_rule *rule, const char *name); + +/* + * Get the name of a uprobe event rule. + * + * The caller does not assume the ownership of the returned name. + * The name shall only only be used for the duration of the event + * rule's lifetime, or before a different name is set. + * + * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on + * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed, + * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call. + */ +extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_name( + const struct lttng_event_rule *rule, const char **name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_UPROBE_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index 4557cf0d7..a3d518944 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -28,12 +28,24 @@ #include #include #include +#include #include +#include +#include +#include +#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include diff --git a/include/lttng/snapshot-internal.h b/include/lttng/snapshot-internal.h index 72d492237..8012a1e4e 100644 --- a/include/lttng/snapshot-internal.h +++ b/include/lttng/snapshot-internal.h @@ -50,7 +50,7 @@ struct lttng_snapshot_output_list { size_t count; /* - * Containes snapshot output object. + * Contains snapshot output object. */ struct lttng_snapshot_output *array; }; diff --git a/include/lttng/snapshot.h b/include/lttng/snapshot.h index 9bdf775a9..33e9a53f9 100644 --- a/include/lttng/snapshot.h +++ b/include/lttng/snapshot.h @@ -39,15 +39,15 @@ void lttng_snapshot_output_destroy(struct lttng_snapshot_output *output); */ /* Return snapshot ID. */ -uint32_t lttng_snapshot_output_get_id(struct lttng_snapshot_output *output); +uint32_t lttng_snapshot_output_get_id(const struct lttng_snapshot_output *output); /* Return maximum size of a snapshot. */ -uint64_t lttng_snapshot_output_get_maxsize(struct lttng_snapshot_output *output); +uint64_t lttng_snapshot_output_get_maxsize(const struct lttng_snapshot_output *output); /* Return snapshot name. */ -const char *lttng_snapshot_output_get_name(struct lttng_snapshot_output *output); +const char *lttng_snapshot_output_get_name(const struct lttng_snapshot_output *output); /* Return snapshot control URL in a text format. */ -const char *lttng_snapshot_output_get_ctrl_url(struct lttng_snapshot_output *output); +const char *lttng_snapshot_output_get_ctrl_url(const struct lttng_snapshot_output *output); /* Return snapshot data URL in a text format. */ -const char *lttng_snapshot_output_get_data_url(struct lttng_snapshot_output *output); +const char *lttng_snapshot_output_get_data_url(const struct lttng_snapshot_output *output); /* * Snapshot output setter family functions. @@ -65,6 +65,36 @@ int lttng_snapshot_output_set_size(uint64_t size, /* Set the snapshot name. */ int lttng_snapshot_output_set_name(const char *name, struct lttng_snapshot_output *output); + +/* + * Set the output destination to be a path on the local filesystem. + * + * The path must be absolute. It can optionally begin with `file://`. + */ +int lttng_snapshot_output_set_local_path(const char *path, + struct lttng_snapshot_output *output); + +/* + * Set the output destination to be the network from a combined control/data + * URL. + * + * `url` must start with `net://` or `net6://`. + */ +int lttng_snapshot_output_set_network_url(const char *url, + struct lttng_snapshot_output *output); + +/* + * Set the output destination to be the network using separate URLs for control + * and data. + * + * `ctrl_url` and `data_url` must start with `tcp://` or `tcp6://`. + */ +int lttng_snapshot_output_set_network_urls( + const char *ctrl_url, const char *data_url, + struct lttng_snapshot_output *output); + +// Deprecated? + /* Set the control URL. Local and remote URL are supported. */ int lttng_snapshot_output_set_ctrl_url(const char *url, struct lttng_snapshot_output *output); diff --git a/include/lttng/trigger/trigger-internal.h b/include/lttng/trigger/trigger-internal.h index cff4504a7..64937661f 100644 --- a/include/lttng/trigger/trigger-internal.h +++ b/include/lttng/trigger/trigger-internal.h @@ -12,39 +12,146 @@ #include #include #include +#include +#include #include #include #include +#include struct lttng_trigger { + struct urcu_ref ref; /* internal use only */ + bool owns_internal_objects; /* internal use only */ + struct lttng_condition *condition; struct lttng_action *action; + char *name; + struct { /* Internal use only */ + bool set; + uint64_t value; + } key; + struct { /* internal use only */ + struct lttng_credentials credentials; + bool set; + } creds; + struct { + enum lttng_trigger_firing_policy_type type; + uint64_t threshold; + uint64_t current_count; + } firing_policy; +}; + +struct lttng_triggers { + struct lttng_dynamic_pointer_array array; }; struct lttng_trigger_comm { /* length excludes its own length. */ + uint32_t name_length /* Includes '\0' */; uint32_t length; - /* A condition and action object follow. */ + uint8_t policy_type; + uint64_t policy_threshold; + /* A name, condition and action object follow. */ char payload[]; } LTTNG_PACKED; +struct lttng_triggers_comm { + uint32_t count; + uint32_t length; + /* Count * lttng_trigger_comm structure */ + char payload[]; +}; + LTTNG_HIDDEN ssize_t lttng_trigger_create_from_buffer(const struct lttng_buffer_view *view, struct lttng_trigger **trigger); LTTNG_HIDDEN -int lttng_trigger_serialize(struct lttng_trigger *trigger, - struct lttng_dynamic_buffer *buf); +int lttng_trigger_serialize(const struct lttng_trigger *trigger, + struct lttng_dynamic_buffer *buf, + int *fd_to_send); LTTNG_HIDDEN -const struct lttng_condition *lttng_trigger_get_const_condition( - const struct lttng_trigger *trigger); +bool lttng_trigger_validate(const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +int lttng_trigger_assign(struct lttng_trigger *dst, + const struct lttng_trigger *src); + +LTTNG_HIDDEN +bool lttng_trigger_is_equal(const struct lttng_trigger *a, + const struct lttng_trigger *b); + +LTTNG_HIDDEN +void lttng_trigger_set_key(struct lttng_trigger *trigger, uint64_t key); + +LTTNG_HIDDEN +uint64_t lttng_trigger_get_key(const struct lttng_trigger *trigger); + +LTTNG_HIDDEN +int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset); + +/* + * Allocate a new list of lttng_trigger. + * The returned object must be freed via lttng_triggers_destroy. + */ +LTTNG_HIDDEN +struct lttng_triggers *lttng_triggers_create(void); + +/* + * Return the non-const pointer of an element at index "index" of a + * lttng_triggers. + * + * The ownership of the lttng_triggers element is NOT transfered. + * The returned object can NOT be freed via lttng_trigger_destroy. + */ +LTTNG_HIDDEN +struct lttng_trigger *lttng_triggers_get_pointer_of_index( + const struct lttng_triggers *triggers, unsigned int index); + +/* + * TODO: + */ +LTTNG_HIDDEN +int lttng_triggers_add( + struct lttng_triggers *triggers, struct lttng_trigger *trigger); + +/* + * Serialize a trigger collection to a lttng_dynamic_buffer. + * Return LTTNG_OK on success, negative lttng error code on error. + */ +LTTNG_HIDDEN +int lttng_triggers_serialize(const struct lttng_triggers *triggers, + struct lttng_dynamic_buffer *buffer); + +LTTNG_HIDDEN +ssize_t lttng_triggers_create_from_buffer(const struct lttng_buffer_view *view, + struct lttng_triggers **triggers); LTTNG_HIDDEN -const struct lttng_action *lttng_trigger_get_const_action( +const struct lttng_credentials *lttng_trigger_get_credentials( const struct lttng_trigger *trigger); LTTNG_HIDDEN -bool lttng_trigger_validate(struct lttng_trigger *trigger); +void lttng_trigger_set_credentials( + struct lttng_trigger *trigger, uid_t uid, gid_t git); + +LTTNG_HIDDEN +void lttng_trigger_get(struct lttng_trigger *trigger); + +LTTNG_HIDDEN +void lttng_trigger_put(struct lttng_trigger *trigger); + +LTTNG_HIDDEN +bool lttng_trigger_is_ready_to_fire( + struct lttng_trigger *trigger); + +/* + * Return the type of any uderlying domain requirement. If no particular + * requirement is needed return LTTNG_DOMAIN_NONE. + */ +LTTNG_HIDDEN +enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction( + const struct lttng_trigger *trigger); #endif /* LTTNG_TRIGGER_INTERNAL_H */ diff --git a/include/lttng/trigger/trigger.h b/include/lttng/trigger/trigger.h index feffc6a8f..823a02651 100644 --- a/include/lttng/trigger/trigger.h +++ b/include/lttng/trigger/trigger.h @@ -11,6 +11,8 @@ struct lttng_action; struct lttng_condition; struct lttng_trigger; +/* A collection of trigger */ +struct lttng_triggers; #ifdef __cplusplus extern "C" { @@ -21,6 +23,20 @@ enum lttng_register_trigger_status { LTTNG_REGISTER_TRIGGER_STATUS_INVALID = -1, }; +enum lttng_trigger_status { + LTTNG_TRIGGER_STATUS_OK = 0, + LTTNG_TRIGGER_STATUS_ERROR = -1, + LTTNG_TRIGGER_STATUS_UNKNOWN = -2, + LTTNG_TRIGGER_STATUS_INVALID = -3, + LTTNG_TRIGGER_STATUS_UNSET = -4, + LTTNG_TRIGGER_STATUS_UNSUPPORTED = -5, +}; + +enum lttng_trigger_firing_policy_type { + LTTNG_TRIGGER_FIRE_EVERY_N = 0, + LTTNG_TRIGGER_FIRE_ONCE_AFTER_N = 1, +}; + /* * Create a trigger object associating a condition and an action. * @@ -52,6 +68,9 @@ extern struct lttng_trigger *lttng_trigger_create( extern struct lttng_condition *lttng_trigger_get_condition( struct lttng_trigger *trigger); +const struct lttng_condition *lttng_trigger_get_const_condition( + const struct lttng_trigger *trigger); + /* * Get the action of a trigger. * @@ -62,6 +81,58 @@ extern struct lttng_condition *lttng_trigger_get_condition( extern struct lttng_action *lttng_trigger_get_action( struct lttng_trigger *trigger); +const struct lttng_action *lttng_trigger_get_const_action( + const struct lttng_trigger *trigger); + +/* + * Get the name of a trigger. + * + * The caller does not assume the ownership of the returned name. + * The name shall only only be used for the duration of the trigger's + * lifetime, or before a different name is set. + * + * Returns LTTNG_TRIGGER_STATUS_OK and a pointer to the trigger's name on + * success, LTTNG_TRIGGER_STATUS_INVALID if an invalid parameter is passed, + * or LTTNG_TRIGGER_STATUS_UNSET if a name was not set prior to this call. + */ +extern enum lttng_trigger_status lttng_trigger_get_name( + const struct lttng_trigger *trigger, const char **name); + +/* + * Set the trigger name. + * + * A name is optional. + * A name will be assigned on trigger registration if no name is set. + * + * The name is copied. + * + * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_set_name( + struct lttng_trigger *trigger, const char *name); + +/* + * Set the trigger firing policy. + * + * This is optional. By default a trigger is set to fire each time the + * associated condition occurs. + * + * Threshold is the number of time the condition must be hit before the policy is + * enacted. + * + * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_set_firing_policy( + struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy_type policy_type, + unsigned long long threshold); +extern enum lttng_trigger_status lttng_trigger_get_firing_policy( + const struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy_type *policy_type, + unsigned long long *threshold); + /* * Destroy (frees) a trigger object. */ @@ -83,7 +154,44 @@ extern int lttng_register_trigger(struct lttng_trigger *trigger); * * Return 0 on success, a negative LTTng error code on error. */ -extern int lttng_unregister_trigger(struct lttng_trigger *trigger); +extern int lttng_unregister_trigger(const struct lttng_trigger *trigger); + +/* + * List current triggers. + * + * On success, triggers is allocated. + * The trigger collection must be free by the caller with lttng_destroy_triggers + * + * Returns 0 on success, else a negative LTTng error code. + */ +extern int lttng_list_triggers(struct lttng_triggers **triggers); + +/* + * Get a trigger from the collection at a given index. + * + * Note that the collection maintains the ownership of the returned trigger. + * It must not be destroyed by the user, nor should it be held beyond the + * lifetime of the trigger collection. + * + * Returns a trigger, or NULL on error. + */ +extern const struct lttng_trigger *lttng_triggers_get_at_index( + const struct lttng_triggers *triggers, unsigned int index); + +/* + * Get the number of trigger in a tracker id list. + * + * Return LTTNG_TRIGGER_STATUS_OK on success, + * LTTNG_TRIGGER_STATUS_INVALID when passed invalid parameters. + */ +extern enum lttng_trigger_status lttng_triggers_get_count( + const struct lttng_triggers *triggers, unsigned int *count); + +/* + * Destroy a trigger collection. + */ +extern void lttng_triggers_destroy(struct lttng_triggers *ids); + #ifdef __cplusplus } diff --git a/include/lttng/userspace-probe-internal.h b/include/lttng/userspace-probe-internal.h index 828795493..ef6a5726e 100644 --- a/include/lttng/userspace-probe-internal.h +++ b/include/lttng/userspace-probe-internal.h @@ -14,6 +14,10 @@ #include #include +typedef bool (*userspace_probe_location_equal_cb)( + const struct lttng_userspace_probe_location *a, + const struct lttng_userspace_probe_location *b); + /* * No elf-specific comm structure is defined since no elf-specific payload is * currently needed. @@ -79,6 +83,7 @@ struct lttng_userspace_probe_location_tracepoint_comm { struct lttng_userspace_probe_location { enum lttng_userspace_probe_location_type type; struct lttng_userspace_probe_location_lookup_method *lookup_method; + userspace_probe_location_equal_cb equal; }; struct lttng_userspace_probe_location_function { @@ -142,4 +147,13 @@ LTTNG_HIDDEN struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy( const struct lttng_userspace_probe_location *location); +LTTNG_HIDDEN +bool lttng_userspace_probe_location_is_equal( + const struct lttng_userspace_probe_location *a, + const struct lttng_userspace_probe_location *b); + +LTTNG_HIDDEN +int lttng_userspace_probe_location_set_binary_fd( + struct lttng_userspace_probe_location *location, int fd); + #endif /* LTTNG_USERSPACE_PROBE_INTERNAL_H */ diff --git a/m4/ax_append_compile_flags.m4 b/m4/ax_append_compile_flags.m4 new file mode 100644 index 000000000..9c856356c --- /dev/null +++ b/m4/ax_append_compile_flags.m4 @@ -0,0 +1,46 @@ +# ============================================================================ +# https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# For every FLAG1, FLAG2 it is checked whether the compiler works with the +# flag. If it does, the flag is added FLAGS-VARIABLE +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. During the check the flag is always added to the +# current language's flags. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: This macro depends on the AX_APPEND_FLAG and +# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with +# AX_APPEND_LINK_FLAGS. +# +# LICENSE +# +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 7 + +AC_DEFUN([AX_APPEND_COMPILE_FLAGS], +[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) +AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) +for flag in $1; do + AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4]) +done +])dnl AX_APPEND_COMPILE_FLAGS diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4 new file mode 100644 index 000000000..dd6d8b614 --- /dev/null +++ b/m4/ax_append_flag.m4 @@ -0,0 +1,50 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_append_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) +# +# DESCRIPTION +# +# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space +# added in between. +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains +# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly +# FLAG. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AC_DEFUN([AX_APPEND_FLAG], +[dnl +AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF +AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) +AS_VAR_SET_IF(FLAGS,[ + AS_CASE([" AS_VAR_GET(FLAGS) "], + [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], + [ + AS_VAR_APPEND(FLAGS,[" $1"]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) + ], + [ + AS_VAR_SET(FLAGS,[$1]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) +AS_VAR_POPDEF([FLAGS])dnl +])dnl AX_APPEND_FLAG diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 000000000..bd753b34d --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,53 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_require_defined.m4 b/m4/ax_require_defined.m4 new file mode 100644 index 000000000..17c3eab7d --- /dev/null +++ b/m4/ax_require_defined.m4 @@ -0,0 +1,37 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_require_defined.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_REQUIRE_DEFINED(MACRO) +# +# DESCRIPTION +# +# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have +# been defined and thus are available for use. This avoids random issues +# where a macro isn't expanded. Instead the configure script emits a +# non-fatal: +# +# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found +# +# It's like AC_REQUIRE except it doesn't expand the required macro. +# +# Here's an example: +# +# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) +# +# LICENSE +# +# Copyright (c) 2014 Mike Frysinger +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 2 + +AC_DEFUN([AX_REQUIRE_DEFINED], [dnl + m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) +])dnl AX_REQUIRE_DEFINED diff --git a/rename.sh b/rename.sh new file mode 100644 index 000000000..ded105d2a --- /dev/null +++ b/rename.sh @@ -0,0 +1,13 @@ +ag --ignore rename.sh -l "event-rule-uprobe-internal\.h" | xargs sed -i 's/event-rule-uprobe-internal\.h/uprobe-internal.h/g' +ag --ignore rename.sh -l "event-rule-kprobe-internal\.h" | xargs sed -i 's/event-rule-kprobe-internal\.h/kprobe-internal.h/g' +ag --ignore rename.sh -l "event-rule-kretprobe-internal\.h" | xargs sed -i 's/event-rule-kretprobe-internal\.h/kretprobe-internal.h/g' +ag --ignore rename.sh -l "event-rule-syscall-internal\.h" | xargs sed -i 's/event-rule-syscall-internal\.h/syscall-internal.h/g' +ag --ignore rename.sh -l "event-rule-tracepoint-internal\.h" | xargs sed -i 's/event-rule-tracepoint-internal\.h/tracepoint-internal.h/g' + +ag --ignore rename.sh -l "event-rule-uprobe\.h" | xargs sed -i 's/event-rule-uprobe\.h/uprobe.h/g' +ag --ignore rename.sh -l "event-rule-kprobe\.h" | xargs sed -i 's/event-rule-kprobe\.h/kprobe.h/g' +ag --ignore rename.sh -l "event-rule-kretprobe\.h" | xargs sed -i 's/event-rule-kretprobe\.h/kretprobe.h/g' +ag --ignore rename.sh -l "event-rule-syscall\.h" | xargs sed -i 's/event-rule-syscall\.h/syscall.h/g' +ag --ignore rename.sh -l "event-rule-tracepoint\.h" | xargs sed -i 's/event-rule-tracepoint\.h/tracepoint.h/g' +git add . +git commit --amend --no-edit diff --git a/src/bin/lttng-crash/lttng-crash.c b/src/bin/lttng-crash/lttng-crash.c index c0bf42c5a..98c52c59d 100644 --- a/src/bin/lttng-crash/lttng-crash.c +++ b/src/bin/lttng-crash/lttng-crash.c @@ -180,9 +180,9 @@ struct lttng_crash_layout { }; /* Variables */ -static char *progname, - *opt_viewer_path = NULL, - *opt_output_path = NULL; +static const char *progname; +static char *opt_viewer_path = NULL; +static char *opt_output_path = NULL; static char *input_path; @@ -296,7 +296,7 @@ static int parse_args(int argc, char **argv) } if (!opt_viewer_path) { - opt_viewer_path = DEFAULT_VIEWER; + opt_viewer_path = (char *) DEFAULT_VIEWER; } /* No leftovers, or more than one input path, print usage and quit */ diff --git a/src/bin/lttng-relayd/cmd-2-2.c b/src/bin/lttng-relayd/cmd-2-2.c index 09014be0f..8566ecfa8 100644 --- a/src/bin/lttng-relayd/cmd-2-2.c +++ b/src/bin/lttng-relayd/cmd-2-2.c @@ -10,6 +10,8 @@ #define _LGPL_SOURCE #include +#include "cmd-2-2.h" + #include #include diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am index ee53655be..dd807125b 100644 --- a/src/bin/lttng-sessiond/Makefile.am +++ b/src/bin/lttng-sessiond/Makefile.am @@ -54,7 +54,8 @@ lttng_sessiond_SOURCES = utils.c utils.h \ manage-kernel.c manage-kernel.h \ manage-consumer.c manage-consumer.h \ clear.c clear.h \ - tracker.c tracker.h + tracker.c tracker.h \ + action-executor.c action-executor.h if HAVE_LIBLTTNG_UST_CTL lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \ diff --git a/src/bin/lttng-sessiond/action-executor.c b/src/bin/lttng-sessiond/action-executor.c new file mode 100644 index 000000000..40686eb7b --- /dev/null +++ b/src/bin/lttng-sessiond/action-executor.c @@ -0,0 +1,654 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "action-executor.h" +#include "cmd.h" +#include "health-sessiond.h" +#include "lttng-sessiond.h" +#include "notification-thread-internal.h" +#include "session.h" +#include "thread.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define THREAD_NAME "Action Executor" +#define MAX_QUEUED_WORK_COUNT 8192 + +struct action_work_item { + uint64_t id; + struct lttng_trigger *trigger; + struct notification_client_list *client_list; + struct cds_list_head list_node; +}; + +struct action_executor { + struct lttng_thread *thread; + struct notification_thread_handle *notification_thread_handle; + struct { + uint64_t pending_count; + struct cds_list_head list; + pthread_cond_t cond; + pthread_mutex_t lock; + } work; + bool should_quit; + uint64_t next_work_item_id; +}; + +typedef int (*action_executor_handler)(struct action_executor *executor, + const struct action_work_item *, + const struct lttng_action *action); + +static int action_executor_notify_handler(struct action_executor *executor, + const struct action_work_item *, + const struct lttng_action *); +static int action_executor_start_session_handler(struct action_executor *executor, + const struct action_work_item *, + const struct lttng_action *); +static int action_executor_stop_session_handler(struct action_executor *executor, + const struct action_work_item *, + const struct lttng_action *); +static int action_executor_rotate_session_handler(struct action_executor *executor, + const struct action_work_item *, + const struct lttng_action *); +static int action_executor_snapshot_session_handler(struct action_executor *executor, + const struct action_work_item *, + const struct lttng_action *); +static int action_executor_group_handler(struct action_executor *executor, + const struct action_work_item *, + const struct lttng_action *); +static int action_executor_generic_handler(struct action_executor *executor, + const struct action_work_item *, + const struct lttng_action *); + +static const action_executor_handler action_executors[] = { + [LTTNG_ACTION_TYPE_NOTIFY] = action_executor_notify_handler, + [LTTNG_ACTION_TYPE_START_SESSION] = action_executor_start_session_handler, + [LTTNG_ACTION_TYPE_STOP_SESSION] = action_executor_stop_session_handler, + [LTTNG_ACTION_TYPE_ROTATE_SESSION] = action_executor_rotate_session_handler, + [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = action_executor_snapshot_session_handler, + [LTTNG_ACTION_TYPE_GROUP] = action_executor_group_handler, +}; + +static const char *get_action_name(const struct lttng_action *action) +{ + const char *action_type_names[] = { + [LTTNG_ACTION_TYPE_NOTIFY] = "Notify", + [LTTNG_ACTION_TYPE_START_SESSION] = "Start session", + [LTTNG_ACTION_TYPE_STOP_SESSION] = "Stop session", + [LTTNG_ACTION_TYPE_ROTATE_SESSION] = "Rotate session", + [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = "Snapshot session", + [LTTNG_ACTION_TYPE_GROUP] = "Group", + }; + + return action_type_names[lttng_action_get_type(action)]; +} + +static const char *get_trigger_name(const struct lttng_trigger *trigger) +{ + const char *trigger_name; + enum lttng_trigger_status trigger_status; + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + return trigger_name; +} + +static int client_handle_transmission_status( + struct notification_client *client, + enum client_transmission_status status, + void *user_data) +{ + struct action_executor *executor = user_data; + + switch (status) { + case CLIENT_TRANSMISSION_STATUS_COMPLETE: + DBG("Sent notification to client"); + break; + default: + ERR("Could not send notification to client"); + } + + return 0; +} + +static int action_executor_notify_handler(struct action_executor *executor, + const struct action_work_item *work_item, + const struct lttng_action *action) +{ + int ret = 0; + struct lttng_evaluation *evaluation = NULL; + + assert(work_item->client_list); + + evaluation = lttng_evaluation_event_rule_create( + get_trigger_name(work_item->trigger)); + if (!evaluation) { + ERR("Failed to create event rule hit evaluation"); + ret = -1; + goto end; + } + + ret = notification_client_list_send_evaluation(work_item->client_list, + lttng_trigger_get_const_condition(work_item->trigger), + evaluation, + lttng_trigger_get_credentials(work_item->trigger), NULL, + client_handle_transmission_status, executor); +end: + lttng_evaluation_destroy(evaluation); + return ret; +} + +static int action_executor_start_session_handler(struct action_executor *executor, + const struct action_work_item *work_item, + const struct lttng_action *action) +{ + int ret = 0; + const char *session_name; + enum lttng_action_status action_status; + struct ltt_session *session; + + action_status = lttng_action_start_session_get_session_name( + action, &session_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to get session name from \"%s\" action", + get_action_name(action)); + ret = -1; + goto end; + } + + session_lock_list(); + session = session_find_by_name(session_name); + if (session) { + enum lttng_error_code cmd_ret; + + session_lock(session); + cmd_ret = cmd_start_trace(session); + session_unlock(session); + + switch (cmd_ret) { + case LTTNG_OK: + DBG("Successfully started session \"%s\" on behalf of trigger \"%s\"", + session_name, + get_trigger_name(work_item->trigger)); + break; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + DBG("Attempted to start session \"%s\" on behalf of trigger \"%s\" but it was already started", + session_name, + get_trigger_name(work_item->trigger)); + break; + default: + WARN("Failed to start session \"%s\" on behalf of trigger \"%s\": %s", + session_name, + get_trigger_name(work_item->trigger), + lttng_strerror(-cmd_ret)); + break; + } + session_put(session); + } else { + DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"", + session_name, get_action_name(action), + get_trigger_name(work_item->trigger)); + } + session_unlock_list(); +end: + return ret; +} + +static int action_executor_stop_session_handler(struct action_executor *executor, + const struct action_work_item *work_item, + const struct lttng_action *action) +{ + int ret = 0; + const char *session_name; + enum lttng_action_status action_status; + struct ltt_session *session; + + action_status = lttng_action_stop_session_get_session_name( + action, &session_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to get session name from \"%s\" action", + get_action_name(action)); + ret = -1; + goto end; + } + + session_lock_list(); + session = session_find_by_name(session_name); + if (session) { + enum lttng_error_code cmd_ret; + + session_lock(session); + cmd_ret = cmd_stop_trace(session); + session_unlock(session); + + switch (cmd_ret) { + case LTTNG_OK: + DBG("Successfully stopped session \"%s\" on behalf of trigger \"%s\"", + session_name, + get_trigger_name(work_item->trigger)); + break; + case LTTNG_ERR_TRACE_ALREADY_STOPPED: + DBG("Attempted to stop session \"%s\" on behalf of trigger \"%s\" but it was already stopped", + session_name, + get_trigger_name(work_item->trigger)); + break; + default: + WARN("Failed to stop session \"%s\" on behalf of trigger \"%s\": %s", + session_name, + get_trigger_name(work_item->trigger), + lttng_strerror(-cmd_ret)); + break; + } + session_put(session); + } else { + DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"", + session_name, get_action_name(action), + get_trigger_name(work_item->trigger)); + } + session_unlock_list(); +end: + return ret; +} + +static int action_executor_rotate_session_handler(struct action_executor *executor, + const struct action_work_item *work_item, + const struct lttng_action *action) +{ + int ret = 0; + const char *session_name; + enum lttng_action_status action_status; + struct ltt_session *session; + + action_status = lttng_action_rotate_session_get_session_name( + action, &session_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to get session name from \"%s\" action", + get_action_name(action)); + ret = -1; + goto end; + } + + session_lock_list(); + session = session_find_by_name(session_name); + if (session) { + enum lttng_error_code cmd_ret; + + session_lock(session); + cmd_ret = cmd_rotate_session(session, NULL, false, + LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED); + session_unlock(session); + + switch (cmd_ret) { + case LTTNG_OK: + DBG("Successfully started rotation of session \"%s\" on behalf of trigger \"%s\"", + session_name, + get_trigger_name(work_item->trigger)); + break; + case LTTNG_ERR_ROTATION_PENDING: + DBG("Attempted to start a rotation of session \"%s\" on behalf of trigger \"%s\" but a rotation is already ongoing", + session_name, + get_trigger_name(work_item->trigger)); + break; + case LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP: + case LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR: + DBG("Attempted to start a rotation of session \"%s\" on behalf of trigger \"%s\" but a rotation has already been completed since the last stop or clear", + session_name, + get_trigger_name(work_item->trigger)); + break; + default: + WARN("Failed to start a rotation of session \"%s\" on behalf of trigger \"%s\": %s", + session_name, + get_trigger_name(work_item->trigger), + lttng_strerror(-cmd_ret)); + break; + } + session_put(session); + } else { + DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"", + session_name, get_action_name(action), + get_trigger_name(work_item->trigger)); + } + session_unlock_list(); +end: + return ret; +} + +static int action_executor_snapshot_session_handler(struct action_executor *executor, + const struct action_work_item *work_item, + const struct lttng_action *action) +{ + int ret = 0; + const char *session_name; + enum lttng_action_status action_status; + struct ltt_session *session; + const struct lttng_snapshot_output default_snapshot_output = { + .max_size = UINT64_MAX, + }; + const struct lttng_snapshot_output *snapshot_output = + &default_snapshot_output; + + action_status = lttng_action_snapshot_session_get_session_name( + action, &session_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to get session name from \"%s\" action", + get_action_name(action)); + ret = -1; + goto end; + } + + action_status = lttng_action_snapshot_session_get_output_const( + action, &snapshot_output); + if (action_status != LTTNG_ACTION_STATUS_OK && + action_status != LTTNG_ACTION_STATUS_UNSET) { + ERR("Failed to get output from \"%s\" action", + get_action_name(action)); + ret = -1; + goto end; + } + + session_lock_list(); + session = session_find_by_name(session_name); + if (session) { + enum lttng_error_code cmd_ret; + + session_lock(session); + cmd_ret = cmd_snapshot_record(session, snapshot_output, 0); + session_unlock(session); + + switch (cmd_ret) { + case LTTNG_OK: + DBG("Successfully recorded snapshot of session \"%s\" on behalf of trigger \"%s\"", + session_name, + get_trigger_name(work_item->trigger)); + break; + default: + WARN("Failed to record snapshot of session \"%s\" on behalf of trigger \"%s\": %s", + session_name, + get_trigger_name(work_item->trigger), + lttng_strerror(-cmd_ret)); + break; + } + session_put(session); + } else { + DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"", + session_name, get_action_name(action), + get_trigger_name(work_item->trigger)); + } + session_unlock_list(); +end: + return ret; +} + +static int action_executor_group_handler(struct action_executor *executor, + const struct action_work_item *work_item, + const struct lttng_action *action_group) +{ + int ret = 0; + unsigned int i, count; + enum lttng_action_status action_status; + + action_status = lttng_action_group_get_count(action_group, &count); + if (action_status != LTTNG_ACTION_STATUS_OK) { + /* Fatal error. */ + ERR("Failed to get count of action in action group"); + ret = -1; + goto end; + } + + DBG("Action group has %u action%s", count, count != 1 ? "s" : ""); + for (i = 0; i < count; i++) { + const struct lttng_action *action = + lttng_action_group_get_at_index_const( + action_group, i); + + ret = action_executor_generic_handler( + executor, work_item, action); + if (ret) { + ERR("Stopping the execution of the action group of trigger \"%s\" following a fatal error", + get_trigger_name(work_item->trigger)); + goto end; + } + } +end: + return ret; +} + +static int action_executor_generic_handler(struct action_executor *executor, + const struct action_work_item *work_item, + const struct lttng_action *action) +{ + DBG("Executing action \"%s\" of trigger \"%s\" action work item %" PRIu64, + get_action_name(action), + get_trigger_name(work_item->trigger), + work_item->id); + + return action_executors[lttng_action_get_type(action)]( + executor, work_item, action); +} + +static int action_work_item_execute(struct action_executor *executor, + struct action_work_item *work_item) +{ + int ret; + const struct lttng_action *action = + lttng_trigger_get_const_action(work_item->trigger); + + DBG("Starting execution of action work item %" PRIu64 " of trigger \"%s\"", + work_item->id, get_trigger_name(work_item->trigger)); + ret = action_executor_generic_handler(executor, work_item, action); + DBG("Completed execution of action work item %" PRIu64 " of trigger \"%s\"", + work_item->id, get_trigger_name(work_item->trigger)); + return ret; +} + +static void action_work_item_destroy(struct action_work_item *work_item) +{ + lttng_trigger_put(work_item->trigger); + notification_client_list_put(work_item->client_list); + free(work_item); +} + +static void *action_executor_thread(void *_data) +{ + struct action_executor *executor = _data; + + assert(executor); + + health_register(health_sessiond, HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR); + + rcu_register_thread(); + rcu_thread_online(); + + DBG("Entering work execution loop"); + pthread_mutex_lock(&executor->work.lock); + while (!executor->should_quit) { + int ret; + struct action_work_item *work_item; + + health_code_update(); + if (executor->work.pending_count == 0) { + health_poll_entry(); + DBG("No work items enqueued, entering wait"); + pthread_cond_wait(&executor->work.cond, + &executor->work.lock); + DBG("Woke-up from wait"); + health_poll_exit(); + continue; + } + + /* Pop item from front of the listwith work lock held. */ + work_item = cds_list_first_entry(&executor->work.list, + struct action_work_item, list_node); + cds_list_del(&work_item->list_node); + executor->work.pending_count--; + + /* + * Work can be performed without holding the work lock, + * allowing new items to be queued. + */ + pthread_mutex_unlock(&executor->work.lock); + ret = action_work_item_execute(executor, work_item); + action_work_item_destroy(work_item); + if (ret) { + /* Fatal error. */ + break; + } + health_code_update(); + pthread_mutex_lock(&executor->work.lock); + } + pthread_mutex_unlock(&executor->work.lock); + DBG("Left work execution loop"); + + health_code_update(); + + rcu_thread_offline(); + rcu_unregister_thread(); + health_unregister(health_sessiond); + + return NULL; +} + +static bool shutdown_action_executor_thread(void *_data) +{ + struct action_executor *executor = _data; + + /* TODO. */ + executor->should_quit = true; + pthread_cond_signal(&executor->work.cond); + return true; +} + +static void clean_up_action_executor_thread(void *_data) +{ + struct action_executor *executor = _data; + + assert(cds_list_empty(&executor->work.list)); + + pthread_mutex_destroy(&executor->work.lock); + pthread_cond_destroy(&executor->work.cond); + free(executor); +} + +struct action_executor *action_executor_create( + struct notification_thread_handle *handle) +{ + struct action_executor *executor = zmalloc(sizeof(*executor)); + + if (!executor) { + goto end; + } + + CDS_INIT_LIST_HEAD(&executor->work.list); + pthread_cond_init(&executor->work.cond, NULL); + pthread_mutex_init(&executor->work.lock, NULL); + executor->notification_thread_handle = handle; + + executor->thread = lttng_thread_create(THREAD_NAME, + action_executor_thread, shutdown_action_executor_thread, + clean_up_action_executor_thread, executor); +end: + return executor; +} + +void action_executor_destroy(struct action_executor *executor) +{ + struct action_work_item *work_item, *tmp; + + /* TODO Wait for work list to drain? */ + lttng_thread_shutdown(executor->thread); + pthread_mutex_lock(&executor->work.lock); + if (executor->work.pending_count != 0) { + WARN("%" PRIu64 + " trigger action%s still queued for execution and will be discarded", + executor->work.pending_count, + executor->work.pending_count == 1 ? " is" : + "s are"); + } + + cds_list_for_each_entry_safe ( + work_item, tmp, &executor->work.list, list_node) { + WARN("Discarding action work item %" PRIu64 + " associated to trigger \"%s\"", + work_item->id, get_trigger_name(work_item->trigger)); + cds_list_del(&work_item->list_node); + action_work_item_destroy(work_item); + } + pthread_mutex_unlock(&executor->work.lock); + lttng_thread_put(executor->thread); +} + +/* RCU read-lock must be held by the caller. */ +enum action_executor_status action_executor_enqueue( + struct action_executor *executor, + struct lttng_trigger *trigger, + struct notification_client_list *client_list) +{ + enum action_executor_status executor_status = ACTION_EXECUTOR_STATUS_OK; + const uint64_t work_item_id = executor->next_work_item_id++; + struct action_work_item *work_item; + bool signal = false; + + pthread_mutex_lock(&executor->work.lock); + /* Check for queue overflow. */ + if (executor->work.pending_count >= MAX_QUEUED_WORK_COUNT) { + /* Most likely spammy, remove if it is the case. */ + DBG("Refusing to enqueue action for trigger \"%s\" as work item %" PRIu64 + " (overflow)", + get_trigger_name(trigger), work_item_id); + executor_status = ACTION_EXECUTOR_STATUS_OVERFLOW; + goto error_unlock; + } + + work_item = zmalloc(sizeof(*work_item)); + if (!work_item) { + PERROR("Failed to allocate action executor work item on behalf of trigger \"%s\"", + get_trigger_name(trigger)); + executor_status = ACTION_EXECUTOR_STATUS_ERROR; + goto error_unlock; + } + + lttng_trigger_get(trigger); + if (client_list) { + const bool reference_acquired = + notification_client_list_get(client_list); + + assert(reference_acquired); + } + + *work_item = (typeof(*work_item)){ + .id = work_item_id, + .trigger = trigger, + .client_list = client_list, + .list_node = CDS_LIST_HEAD_INIT(work_item->list_node), + }; + cds_list_add_tail(&work_item->list_node, &executor->work.list); + executor->work.pending_count++; + DBG("Enqueued action for trigger \"%s\" as work item %" PRIu64, + get_trigger_name(trigger), work_item_id); + signal = true; + +error_unlock: + pthread_mutex_unlock(&executor->work.lock); + if (signal) { + pthread_cond_signal(&executor->work.cond); + } + return executor_status; +} diff --git a/src/bin/lttng-sessiond/action-executor.h b/src/bin/lttng-sessiond/action-executor.h new file mode 100644 index 000000000..60a740def --- /dev/null +++ b/src/bin/lttng-sessiond/action-executor.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef ACTION_EXECUTOR_H +#define ACTION_EXECUTOR_H + +struct action_executor; +struct notification_thread_handle; +struct lttng_trigger; +struct notification_client_list; + +enum action_executor_status { + ACTION_EXECUTOR_STATUS_OK, + ACTION_EXECUTOR_STATUS_OVERFLOW, + ACTION_EXECUTOR_STATUS_ERROR, + ACTION_EXECUTOR_STATUS_INVALID, +}; + +struct action_executor *action_executor_create( + struct notification_thread_handle *handle); + +void action_executor_destroy(struct action_executor *executor); + +enum action_executor_status action_executor_enqueue( + struct action_executor *executor, + struct lttng_trigger *trigger, + struct notification_client_list *list); + +#endif /* ACTION_EXECUTOR_H */ diff --git a/src/bin/lttng-sessiond/agent-thread.c b/src/bin/lttng-sessiond/agent-thread.c index c278ce95e..3587fd471 100644 --- a/src/bin/lttng-sessiond/agent-thread.c +++ b/src/bin/lttng-sessiond/agent-thread.c @@ -49,6 +49,8 @@ static void update_agent_app(struct agent_app *app) { struct ltt_session *session, *stmp; struct ltt_session_list *list; + struct agent *trigger_agent; + struct lttng_ht_iter iter; list = session_get_list(); assert(list); @@ -74,6 +76,14 @@ static void update_agent_app(struct agent_app *app) session_put(session); } session_unlock_list(); + + /* Do we need more locking here? maybe against trigger add? */ + rcu_read_lock(); + cds_lfht_for_each_entry (trigger_agents_ht_by_domain->ht, &iter.iter, + trigger_agent, node.node) { + agent_update(trigger_agent, app->sock->fd); + } + rcu_read_unlock(); } /* diff --git a/src/bin/lttng-sessiond/agent.c b/src/bin/lttng-sessiond/agent.c index 1926b0820..0b95c4d1d 100644 --- a/src/bin/lttng-sessiond/agent.c +++ b/src/bin/lttng-sessiond/agent.c @@ -11,6 +11,12 @@ #include #include +#include +#include +#include +#include +#include + #include #include @@ -101,7 +107,8 @@ no_match: } /* - * Match function for the events hash table lookup by name and loglevel. + * Match function for the events hash table lookup by name, loglevel and + * filter_expression. */ static int ht_match_event(struct cds_lfht_node *node, const void *_key) @@ -682,6 +689,7 @@ int agent_enable_event(struct agent_event *event, } event->enabled = 1; + event->user_refcount++; ret = LTTNG_OK; error: @@ -791,6 +799,17 @@ int agent_disable_event(struct agent_event *event, goto end; } + if (event->user_refcount - 1 != 0) { + /* + * Disable the agent event only when all users (trigger etc.) + * have disabled it. + */ + + event->user_refcount--; + ret = LTTNG_OK; + goto end; + } + rcu_read_lock(); cds_lfht_for_each_entry(agent_apps_ht_by_sock->ht, &iter.iter, app, @@ -806,6 +825,7 @@ int agent_disable_event(struct agent_event *event, } } + event->user_refcount = 0; event->enabled = 0; error: @@ -1206,6 +1226,66 @@ void agent_find_events_by_name(const char *name, struct agent *agt, ht_match_event_by_name, &key, &iter->iter); } +/* + * Find the agent event matching the trigger. + * + * RCU read side lock MUST be acquired. It must be kept for as long as + * the returned agent_event is used. + * + * Return object if found else NULL. + */ +struct agent_event *agent_find_event_by_trigger( + const struct lttng_trigger *trigger, struct agent *agt) +{ + enum lttng_condition_status c_status; + enum lttng_event_rule_status er_status; + enum lttng_domain_type d_type; + const struct lttng_condition *condition; + const struct lttng_event_rule *rule; + const char *name; + const char *filter_expression; + /* TODO validate if this is the unset value or no */ + int loglevel_value = 0; + enum lttng_loglevel_type loglevel_type; + + assert(agt); + assert(agt->events); + + condition = lttng_trigger_get_const_condition(trigger); + + assert(lttng_condition_get_type(condition) == + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + c_status = lttng_condition_event_rule_get_rule(condition, &rule); + assert(c_status == LTTNG_CONDITION_STATUS_OK); + + assert(lttng_event_rule_get_type(rule) == + LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + + d_type = lttng_event_rule_get_domain_type(rule); + assert(d_type == LTTNG_DOMAIN_JUL || d_type == LTTNG_DOMAIN_LOG4J || + d_type == LTTNG_DOMAIN_PYTHON); + + /* Get the name (aka pattern) */ + er_status = lttng_event_rule_tracepoint_get_pattern(rule, &name); + assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + + /* Get the internal filter_expression */ + filter_expression = lttng_event_rule_get_filter(rule); + + er_status = lttng_event_rule_tracepoint_get_loglevel_type( + rule, &loglevel_type); + assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + er_status = lttng_event_rule_tracepoint_get_loglevel( + rule, &loglevel_value); + assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + } + + return agent_find_event(name, loglevel_type, loglevel_value, + filter_expression, agt); +} + /* * Get the next agent event duplicate by name. This should be called * after a call to agent_find_events_by_name() to iterate on events. @@ -1233,8 +1313,10 @@ void agent_event_next_duplicate(const char *name, * Return object if found else NULL. */ struct agent_event *agent_find_event(const char *name, - enum lttng_loglevel_type loglevel_type, int loglevel_value, - char *filter_expression, struct agent *agt) + enum lttng_loglevel_type loglevel_type, + int loglevel_value, + const char *filter_expression, + struct agent *agt) { struct lttng_ht_node_str *node; struct lttng_ht_iter iter; @@ -1347,6 +1429,21 @@ int agent_app_ht_alloc(void) return ret; } +/* + * Allocate agent_apps_ht_by_sock. + */ +int trigger_agent_ht_alloc(void) +{ + int ret = 0; + + trigger_agents_ht_by_domain = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!trigger_agents_ht_by_domain) { + ret = -1; + } + + return ret; +} + /* * Destroy a agent application by socket. */ @@ -1396,6 +1493,32 @@ void agent_app_ht_clean(void) lttng_ht_destroy(agent_apps_ht_by_sock); } +/* + * Clean-up the trigger agent hash table and destroy it. + */ +void trigger_agent_ht_clean(void) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + + if (!trigger_agents_ht_by_domain) { + return; + } + rcu_read_lock(); + cds_lfht_for_each_entry (trigger_agents_ht_by_domain->ht, &iter.iter, + node, node) { + struct agent *agent; + + (void) lttng_ht_del(trigger_agents_ht_by_domain, &iter); + + agent = caa_container_of(node, struct agent, node); + agent_destroy(agent); + } + rcu_read_unlock(); + + lttng_ht_destroy(trigger_agents_ht_by_domain); +} + /* * Update a agent application (given socket) using the given agent. * @@ -1449,3 +1572,27 @@ void agent_update(struct agent *agt, int sock) rcu_read_unlock(); } + +struct agent *trigger_find_agent(enum lttng_domain_type domain_type) +{ + struct agent *agt = NULL; + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + uint64_t key; + + assert(trigger_agents_ht_by_domain); + + DBG3("Trigger agent lookup for domain %d", domain_type); + + key = domain_type; + + lttng_ht_lookup(trigger_agents_ht_by_domain, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (!node) { + goto end; + } + agt = caa_container_of(node, struct agent, node); + +end: + return agt; +} diff --git a/src/bin/lttng-sessiond/agent.h b/src/bin/lttng-sessiond/agent.h index 2d2d64255..ebb12628e 100644 --- a/src/bin/lttng-sessiond/agent.h +++ b/src/bin/lttng-sessiond/agent.h @@ -24,11 +24,15 @@ */ extern struct lttng_ht *agent_apps_ht_by_sock; +/* + * Hash table that contains the trigger agents by domain */ +extern struct lttng_ht *trigger_agents_ht_by_domain; + struct agent_ht_key { const char *name; int loglevel_value; enum lttng_loglevel_type loglevel_type; - char *filter_expression; + const char *filter_expression; }; /* @@ -87,6 +91,12 @@ struct agent_event { struct lttng_filter_bytecode *filter; char *filter_expression; struct lttng_event_exclusion *exclusion; + + /* + * Multiple triggers and events can use this agent event. + * The event can only be disabled when the count is zero. + */ + unsigned int user_refcount; }; /* @@ -133,8 +143,10 @@ struct agent_event *agent_create_event(const char *name, void agent_add_event(struct agent_event *event, struct agent *agt); struct agent_event *agent_find_event(const char *name, - enum lttng_loglevel_type loglevel_type, int loglevel_value, - char *filter_expression, struct agent *agt); + enum lttng_loglevel_type loglevel_type, + int loglevel_value, + const char *filter_expression, + struct agent *agt); void agent_find_events_by_name(const char *name, struct agent *agt, struct lttng_ht_iter* iter); void agent_event_next_duplicate(const char *name, @@ -167,4 +179,12 @@ void agent_update(struct agent *agt, int sock); int agent_list_events(struct lttng_event **events, enum lttng_domain_type domain); +struct agent_event *agent_find_event_by_trigger( + const struct lttng_trigger *trigger, struct agent *agt); + +/* todo: find a better place for this */ +struct agent *trigger_find_agent(enum lttng_domain_type domain_type); +void trigger_agent_ht_clean(void); +int trigger_agent_ht_alloc(void); + #endif /* LTTNG_SESSIOND_AGENT_H */ diff --git a/src/bin/lttng-sessiond/client.c b/src/bin/lttng-sessiond/client.c index ae0e02092..e381514e0 100644 --- a/src/bin/lttng-sessiond/client.c +++ b/src/bin/lttng-sessiond/client.c @@ -30,6 +30,7 @@ #include "utils.h" #include "manage-consumer.h" #include "clear.h" +#include "agent-thread.h" static bool is_root; @@ -182,7 +183,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) case LTTNG_CONSUMER64_UST: { if (config.consumerd64_lib_dir.value) { - char *tmp; + const char *tmp; size_t tmplen; char *tmpnew; @@ -219,7 +220,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) case LTTNG_CONSUMER32_UST: { if (config.consumerd32_lib_dir.value) { - char *tmp; + const char *tmp; size_t tmplen; char *tmpnew; @@ -532,7 +533,6 @@ static int receive_userspace_probe(struct command_ctx *cmd_ctx, int sock, { int fd, ret; struct lttng_userspace_probe_location *probe_location; - const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; struct lttng_dynamic_buffer probe_location_buffer; struct lttng_buffer_view buffer_view; @@ -592,30 +592,7 @@ static int receive_userspace_probe(struct command_ctx *cmd_ctx, int sock, * Set the file descriptor received from the client through the unix * socket in the probe location. */ - lookup = lttng_userspace_probe_location_get_lookup_method(probe_location); - if (!lookup) { - ret = LTTNG_ERR_PROBE_LOCATION_INVAL; - goto error; - } - - /* - * From the kernel tracer's perspective, all userspace probe event types - * are all the same: a file and an offset. - */ - switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - ret = lttng_userspace_probe_location_function_set_binary_fd( - probe_location, fd); - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - ret = lttng_userspace_probe_location_tracepoint_set_binary_fd( - probe_location, fd); - break; - default: - ret = LTTNG_ERR_PROBE_LOCATION_INVAL; - goto error; - } - + ret = lttng_userspace_probe_location_set_binary_fd(probe_location, fd); if (ret) { ret = LTTNG_ERR_PROBE_LOCATION_INVAL; goto error; @@ -710,6 +687,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, int ret = LTTNG_OK; int need_tracing_session = 1; int need_domain; + int need_consumerd = 1; DBG("Processing client command %d", cmd_ctx->lsm->cmd_type); @@ -733,19 +711,29 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_SET_SESSION_SHM_PATH: case LTTNG_REGENERATE_METADATA: case LTTNG_REGENERATE_STATEDUMP: - case LTTNG_REGISTER_TRIGGER: - case LTTNG_UNREGISTER_TRIGGER: case LTTNG_ROTATE_SESSION: case LTTNG_ROTATION_GET_INFO: case LTTNG_ROTATION_SET_SCHEDULE: case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: case LTTNG_CLEAR_SESSION: + case LTTNG_LIST_TRIGGERS: need_domain = 0; break; default: need_domain = 1; } + /* Needs a functioning consumerd */ + switch (cmd_ctx->lsm->cmd_type) { + case LTTNG_REGISTER_TRIGGER: + case LTTNG_UNREGISTER_TRIGGER: + need_consumerd = 0; + break; + default: + need_consumerd = 1; + break; + } + if (config.no_kernel && need_domain && cmd_ctx->lsm->domain.type == LTTNG_DOMAIN_KERNEL) { if (!is_root) { @@ -785,6 +773,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_ROTATE_SESSION: case LTTNG_ROTATION_GET_INFO: case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: + case LTTNG_REGISTER_TRIGGER: + case LTTNG_LIST_TRIGGERS: break; default: /* Setup lttng message with no payload */ @@ -805,6 +795,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_SAVE_SESSION: case LTTNG_REGISTER_TRIGGER: case LTTNG_UNREGISTER_TRIGGER: + case LTTNG_LIST_TRIGGERS: need_tracing_session = 0; break; default: @@ -883,7 +874,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, } /* Consumer is in an ERROR state. Report back to client */ - if (uatomic_read(&kernel_consumerd_state) == CONSUMER_ERROR) { + if (need_consumerd && uatomic_read(&kernel_consumerd_state) == + CONSUMER_ERROR) { ret = LTTNG_ERR_NO_KERNCONSUMERD; goto error; } @@ -928,14 +920,21 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_LOG4J: case LTTNG_DOMAIN_PYTHON: + if (!agent_tracing_is_enabled()) { + ret = LTTNG_ERR_AGENT_TRACING_DISABLED; + goto error; + } + /* Fallthrough */ case LTTNG_DOMAIN_UST: { if (!ust_app_supported()) { ret = LTTNG_ERR_NO_UST; goto error; } + /* Consumer is in an ERROR state. Report back to client */ - if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) { + if (need_consumerd && uatomic_read(&ust_consumerd_state) == + CONSUMER_ERROR) { ret = LTTNG_ERR_NO_USTCONSUMERD; goto error; } @@ -1957,8 +1956,33 @@ error_add_context: } case LTTNG_REGISTER_TRIGGER: { + struct lttng_dynamic_buffer payload; + struct lttng_trigger *return_trigger; + + lttng_dynamic_buffer_init(&payload); ret = cmd_register_trigger(cmd_ctx, *sock, - notification_thread_handle); + notification_thread_handle, &return_trigger); + if (ret != LTTNG_OK) { + goto error; + } + + ret = lttng_trigger_serialize(return_trigger, &payload, NULL); + if (ret) { + ERR("Failed to serialize trigger in reply to \"register trigger\" command"); + ret = LTTNG_ERR_NOMEM; + lttng_trigger_destroy(return_trigger); + goto error; + } + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, payload.data, + payload.size); + if (ret) { + lttng_trigger_destroy(return_trigger); + ret = LTTNG_ERR_NOMEM; + goto error; + } + lttng_trigger_destroy(return_trigger); + lttng_dynamic_buffer_reset(&payload); + ret = LTTNG_OK; break; } case LTTNG_UNREGISTER_TRIGGER: @@ -2071,6 +2095,37 @@ error_add_context: ret = cmd_clear_session(cmd_ctx->session, sock); break; } + case LTTNG_LIST_TRIGGERS: + { + struct lttng_dynamic_buffer payload; + struct lttng_triggers *return_triggers; + + lttng_dynamic_buffer_init(&payload); + ret = cmd_list_triggers(cmd_ctx, *sock, + notification_thread_handle, &return_triggers); + if (ret != LTTNG_OK) { + goto error; + } + + ret = lttng_triggers_serialize(return_triggers, &payload); + if (ret) { + ERR("Failed to serialize triggers in reply to \"list triggers\" command"); + ret = LTTNG_ERR_NOMEM; + lttng_triggers_destroy(return_triggers); + goto error; + } + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, payload.data, + payload.size); + if (ret) { + ret = LTTNG_ERR_NOMEM; + lttng_triggers_destroy(return_triggers); + goto error; + } + lttng_dynamic_buffer_reset(&payload); + lttng_triggers_destroy(return_triggers); + ret = LTTNG_OK; + break; + } default: ret = LTTNG_ERR_UND; break; diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 5e522d6fe..f4c7390ef 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -27,6 +27,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -4283,8 +4290,94 @@ end: return ret; } +/* TODO: is this the best place to perform this? (code wise) */ +/* + * Set sock to -1 if reception of more information is not necessary e.g on + * unregister. TODO find a better way. + * + * On success LTTNG_OK. On error, returns lttng_error code. + * */ +static enum lttng_error_code prepare_trigger_object(struct lttng_trigger *trigger, int sock) +{ + enum lttng_error_code ret; + /* Internal object of the trigger might have to "generate" and + * "populate" internal field e.g filter bytecode + */ + struct lttng_condition *condition = NULL; + condition = lttng_trigger_get_condition(trigger); + if (!condition) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + { + struct lttng_event_rule *event_rule; + const struct lttng_credentials *credential = lttng_trigger_get_credentials(trigger); + lttng_condition_event_rule_get_rule_no_const( + condition, &event_rule); + ret = lttng_event_rule_populate(event_rule, credential->uid, credential->gid); + if (ret != LTTNG_OK) { + goto end; + } + + switch (lttng_event_rule_get_type(event_rule)) { + case LTTNG_EVENT_RULE_TYPE_UPROBE: + { + int fd; + struct lttng_userspace_probe_location *location = lttng_event_rule_uprobe_get_location_no_const(event_rule); + + if (sock < 0) { + /* Nothing to receive */ + break; + } + /* + * Receive the file descriptor to the target binary from + * the client. + */ + DBG("Receiving userspace probe target FD from client ..."); + ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1); + if (ret <= 0) { + DBG("Nothing recv() from client userspace probe fd... continuing"); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } + + /* + * Set the file descriptor received from the client + * through the unix socket in the probe location. + */ + ret = lttng_userspace_probe_location_set_binary_fd( + location, fd); + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } + + break; + } + default: + /* Nothing to do */ + break; + } + ret = LTTNG_OK; + break; + } + default: + { + ret = LTTNG_OK; + break; + } + } +end: + return ret; +} + +/* Caller must call lttng_destroy_trigger on the returned trigger object */ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, - struct notification_thread_handle *notification_thread) + struct notification_thread_handle *notification_thread, + struct lttng_trigger **return_trigger) { int ret; size_t trigger_len; @@ -4318,9 +4411,95 @@ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, goto end; } + /* Set the trigger credential */ + lttng_trigger_set_credentials(trigger, cmd_ctx->creds.uid, cmd_ctx->creds.gid); + + /* Prepare internal trigger object if needed on reception. + * Handles also special treatment for certain internal object of the + * trigger (e.g uprobe event rule binary fd. + */ + ret = prepare_trigger_object(trigger, sock); + if (ret != LTTNG_OK) { + goto end; + } + + /* + * Since we return the trigger object, take a reference to it + * Caller is responsible for calling lttng_destroy_trigger on it. + * This thread does not OWN the trigger. + */ + lttng_trigger_get(trigger); + + /* Inform the notification thread */ ret = notification_thread_command_register_trigger(notification_thread, trigger); - /* Ownership of trigger was transferred. */ + if (ret != LTTNG_OK) { + goto end; + } + + /* Synchronize tracers, only if needed */ + /* TODO: maybe extract somewhere else */ + { + struct lttng_condition *condition = NULL; + condition = lttng_trigger_get_condition(trigger); + if (!condition) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { + const struct lttng_event_rule *rule = NULL; + (void) lttng_condition_event_rule_get_rule(condition, &rule); + if (!rule) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) { + /* TODO: get the token value from the + * notification thread and only perform an + * enable and a disable.... This is NOT + * OPTIMIZED AT ALL + */ + kernel_update_tokens(); + } else { + /* TODO: get the token value from the + * notification thread and only perform an + * enable and a disable.... This is NOT + * OPTIMIZED AT ALL + */ + ust_app_global_update_all_tokens(); + /* Agent handling */ + if (lttng_event_rule_is_agent(rule)) { + struct agent *agt; + const char *pattern; + enum lttng_domain_type domain_type; + domain_type = lttng_event_rule_get_domain_type( + rule); + (void) lttng_event_rule_tracepoint_get_pattern( + rule, &pattern); + agt = trigger_find_agent(domain_type); + if (!agt) { + agt = agent_create(domain_type); + if (!agt) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + agent_add(agt, trigger_agents_ht_by_domain); + } + + ret = trigger_agent_enable( + trigger, agt); + if (ret != LTTNG_OK) { + goto end; + } + } + } + } + } + + /* Return an image of the updated object to the client */ + *return_trigger = trigger; + /* Ownership of trigger was transferred to caller. */ trigger = NULL; end: lttng_trigger_destroy(trigger); @@ -4363,14 +4542,110 @@ int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, goto end; } + lttng_trigger_set_credentials(trigger, cmd_ctx->creds.uid, cmd_ctx->creds.gid); + + /* TODO: forgotting this bited me for agent since the filter is + * genereted on this side. Wonder if we could find a way to detect when + * do it or not. + */ + + /* Prepare internal trigger object if needed on reception. + * Handles also special treatment for certain internal object of the + * trigger (e.g uprobe event rule binary fd. + */ + ret = prepare_trigger_object(trigger, -1); + if (ret != LTTNG_OK) { + goto end; + } + ret = notification_thread_command_unregister_trigger(notification_thread, trigger); + + /* Synchronize tracers, only if needed */ + /* TODO: maybe extract somewhere else */ + { + struct lttng_condition *condition = NULL; + condition = lttng_trigger_get_condition(trigger); + if (!condition) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { + const struct lttng_event_rule *rule = NULL; + (void) lttng_condition_event_rule_get_rule(condition, &rule); + if (!rule) { + ret = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) { + /* TODO: get the token value from the + * notification thread and only perform an + * enable and a disable.... This is NOT + * OPTIMIZED AT ALL + */ + kernel_update_tokens(); + } else { + /* TODO: get the token value from the + * notification thread and only perform an + * enable and a disable.... This is NOT + * OPTIMIZED AT ALL + */ + ust_app_global_update_all_tokens(); + if (lttng_event_rule_is_agent(rule)) { + struct agent *agt; + const char *pattern; + enum lttng_domain_type domain_type; + + domain_type = lttng_event_rule_get_domain_type( + rule); + (void) lttng_event_rule_tracepoint_get_pattern( + rule, &pattern); + + agt = trigger_find_agent(domain_type); + if (!agt) { + ret = LTTNG_ERR_UST_EVENT_NOT_FOUND; + goto end; + } + ret = trigger_agent_disable( + trigger, agt); + if (ret != LTTNG_OK) { + goto end; + } + } + } + } + } + end: lttng_trigger_destroy(trigger); lttng_dynamic_buffer_reset(&trigger_buffer); return ret; } +int cmd_list_triggers(struct command_ctx *cmd_ctx, int sock, + struct notification_thread_handle *notification_thread, + struct lttng_triggers **return_triggers) +{ + int ret = 0; + enum lttng_error_code ret_code; + struct lttng_triggers *triggers = NULL; + + /* Get list of token trigger from the notification thread here */ + ret_code = notification_thread_command_list_triggers(notification_thread_handle, cmd_ctx->creds.uid, cmd_ctx->creds.gid, &triggers); + if (ret_code != LTTNG_OK) { + ret = ret_code; + goto end; + } + + /* Return a "view" of the current triggers */ + *return_triggers = triggers; + triggers = NULL; + ret = LTTNG_OK; +end: + lttng_triggers_destroy(triggers); + return ret; +} /* * Send relayd sockets from snapshot output to consumer. Ignore request if the * snapshot output is *not* set with a remote destination. diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h index 23d9d5e02..76052100d 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -125,10 +125,15 @@ int cmd_regenerate_metadata(struct ltt_session *session); int cmd_regenerate_statedump(struct ltt_session *session); int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, - struct notification_thread_handle *notification_thread_handle); + struct notification_thread_handle *notification_thread_handle, + struct lttng_trigger **return_trigger); int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, struct notification_thread_handle *notification_thread_handle); +int cmd_list_triggers(struct command_ctx *cmd_ctx, int sock, + struct notification_thread_handle *notification_thread_handle, + struct lttng_triggers **return_triggers); + int cmd_rotate_session(struct ltt_session *session, struct lttng_rotate_session_return *rotate_return, bool quiet_rotation, diff --git a/src/bin/lttng-sessiond/dispatch.c b/src/bin/lttng-sessiond/dispatch.c index 4fe3dfce7..ea147ddf8 100644 --- a/src/bin/lttng-sessiond/dispatch.c +++ b/src/bin/lttng-sessiond/dispatch.c @@ -36,6 +36,7 @@ static void update_ust_app(int app_sock) { struct ltt_session *sess, *stmp; const struct ltt_session_list *session_list = session_get_list(); + struct ust_app *app; /* Consumer is in an ERROR state. Stop any application update. */ if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) { @@ -43,10 +44,25 @@ static void update_ust_app(int app_sock) return; } + rcu_read_lock(); + assert(app_sock >= 0); + app = ust_app_find_by_sock(app_sock); + if (app == NULL) { + /* + * Application can be unregistered before so + * this is possible hence simply stopping the + * update. + */ + DBG3("UST app update failed to find app sock %d", + app_sock); + goto unlock_rcu; + } + + /* Update all tokens for the app */ + ust_app_global_update_tokens(app); + /* For all tracing session(s) */ cds_list_for_each_entry_safe(sess, stmp, &session_list->head, list) { - struct ust_app *app; - if (!session_get(sess)) { continue; } @@ -55,26 +71,14 @@ static void update_ust_app(int app_sock) goto unlock_session; } - rcu_read_lock(); - assert(app_sock >= 0); - app = ust_app_find_by_sock(app_sock); - if (app == NULL) { - /* - * Application can be unregistered before so - * this is possible hence simply stopping the - * update. - */ - DBG3("UST app update failed to find app sock %d", - app_sock); - goto unlock_rcu; - } ust_app_global_update(sess->ust_session, app); - unlock_rcu: - rcu_read_unlock(); unlock_session: session_unlock(sess); session_put(sess); } + +unlock_rcu: + rcu_read_unlock(); } /* @@ -386,6 +390,8 @@ static void *thread_dispatch_ust_registration(void *data) /* Set app version. This call will print an error if needed. */ (void) ust_app_version(app); + (void) ust_app_setup_trigger_group(app); + /* Send notify socket through the notify pipe. */ ret = send_socket_to_thread( notifiers->apps_cmd_notify_pipe_write_fd, diff --git a/src/bin/lttng-sessiond/event.c b/src/bin/lttng-sessiond/event.c index 189236beb..6a7545dc0 100644 --- a/src/bin/lttng-sessiond/event.c +++ b/src/bin/lttng-sessiond/event.c @@ -12,6 +12,10 @@ #include #include +#include +#include +#include +#include #include #include #include @@ -27,6 +31,7 @@ #include "trace-kernel.h" #include "trace-ust.h" #include "agent.h" +#include "utils.h" /* * Add unique UST event based on the event name, filter bytecode and loglevel. @@ -366,6 +371,20 @@ error: return ret; } +static void agent_enable_all(struct agent *agt) +{ + struct agent_event *aevent; + struct lttng_ht_iter iter; + + /* Flag every event that they are now enabled. */ + rcu_read_lock(); + cds_lfht_for_each_entry ( + agt->events->ht, &iter.iter, aevent, node.node) { + aevent->enabled = 1; + } + rcu_read_unlock(); +} + /* * Enable all agent event for a given UST session. * @@ -376,8 +395,6 @@ int event_agent_enable_all(struct ltt_ust_session *usess, struct lttng_filter_bytecode *filter ,char *filter_expression) { int ret; - struct agent_event *aevent; - struct lttng_ht_iter iter; assert(usess); @@ -389,13 +406,7 @@ int event_agent_enable_all(struct ltt_ust_session *usess, goto error; } - /* Flag every event that they are now enabled. */ - rcu_read_lock(); - cds_lfht_for_each_entry(agt->events->ht, &iter.iter, aevent, - node.node) { - aevent->enabled = 1; - } - rcu_read_unlock(); + agent_enable_all(agt); ret = LTTNG_OK; @@ -467,28 +478,17 @@ end: return ret; } -/* - * Enable a single agent event for a given UST session. - * - * Return LTTNG_OK on success or else a LTTNG_ERR* code. - */ -int event_agent_enable(struct ltt_ust_session *usess, - struct agent *agt, struct lttng_event *event, +static int agent_enable(struct agent *agt, + struct lttng_event *event, struct lttng_filter_bytecode *filter, char *filter_expression) { int ret, created = 0; struct agent_event *aevent; - assert(usess); assert(event); assert(agt); - DBG("Event agent enabling %s for session %" PRIu64 " with loglevel type %d " - ", loglevel %d and filter \"%s\"", event->name, - usess->id, event->loglevel_type, event->loglevel, - filter_expression ? filter_expression : "NULL"); - aevent = agent_find_event(event->name, event->loglevel_type, event->loglevel, filter_expression, agt); if (!aevent) { @@ -542,6 +542,113 @@ end: return ret; } +/* + * Enable a single agent event for a given UST session. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int event_agent_enable(struct ltt_ust_session *usess, + struct agent *agt, + struct lttng_event *event, + struct lttng_filter_bytecode *filter, + char *filter_expression) +{ + assert(usess); + assert(event); + assert(agt); + + DBG("Event agent enabling %s for session %" PRIu64 + " with loglevel type %d " + ", loglevel %d and filter \"%s\"", + event->name, usess->id, event->loglevel_type, + event->loglevel, + filter_expression ? filter_expression : "NULL"); + + return agent_enable(agt, event, filter, filter_expression); +} + +/* + * Enable a single agent event for a trigger. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int trigger_agent_enable(const struct lttng_trigger *trigger, struct agent *agt) +{ + int ret; + enum lttng_condition_status c_status; + enum lttng_domain_type d_type; + const struct lttng_condition *condition; + const struct lttng_event_rule *rule; + const char *filter_expression; + char *filter_expression_copy = NULL; + const struct lttng_filter_bytecode *filter_bytecode; + struct lttng_filter_bytecode *filter_bytecode_copy = NULL; + struct lttng_event *event = NULL; + + assert(trigger); + assert(agt); + + condition = lttng_trigger_get_const_condition(trigger); + + assert(lttng_condition_get_type(condition) == + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + c_status = lttng_condition_event_rule_get_rule(condition, &rule); + assert(c_status == LTTNG_CONDITION_STATUS_OK); + + assert(lttng_event_rule_get_type(rule) == + LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + + d_type = lttng_event_rule_get_domain_type(rule); + assert(d_type == agt->domain); + + event = lttng_event_rule_generate_lttng_event(rule); + if (!event) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Get the internal filter_expression and bytecode */ + filter_expression = lttng_event_rule_get_filter(rule); + if (filter_expression) { + filter_expression_copy = strdup(filter_expression); + if (!filter_expression_copy) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Get the filter bytecode */ + filter_bytecode = lttng_event_rule_get_filter_bytecode(rule); + if (filter_bytecode) { + filter_bytecode_copy = + copy_filter_bytecode(filter_bytecode); + if (!filter_bytecode_copy) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + } + } + + DBG("Event agent enabling %s for trigger %" PRIu64 + " with loglevel type %d " + ", loglevel %d and filter \"%s\"", + event->name, lttng_trigger_get_key(trigger), + event->loglevel_type, event->loglevel, + filter_expression ? filter_expression : "NULL"); + + ret = agent_enable(agt, event, filter_bytecode_copy, + filter_expression_copy); + /* Ownership was passed even in case of error */ + filter_expression_copy = NULL; + filter_bytecode_copy = NULL; + +end: + free(filter_expression_copy); + free(filter_bytecode_copy); + free(event); + return ret; +} + /* * Return the default event name associated with the provided UST domain. Return * NULL on error. @@ -567,6 +674,43 @@ const char *event_get_default_agent_ust_name(enum lttng_domain_type domain) return default_event_name; } +static int trigger_agent_disable_one(const struct lttng_trigger *trigger, + struct agent *agt, + struct agent_event *aevent) + +{ + int ret; + + assert(agt); + assert(trigger); + assert(aevent); + + /* + * Actual ust event un-registration happens on the trigger + * un-registration at that point. + */ + + DBG("Event agent disabling %s (loglevel type %d, loglevel value %d) for trigger %" PRIu64, + aevent->name, aevent->loglevel_type, + aevent->loglevel_value, lttng_trigger_get_key(trigger)); + + /* Already disabled? */ + if (!aevent->enabled) { + goto end; + } + + ret = agent_disable_event(aevent, agt->domain); + if (ret != LTTNG_OK) { + goto error; + } + +end: + return LTTNG_OK; + +error: + return ret; +} + /* * Disable a given agent event for a given UST session. * @@ -659,6 +803,44 @@ error: return ret; } +/* + * Disable agent event matching a given trigger. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int trigger_agent_disable( + const struct lttng_trigger *trigger, struct agent *agt) +{ + int ret = LTTNG_OK; + struct agent_event *aevent; + + assert(trigger); + assert(agt); + + DBG("Event agent disabling for trigger %" PRIu64, + lttng_trigger_get_key(trigger)); + + rcu_read_lock(); + aevent = agent_find_event_by_trigger(trigger, agt); + + if (aevent == NULL) { + DBG2("Event agent NOT found by trigger %" PRIu64, + lttng_trigger_get_key(trigger)); + ret = LTTNG_ERR_UST_EVENT_NOT_FOUND; + goto end; + } + + ret = trigger_agent_disable_one(trigger, agt, aevent); + + if (ret != LTTNG_OK) { + goto end; + } + +end: + rcu_read_unlock(); + return ret; +} + /* * Disable all agent events matching a given name for a given UST session. * diff --git a/src/bin/lttng-sessiond/event.h b/src/bin/lttng-sessiond/event.h index 1c646db37..c7a849c0b 100644 --- a/src/bin/lttng-sessiond/event.h +++ b/src/bin/lttng-sessiond/event.h @@ -42,6 +42,11 @@ int event_agent_disable(struct ltt_ust_session *usess, struct agent *agt, const char *event_name); int event_agent_disable_all(struct ltt_ust_session *usess, struct agent *agt); +int trigger_agent_enable( + const struct lttng_trigger *trigger, struct agent *agt); +int trigger_agent_disable( + const struct lttng_trigger *trigger, struct agent *agt); + const char *event_get_default_agent_ust_name(enum lttng_domain_type domain); #endif /* _LTT_EVENT_H */ diff --git a/src/bin/lttng-sessiond/globals.c b/src/bin/lttng-sessiond/globals.c index 20aa790a7..6e111b63b 100644 --- a/src/bin/lttng-sessiond/globals.c +++ b/src/bin/lttng-sessiond/globals.c @@ -20,8 +20,12 @@ long page_size; struct health_app *health_sessiond; struct notification_thread_handle *notification_thread_handle; +pthread_mutex_t notification_trigger_tokens_ht_lock = PTHREAD_MUTEX_INITIALIZER; struct lttng_ht *agent_apps_ht_by_sock = NULL; +struct lttng_ht *trigger_agents_ht_by_domain = NULL; + +struct lttng_ht *registered_ust_event_rule = NULL; struct lttng_kernel_tracer_version kernel_tracer_version; struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version; diff --git a/src/bin/lttng-sessiond/health-sessiond.h b/src/bin/lttng-sessiond/health-sessiond.h index 7c9dbd0b3..b541822f8 100644 --- a/src/bin/lttng-sessiond/health-sessiond.h +++ b/src/bin/lttng-sessiond/health-sessiond.h @@ -23,6 +23,7 @@ enum health_type_sessiond { HEALTH_SESSIOND_TYPE_NOTIFICATION = 8, HEALTH_SESSIOND_TYPE_ROTATION = 9, HEALTH_SESSIOND_TYPE_TIMER = 10, + HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR = 11, NR_HEALTH_SESSIOND_TYPES, }; diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 4ee4bea64..646da3ba8 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -15,11 +15,20 @@ #include #include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include + #include "lttng-sessiond.h" #include "lttng-syscall.h" #include "consumer.h" @@ -29,6 +38,7 @@ #include "utils.h" #include "rotate.h" #include "modprobe.h" +#include "notification-thread-commands.h" /* * Key used to reference a channel between the sessiond and the consumer. This @@ -39,9 +49,10 @@ static uint64_t next_kernel_channel_key; static const char *module_proc_lttng = "/proc/lttng"; static int kernel_tracer_fd = -1; +static int kernel_tracer_trigger_group_fd = -1; +static int kernel_tracer_trigger_group_notification_fd = -1; +static struct ltt_kernel_token_event_rule_list kernel_tracer_token_list; -#include -#include /* * Add context on a kernel channel. * @@ -213,6 +224,44 @@ error: return -1; } +/* + * Create a kernel channel, register it to the kernel tracer and add it to the + * kernel session. + */ +static +int kernel_create_trigger_group(int *trigger_group_fd) +{ + int ret; + int local_fd = -1; + + assert(trigger_group_fd); + + /* Kernel tracer channel creation */ + ret = kernctl_create_trigger_group(kernel_tracer_fd); + if (ret < 0) { + PERROR("ioctl kernel create trigger group"); + ret = -1; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session fd"); + } + + DBG("Kernel trigger group created (fd: %d)", + local_fd); + ret = 0; + +error: + *trigger_group_fd = local_fd; + return ret; +} + /* * Compute the offset of the instrumentation byte in the binary based on the * function probe location using the ELF lookup method. @@ -224,7 +273,7 @@ error: static int extract_userspace_probe_offset_function_elf( const struct lttng_userspace_probe_location *probe_location, - struct ltt_kernel_session *session, uint64_t *offset) + uid_t uid, gid_t gid, uint64_t *offset) { int fd; int ret = 0; @@ -261,8 +310,7 @@ int extract_userspace_probe_offset_function_elf( goto end; } - ret = run_as_extract_elf_symbol_offset(fd, symbol, session->uid, - session->gid, offset); + ret = run_as_extract_elf_symbol_offset(fd, symbol, uid, gid, offset); if (ret < 0) { DBG("userspace probe offset calculation failed for " "function %s", symbol); @@ -286,7 +334,7 @@ end: static int extract_userspace_probe_offset_tracepoint_sdt( const struct lttng_userspace_probe_location *probe_location, - struct ltt_kernel_session *session, uint64_t **offsets, + uid_t uid, gid_t gid, uint64_t **offsets, uint32_t *offsets_count) { enum lttng_userspace_probe_location_lookup_method_type lookup_method_type; @@ -332,7 +380,7 @@ int extract_userspace_probe_offset_tracepoint_sdt( } ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name, - session->uid, session->gid, offsets, offsets_count); + uid, gid, offsets, offsets_count); if (ret < 0) { DBG("userspace probe offset calculation failed for sdt " "probe %s:%s", provider_name, probe_name); @@ -353,29 +401,16 @@ end: return ret; } -/* - * Extract the offsets of the instrumentation point for the different lookup - * methods. - */ static -int userspace_probe_add_callsites(struct lttng_event *ev, - struct ltt_kernel_session *session, int fd) +int userspace_probe_add_callsite( + const struct lttng_userspace_probe_location *location, + uid_t uid, gid_t gid, int fd) { const struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; enum lttng_userspace_probe_location_lookup_method_type type; - const struct lttng_userspace_probe_location *location = NULL; int ret; - assert(ev); - assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); - - location = lttng_event_get_userspace_probe_location(ev); - if (!location) { - ret = -1; - goto end; - } - lookup_method = - lttng_userspace_probe_location_get_lookup_method(location); + lookup_method = lttng_userspace_probe_location_get_lookup_method(location); if (!lookup_method) { ret = -1; goto end; @@ -388,7 +423,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, struct lttng_kernel_event_callsite callsite; uint64_t offset; - ret = extract_userspace_probe_offset_function_elf(location, session, &offset); + ret = extract_userspace_probe_offset_function_elf(location, + uid, gid, &offset); if (ret) { ret = LTTNG_ERR_PROBE_LOCATION_INVAL; goto end; @@ -397,8 +433,7 @@ int userspace_probe_add_callsites(struct lttng_event *ev, callsite.u.uprobe.offset = offset; ret = kernctl_add_callsite(fd, &callsite); if (ret) { - WARN("Adding callsite to userspace probe " - "event %s failed.", ev->name); + WARN("Adding callsite to ELF userspace probe failed."); ret = LTTNG_ERR_KERN_ENABLE_FAIL; goto end; } @@ -415,8 +450,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, * This call allocates the offsets buffer. This buffer must be freed * by the caller */ - ret = extract_userspace_probe_offset_tracepoint_sdt(location, session, - &offsets, &offsets_count); + ret = extract_userspace_probe_offset_tracepoint_sdt(location, + uid, gid, &offsets, &offsets_count); if (ret) { ret = LTTNG_ERR_PROBE_LOCATION_INVAL; goto end; @@ -425,8 +460,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, callsite.u.uprobe.offset = offsets[i]; ret = kernctl_add_callsite(fd, &callsite); if (ret) { - WARN("Adding callsite to userspace probe " - "event %s failed.", ev->name); + WARN("Adding callsite to SDT userspace probe " + "failed."); ret = LTTNG_ERR_KERN_ENABLE_FAIL; free(offsets); goto end; @@ -443,6 +478,71 @@ end: return ret; } +/* + * Extract the offsets of the instrumentation point for the different lookup + * methods. + */ +static +int userspace_probe_event_add_callsites(struct lttng_event *ev, + struct ltt_kernel_session *session, int fd) +{ + const struct lttng_userspace_probe_location *location = NULL; + int ret; + + assert(ev); + assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); + + location = lttng_event_get_userspace_probe_location(ev); + if (!location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite(location, session->uid, session->gid, + fd); + if (ret) { + WARN("Adding callsite to userspace probe event \"%s\" " + "failed.", ev->name); + } + +end: + return ret; +} + +/* + * Extract the offsets of the instrumentation point for the different lookup + * methods. + */ +static int userspace_probe_event_rule_add_callsites( + const struct lttng_event_rule *rule, + const struct lttng_credentials *creds, + int fd) +{ + const struct lttng_userspace_probe_location *location = NULL; + enum lttng_event_rule_status status; + int ret; + + assert(rule); + assert(creds); + assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE); + + status = lttng_event_rule_uprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite(location, creds->uid, creds->gid, + fd); + if (ret) { + WARN("Adding callsite to userspace probe object %d" + "failed.", fd); + } + +end: + return ret; +} + /* * Create a kernel event, enable it to the kernel tracer and add it to the * channel event list of the kernel session. @@ -512,7 +612,8 @@ int kernel_create_event(struct lttng_event *ev, } if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) { - ret = userspace_probe_add_callsites(ev, channel->session, event->fd); + ret = userspace_probe_event_add_callsites(ev, channel->session, + event->fd); if (ret) { goto add_callsite_error; } @@ -669,6 +770,36 @@ error: return ret; } +/* + * Disable a kernel trigger. + */ +int kernel_disable_token_event_rule(struct ltt_kernel_token_event_rule *event) +{ + int ret; + + assert(event); + + ret = kernctl_disable(event->fd); + if (ret < 0) { + switch (-ret) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("disable kernel event"); + break; + } + goto error; + } + + event->enabled = 0; + DBG("Kernel trigger token %" PRIu64" disabled (fd: %d)", event->token, event->fd); + + return 0; + +error: + return ret; +} static struct lttng_tracker_list *get_id_tracker_list( struct ltt_kernel_session *session, enum lttng_tracker_type tracker_type) @@ -1732,20 +1863,37 @@ int init_kernel_tracer(void) if (ret < 0) { goto error_modules; } - if (ret < 1) { WARN("Kernel tracer does not support buffer monitoring. " "The monitoring timer of channels in the kernel domain " "will be set to 0 (disabled)."); } + ret = kernel_create_trigger_group(&kernel_tracer_trigger_group_fd); + if (ret < 0) { + /* TODO: error handling if it is not supported etc. */ + WARN("Failed trigger group creation"); + kernel_tracer_trigger_group_fd = -1; + /* This is not fatal */ + } else { + ret = kernel_create_trigger_group_notification_fd(&kernel_tracer_trigger_group_notification_fd); + if (ret < 0) { + goto error_modules; + } + } + + CDS_INIT_LIST_HEAD(&kernel_tracer_token_list.head); + DBG("Kernel tracer fd %d", kernel_tracer_fd); + DBG("Kernel tracer trigger group fd %d", kernel_tracer_trigger_group_fd); + DBG("Kernel tracer trigger group notificationi fd %d", kernel_tracer_trigger_group_notification_fd); ret = syscall_init_table(kernel_tracer_fd); if (ret < 0) { ERR("Unable to populate syscall table. Syscall tracing won't " "work for this session daemon."); } + return 0; error_version: @@ -1781,6 +1929,31 @@ void cleanup_kernel_tracer(void) { int ret; + struct ltt_kernel_token_event_rule *rule, *rtmp; + cds_list_for_each_entry_safe(rule, rtmp, &kernel_tracer_token_list.head, list) { + kernel_disable_token_event_rule(rule); + trace_kernel_destroy_token_event_rule(rule); + } + + DBG2("Closing kernel trigger group notification fd"); + if (kernel_tracer_trigger_group_notification_fd >= 0) { + ret = close(kernel_tracer_trigger_group_notification_fd); + if (ret) { + PERROR("close"); + } + kernel_tracer_trigger_group_notification_fd = -1; + } + + /* TODO: do we iterate over the list to remove all token? */ + DBG2("Closing kernel trigger group fd"); + if (kernel_tracer_trigger_group_fd >= 0) { + ret = close(kernel_tracer_trigger_group_fd); + if (ret) { + PERROR("close"); + } + kernel_tracer_trigger_group_fd = -1; + } + DBG2("Closing kernel fd"); if (kernel_tracer_fd >= 0) { ret = close(kernel_tracer_fd); @@ -1789,6 +1962,8 @@ void cleanup_kernel_tracer(void) } kernel_tracer_fd = -1; } + + DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); free(syscall_table); @@ -1879,3 +2054,274 @@ end: rcu_read_unlock(); return status; } + +enum lttng_error_code kernel_create_trigger_group_notification_fd( + int *trigger_group_notification_fd) +{ + enum lttng_error_code ret = LTTNG_OK; + int local_fd = -1; + + assert(trigger_group_notification_fd); + + ret = kernctl_create_trigger_group_notification_fd(kernel_tracer_trigger_group_fd); + if (ret < 0) { + PERROR("ioctl kernel create trigger group"); + ret = -1; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session fd"); + } + + DBG("Kernel trigger group notification created (fd: %d)", + local_fd); + ret = 0; + +error: + *trigger_group_notification_fd = local_fd; + return ret; +} + +enum lttng_error_code kernel_destroy_trigger_group_notification_fd( + int trigger_group_notification_fd) +{ + enum lttng_error_code ret = LTTNG_OK; + DBG("Closing trigger group notification fd %d", trigger_group_notification_fd); + if (trigger_group_notification_fd >= 0) { + ret = close(trigger_group_notification_fd); + if (ret) { + PERROR("close"); + } + } + return ret; +} + +static int kernel_create_token_event_rule(struct lttng_event_rule *rule, + const struct lttng_credentials *creds, uint64_t token) +{ + int err, fd; + enum lttng_error_code ret; + struct ltt_kernel_token_event_rule *event; + struct lttng_kernel_trigger trigger; + + assert(rule); + + ret = trace_kernel_create_token_event_rule(rule, token, &event); + if (ret != LTTNG_OK) { + goto error; + } + + trace_kernel_init_trigger_from_event_rule(event->event_rule, &trigger); + trigger.id = event->token; + + fd = kernctl_create_trigger(kernel_tracer_trigger_group_fd, &trigger); + if (fd < 0) { + switch (-fd) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Trigger type not implemented"); + ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Event %s not found!", trigger.name); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + PERROR("create trigger ioctl"); + } + goto free_event; + } + + event->fd = fd; + /* Prevent fd duplication after execlp() */ + err = fcntl(event->fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("fcntl session fd"); + } + + if (event->filter) { + err = kernctl_filter(event->fd, event->filter); + if (err < 0) { + switch (-err) { + case ENOMEM: + ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + ret = LTTNG_ERR_FILTER_INVAL; + break; + } + goto filter_error; + } + } + + if (lttng_event_rule_get_type(event->event_rule) == + LTTNG_EVENT_RULE_TYPE_UPROBE) { + ret = userspace_probe_event_rule_add_callsites( + rule, creds, event->fd); + if (ret) { + goto add_callsite_error; + } + } + + err = kernctl_enable(event->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel trigger"); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add event to event list */ + cds_list_add(&event->list, &kernel_tracer_token_list.head); + + DBG("Trigger %s created (fd: %d)", trigger.name, event->fd); + + return 0; + +add_callsite_error: +enable_error: +filter_error: + { + int closeret; + + closeret = close(event->fd); + if (closeret) { + PERROR("close event fd"); + } + } +free_event: + free(event); +error: + return ret; +} + +enum lttng_error_code kernel_update_tokens(void) +{ + enum lttng_error_code ret = LTTNG_OK; + enum lttng_trigger_status t_status; + struct ltt_kernel_token_event_rule *token_event_rule_element; + struct lttng_triggers *triggers; + unsigned int count; + + /* TODO error handling */ + + /* Get list of token trigger from the notification thread here */ + rcu_read_lock(); + pthread_mutex_lock(¬ification_trigger_tokens_ht_lock); + ret = notification_thread_command_get_tokens(notification_thread_handle, &triggers); + if (ret != LTTNG_OK) { + ret = -1; + goto end; + } + + assert(triggers); + + t_status = lttng_triggers_get_count(triggers, &count); + if (t_status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + goto end; + } + + for (unsigned int i = 0; i < count; i++) { + struct lttng_condition *condition; + struct lttng_event_rule *event_rule; + struct lttng_trigger *trigger; + struct ltt_kernel_token_event_rule *k_token; + const struct lttng_credentials *creds; + uint64_t token; + + trigger = lttng_triggers_get_pointer_of_index(triggers, i); + assert(trigger); + + /* TODO: error checking and type checking */ + token = lttng_trigger_get_key(trigger); + condition = lttng_trigger_get_condition(trigger); + (void) lttng_condition_event_rule_get_rule_no_const(condition, &event_rule); + + if (lttng_event_rule_get_domain_type(event_rule) != LTTNG_DOMAIN_KERNEL) { + /* Skip ust related trigger */ + continue; + } + + creds = lttng_trigger_get_credentials(trigger); + /* Iterate over all known token trigger */ + k_token = trace_kernel_find_trigger_by_token(&kernel_tracer_token_list, token); + if (!k_token) { + ret = kernel_create_token_event_rule(event_rule, creds, token); + if (ret < 0) { + goto end; + } + } + } + + /* Remove all unknown trigger from the app + * TODO find a way better way then this, do it on the unregister command + * and be specific on the token to remove instead of going over all + * trigger known to the app. This is sub optimal. + */ + cds_list_for_each_entry (token_event_rule_element, &kernel_tracer_token_list.head, + list) { + uint64_t token; + bool found = false; + + token = token_event_rule_element->token; + + /* + * Check if the app event trigger still exists on the + * notification side. + * TODO: might want to change the backing data struct of the + * lttng_triggers object to allow quick lookup? + * For kernel mostly all of this can be removed once we delete + * on a per trigger basis. + */ + + for (unsigned int i = 0; i < count; i++) { + struct lttng_trigger *trigger; + uint64_t inner_token; + + trigger = lttng_triggers_get_pointer_of_index( + triggers, i); + assert(trigger); + + inner_token = lttng_trigger_get_key(trigger); + + if (inner_token == token) { + found = true; + break; + } + } + + if (found) { + /* Still valid */ + continue; + } + + kernel_disable_token_event_rule(token_event_rule_element); + trace_kernel_destroy_token_event_rule(token_event_rule_element); + } +end: + rcu_read_unlock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + return ret; + +} + +int kernel_get_notification_fd(void) +{ + return kernel_tracer_trigger_group_notification_fd; +} diff --git a/src/bin/lttng-sessiond/kernel.h b/src/bin/lttng-sessiond/kernel.h index 4f3bedea5..d22a5b7a0 100644 --- a/src/bin/lttng-sessiond/kernel.h +++ b/src/bin/lttng-sessiond/kernel.h @@ -74,4 +74,11 @@ bool kernel_tracer_is_initialized(void); enum lttng_error_code kernel_create_channel_subdirectories( const struct ltt_kernel_session *ksess); +enum lttng_error_code kernel_create_trigger_group_notification_fd( + int *trigger_group_notification_fd); +enum lttng_error_code kernel_destroy_trigger_group_notification_fd( + int trigger_group_notification_fd); +enum lttng_error_code kernel_update_tokens(void); +int kernel_get_notification_fd(void); + #endif /* _LTT_KERNEL_CTL_H */ diff --git a/src/bin/lttng-sessiond/lttng-sessiond.h b/src/bin/lttng-sessiond/lttng-sessiond.h index 277fc23e3..ee8ea2400 100644 --- a/src/bin/lttng-sessiond/lttng-sessiond.h +++ b/src/bin/lttng-sessiond/lttng-sessiond.h @@ -70,6 +70,7 @@ extern struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version; /* Notification thread handle. */ extern struct notification_thread_handle *notification_thread_handle; +extern pthread_mutex_t notification_trigger_tokens_ht_lock; /* * This contains extra data needed for processing a command received by the diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 55e0ad3d7..965625c9b 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -314,6 +314,9 @@ static void sessiond_cleanup(void) pthread_mutex_destroy(&session_list->lock); + DBG("Cleaning up all trigger agents"); + trigger_agent_ht_clean(); + DBG("Cleaning up all agent apps"); agent_app_ht_clean(); DBG("Closing all UST sockets"); @@ -1302,6 +1305,7 @@ int main(int argc, char **argv) struct lttng_thread *notification_thread = NULL; struct lttng_thread *register_apps_thread = NULL; + logger_set_thread_name("Main", false); init_kernel_workarounds(); rcu_register_thread(); @@ -1538,6 +1542,11 @@ int main(int argc, char **argv) goto stop_threads; } + if (trigger_agent_ht_alloc()) { + ERR("Failed to allocate trigger agent hash table"); + retval = -1; + goto stop_threads; + } /* * These actions must be executed as root. We do that *after* setting up * the sockets path because we MUST make the check for another daemon using @@ -1635,7 +1644,8 @@ int main(int argc, char **argv) notification_thread_handle = notification_thread_handle_create( ust32_channel_monitor_pipe, ust64_channel_monitor_pipe, - kernel_channel_monitor_pipe); + kernel_channel_monitor_pipe, + kernel_get_notification_fd()); if (!notification_thread_handle) { retval = -1; ERR("Failed to create notification thread shared data"); diff --git a/src/bin/lttng-sessiond/modprobe.c b/src/bin/lttng-sessiond/modprobe.c index 66e80e75b..7bee25f88 100644 --- a/src/bin/lttng-sessiond/modprobe.c +++ b/src/bin/lttng-sessiond/modprobe.c @@ -30,59 +30,64 @@ #define LTTNG_MOD_OPTIONAL 0 /* LTTng kernel tracer mandatory core modules list */ +/* TODO: the new trigger client might not be present in previous lttng-modules + * should it be optional? + * Can we reuse this to also know of the trigger feature is supported? + */ struct kern_modules_param kern_modules_control_core[] = { - { "lttng-ring-buffer-client-discard" }, - { "lttng-ring-buffer-client-overwrite" }, - { "lttng-ring-buffer-metadata-client" }, - { "lttng-ring-buffer-client-mmap-discard" }, - { "lttng-ring-buffer-client-mmap-overwrite" }, - { "lttng-ring-buffer-metadata-mmap-client" }, + { (char *) "lttng-ring-buffer-client-discard" }, + { (char *) "lttng-ring-buffer-client-overwrite" }, + { (char *) "lttng-ring-buffer-metadata-client" }, + { (char *) "lttng-ring-buffer-client-mmap-discard" }, + { (char *) "lttng-ring-buffer-client-mmap-overwrite" }, + { (char *) "lttng-ring-buffer-metadata-mmap-client" }, + { (char *) "lttng-ring-buffer-trigger-client" }, }; /* LTTng kernel tracer probe modules list */ struct kern_modules_param kern_modules_probes_default[] = { - { "lttng-probe-asoc" }, - { "lttng-probe-block" }, - { "lttng-probe-btrfs" }, - { "lttng-probe-compaction" }, - { "lttng-probe-ext3" }, - { "lttng-probe-ext4" }, - { "lttng-probe-gpio" }, - { "lttng-probe-i2c" }, - { "lttng-probe-irq" }, - { "lttng-probe-jbd" }, - { "lttng-probe-jbd2" }, - { "lttng-probe-kmem" }, - { "lttng-probe-kvm" }, - { "lttng-probe-kvm-x86" }, - { "lttng-probe-kvm-x86-mmu" }, - { "lttng-probe-lock" }, - { "lttng-probe-module" }, - { "lttng-probe-napi" }, - { "lttng-probe-net" }, - { "lttng-probe-power" }, - { "lttng-probe-preemptirq" }, - { "lttng-probe-printk" }, - { "lttng-probe-random" }, - { "lttng-probe-rcu" }, - { "lttng-probe-regmap" }, - { "lttng-probe-regulator" }, - { "lttng-probe-rpm" }, - { "lttng-probe-sched" }, - { "lttng-probe-scsi" }, - { "lttng-probe-signal" }, - { "lttng-probe-skb" }, - { "lttng-probe-sock" }, - { "lttng-probe-statedump" }, - { "lttng-probe-sunrpc" }, - { "lttng-probe-timer" }, - { "lttng-probe-udp" }, - { "lttng-probe-vmscan" }, - { "lttng-probe-v4l2" }, - { "lttng-probe-workqueue" }, - { "lttng-probe-writeback" }, - { "lttng-probe-x86-irq-vectors" }, - { "lttng-probe-x86-exceptions" }, + { (char *) "lttng-probe-asoc" }, + { (char *) "lttng-probe-block" }, + { (char *) "lttng-probe-btrfs" }, + { (char *) "lttng-probe-compaction" }, + { (char *) "lttng-probe-ext3" }, + { (char *) "lttng-probe-ext4" }, + { (char *) "lttng-probe-gpio" }, + { (char *) "lttng-probe-i2c" }, + { (char *) "lttng-probe-irq" }, + { (char *) "lttng-probe-jbd" }, + { (char *) "lttng-probe-jbd2" }, + { (char *) "lttng-probe-kmem" }, + { (char *) "lttng-probe-kvm" }, + { (char *) "lttng-probe-kvm-x86" }, + { (char *) "lttng-probe-kvm-x86-mmu" }, + { (char *) "lttng-probe-lock" }, + { (char *) "lttng-probe-module" }, + { (char *) "lttng-probe-napi" }, + { (char *) "lttng-probe-net" }, + { (char *) "lttng-probe-power" }, + { (char *) "lttng-probe-preemptirq" }, + { (char *) "lttng-probe-printk" }, + { (char *) "lttng-probe-random" }, + { (char *) "lttng-probe-rcu" }, + { (char *) "lttng-probe-regmap" }, + { (char *) "lttng-probe-regulator" }, + { (char *) "lttng-probe-rpm" }, + { (char *) "lttng-probe-sched" }, + { (char *) "lttng-probe-scsi" }, + { (char *) "lttng-probe-signal" }, + { (char *) "lttng-probe-skb" }, + { (char *) "lttng-probe-sock" }, + { (char *) "lttng-probe-statedump" }, + { (char *) "lttng-probe-sunrpc" }, + { (char *) "lttng-probe-timer" }, + { (char *) "lttng-probe-udp" }, + { (char *) "lttng-probe-vmscan" }, + { (char *) "lttng-probe-v4l2" }, + { (char *) "lttng-probe-workqueue" }, + { (char *) "lttng-probe-writeback" }, + { (char *) "lttng-probe-x86-irq-vectors" }, + { (char *) "lttng-probe-x86-exceptions" }, }; /* dynamic probe modules list */ diff --git a/src/bin/lttng-sessiond/notification-thread-commands.c b/src/bin/lttng-sessiond/notification-thread-commands.c index 4474d1978..9d346f947 100644 --- a/src/bin/lttng-sessiond/notification-thread-commands.c +++ b/src/bin/lttng-sessiond/notification-thread-commands.c @@ -17,7 +17,6 @@ static void init_notification_thread_command(struct notification_thread_command *cmd) { - memset(cmd, 0, sizeof(*cmd)); CDS_INIT_LIST_HEAD(&cmd->cmd_list_node); lttng_waiter_init(&cmd->reply_waiter); } @@ -54,13 +53,69 @@ error_unlock_queue: return -1; } +static +struct notification_thread_command *notification_thread_command_copy( + const struct notification_thread_command *original_cmd) +{ + struct notification_thread_command *new_cmd; + + new_cmd = zmalloc(sizeof(*new_cmd)); + if (!new_cmd) { + goto end; + } + + *new_cmd = *original_cmd; + init_notification_thread_command(new_cmd); +end: + return new_cmd; +} + +static +int run_command_no_wait(struct notification_thread_handle *handle, + const struct notification_thread_command *in_cmd) +{ + int ret; + uint64_t notification_counter = 1; + struct notification_thread_command *new_cmd = + notification_thread_command_copy(in_cmd); + + if (!new_cmd) { + goto error; + } + new_cmd->is_async = true; + + pthread_mutex_lock(&handle->cmd_queue.lock); + /* Add to queue. */ + cds_list_add_tail(&new_cmd->cmd_list_node, + &handle->cmd_queue.list); + /* Wake-up thread. */ + ret = lttng_write(lttng_pipe_get_writefd(handle->cmd_queue.event_pipe), + ¬ification_counter, sizeof(notification_counter)); + if (ret != sizeof(notification_counter)) { + PERROR("write to notification thread's queue event fd"); + /* + * Remove the command from the list so the notification + * thread does not process it. + */ + cds_list_del(&new_cmd->cmd_list_node); + goto error_unlock_queue; + } + pthread_mutex_unlock(&handle->cmd_queue.lock); + return 0; +error_unlock_queue: + free(new_cmd); + pthread_mutex_unlock(&handle->cmd_queue.lock); +error: + return -1; +} + enum lttng_error_code notification_thread_command_register_trigger( struct notification_thread_handle *handle, struct lttng_trigger *trigger) { int ret; enum lttng_error_code ret_code; - struct notification_thread_command cmd; + struct notification_thread_command cmd = {}; init_notification_thread_command(&cmd); @@ -83,7 +138,7 @@ enum lttng_error_code notification_thread_command_unregister_trigger( { int ret; enum lttng_error_code ret_code; - struct notification_thread_command cmd; + struct notification_thread_command cmd = {}; init_notification_thread_command(&cmd); @@ -108,7 +163,7 @@ enum lttng_error_code notification_thread_command_add_channel( { int ret; enum lttng_error_code ret_code; - struct notification_thread_command cmd; + struct notification_thread_command cmd = {}; init_notification_thread_command(&cmd); @@ -137,7 +192,7 @@ enum lttng_error_code notification_thread_command_remove_channel( { int ret; enum lttng_error_code ret_code; - struct notification_thread_command cmd; + struct notification_thread_command cmd = {}; init_notification_thread_command(&cmd); @@ -162,7 +217,7 @@ enum lttng_error_code notification_thread_command_session_rotation_ongoing( { int ret; enum lttng_error_code ret_code; - struct notification_thread_command cmd; + struct notification_thread_command cmd = {}; init_notification_thread_command(&cmd); @@ -191,7 +246,7 @@ enum lttng_error_code notification_thread_command_session_rotation_completed( { int ret; enum lttng_error_code ret_code; - struct notification_thread_command cmd; + struct notification_thread_command cmd = {}; init_notification_thread_command(&cmd); @@ -213,11 +268,115 @@ end: return ret_code; } +enum lttng_error_code notification_thread_command_add_application( + struct notification_thread_handle *handle, + struct lttng_pipe *pipe) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION; + cmd.parameters.application.read_side_trigger_event_application_pipe = lttng_pipe_get_readfd(pipe); + + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + ret_code = cmd.reply_code; +end: + return ret_code; +} + +enum lttng_error_code notification_thread_command_remove_application( + struct notification_thread_handle *handle, + struct lttng_pipe *pipe) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION; + cmd.parameters.application.read_side_trigger_event_application_pipe = lttng_pipe_get_readfd(pipe); + + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + ret_code = cmd.reply_code; +end: + return ret_code; +} + +enum lttng_error_code notification_thread_command_get_tokens( + struct notification_thread_handle *handle, + struct lttng_triggers **tokens_triggers) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + assert(handle); + assert(tokens_triggers); + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_GET_TOKENS; + + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + ret_code = cmd.reply_code; + *tokens_triggers = cmd.reply.get_tokens.triggers; + +end: + return ret_code; +} + +enum lttng_error_code notification_thread_command_list_triggers( + struct notification_thread_handle *handle, + uid_t uid, + gid_t gid, + struct lttng_triggers **triggers) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + assert(handle); + assert(triggers); + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS; + cmd.parameters.list_triggers.uid = uid; + cmd.parameters.list_triggers.gid = gid; + + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + ret_code = cmd.reply_code; + *triggers = cmd.reply.list_triggers.triggers; + +end: + return ret_code; +} + void notification_thread_command_quit( struct notification_thread_handle *handle) { int ret; - struct notification_thread_command cmd; + struct notification_thread_command cmd = {}; init_notification_thread_command(&cmd); diff --git a/src/bin/lttng-sessiond/notification-thread-commands.h b/src/bin/lttng-sessiond/notification-thread-commands.h index a90d1ac2b..f0e857fae 100644 --- a/src/bin/lttng-sessiond/notification-thread-commands.h +++ b/src/bin/lttng-sessiond/notification-thread-commands.h @@ -15,6 +15,7 @@ #include "notification-thread-internal.h" #include "notification-thread-events.h" #include +#include struct notification_thread_data; struct lttng_trigger; @@ -26,6 +27,10 @@ enum notification_thread_command_type { NOTIFICATION_COMMAND_TYPE_REMOVE_CHANNEL, NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING, NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_COMPLETED, + NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION, + NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION, + NOTIFICATION_COMMAND_TYPE_GET_TOKENS, + NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS, NOTIFICATION_COMMAND_TYPE_QUIT, }; @@ -62,11 +67,32 @@ struct notification_thread_command { uint64_t trace_archive_chunk_id; struct lttng_trace_archive_location *location; } session_rotation; + /* Add/Remove application */ + struct { + int read_side_trigger_event_application_pipe; + } application; + /* List triggers */ + struct { + /* Credential */ + uid_t uid; + gid_t gid; + } list_triggers; + } parameters; + union { + struct { + struct lttng_triggers *triggers; + } get_tokens; + struct { + struct lttng_triggers *triggers; + } list_triggers; + } reply; + /* lttng_waiter on which to wait for command reply (optional). */ struct lttng_waiter reply_waiter; enum lttng_error_code reply_code; + bool is_async; }; enum lttng_error_code notification_thread_command_register_trigger( @@ -99,6 +125,28 @@ enum lttng_error_code notification_thread_command_session_rotation_completed( uint64_t trace_archive_chunk_id, struct lttng_trace_archive_location *location); +enum lttng_error_code notification_thread_command_add_application( + struct notification_thread_handle *handle, + struct lttng_pipe *trigger_event_application_pipe); + +enum lttng_error_code notification_thread_command_remove_application( + struct notification_thread_handle *handle, + struct lttng_pipe *trigger_event_application_pipe); + +/* Must hold the notification_trigger_tokens_ht_lock to protect against + * insertion removal of triggers TODO: is it the case even with refcounting? */ +/* todo find a better way....*/ +enum lttng_error_code notification_thread_command_get_tokens( + struct notification_thread_handle *handle, + struct lttng_triggers **triggers); + +/* TODO: for now we borrow with no refcount the trigger. THIS IS DANGEROUS */ +enum lttng_error_code notification_thread_command_list_triggers( + struct notification_thread_handle *handle, + uid_t uid, + gid_t gid, + struct lttng_triggers **triggers); + void notification_thread_command_quit( struct notification_thread_handle *handle); diff --git a/src/bin/lttng-sessiond/notification-thread-events.c b/src/bin/lttng-sessiond/notification-thread-events.c index 73ec72f89..e3c35f3a5 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.c +++ b/src/bin/lttng-sessiond/notification-thread-events.c @@ -19,12 +19,15 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include #include #include @@ -50,7 +53,7 @@ enum lttng_object_type { struct lttng_trigger_list_element { /* No ownership of the trigger object is assumed. */ - const struct lttng_trigger *trigger; + struct lttng_trigger *trigger; struct cds_list_head node; }; @@ -108,6 +111,7 @@ struct lttng_session_trigger_list { struct lttng_trigger_ht_element { struct lttng_trigger *trigger; struct cds_lfht_node node; + struct cds_lfht_node node_by_name; /* call_rcu delayed reclaim. */ struct rcu_head rcu_node; }; @@ -117,85 +121,17 @@ struct lttng_condition_list_element { struct cds_list_head node; }; -struct notification_client_list_element { - struct notification_client *client; - struct cds_list_head node; -}; - -struct notification_client_list { - const struct lttng_trigger *trigger; - struct cds_list_head list; - struct cds_lfht_node notification_trigger_ht_node; - /* call_rcu delayed reclaim. */ - struct rcu_head rcu_node; -}; - -struct notification_client { - int socket; - /* Client protocol version. */ - uint8_t major, minor; - uid_t uid; - gid_t gid; - /* - * Indicates if the credentials and versions of the client have been - * checked. - */ - bool validated; - /* - * Conditions to which the client's notification channel is subscribed. - * List of struct lttng_condition_list_node. The condition member is - * owned by the client. - */ - struct cds_list_head condition_list; - struct cds_lfht_node client_socket_ht_node; - struct { - struct { - /* - * During the reception of a message, the reception - * buffers' "size" is set to contain the current - * message's complete payload. - */ - struct lttng_dynamic_buffer buffer; - /* Bytes left to receive for the current message. */ - size_t bytes_to_receive; - /* Type of the message being received. */ - enum lttng_notification_channel_message_type msg_type; - /* - * Indicates whether or not credentials are expected - * from the client. - */ - bool expect_creds; - /* - * Indicates whether or not credentials were received - * from the client. - */ - bool creds_received; - /* Only used during credentials reception. */ - lttng_sock_cred creds; - } inbound; - struct { - /* - * Indicates whether or not a notification addressed to - * this client was dropped because a command reply was - * already buffered. - * - * A notification is dropped whenever the buffer is not - * empty. - */ - bool dropped_notification; - /* - * Indicates whether or not a command reply is already - * buffered. In this case, it means that the client is - * not consuming command replies before emitting a new - * one. This could be caused by a protocol error or a - * misbehaving/malicious client. - */ - bool queued_command_reply; - struct lttng_dynamic_buffer buffer; - } outbound; - } communication; - /* call_rcu delayed reclaim. */ - struct rcu_head rcu_node; +/* + * Facilities to carry the different notifications type in the action processing + * code path. + */ +struct lttng_trigger_notification { + union { + struct lttng_ust_trigger_notification *ust; + uint64_t *kernel; + } u; + uint64_t id; + enum lttng_domain_type type; }; struct channel_state_sample { @@ -258,20 +194,29 @@ void lttng_session_trigger_list_destroy( struct lttng_session_trigger_list *list); static int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list, - const struct lttng_trigger *trigger); + struct lttng_trigger *trigger); static -int match_client(struct cds_lfht_node *node, const void *key) +int match_client_socket(struct cds_lfht_node *node, const void *key) { /* This double-cast is intended to supress pointer-to-cast warning. */ - int socket = (int) (intptr_t) key; - struct notification_client *client; + const int socket = (int) (intptr_t) key; + const struct notification_client *client = caa_container_of(node, + struct notification_client, client_socket_ht_node); - client = caa_container_of(node, struct notification_client, - client_socket_ht_node); + return client->socket == socket; +} - return !!(client->socket == socket); +static +int match_client_id(struct cds_lfht_node *node, const void *key) +{ + /* This double-cast is intended to supress pointer-to-cast warning. */ + const notification_client_id id = *((notification_client_id *) key); + const struct notification_client *client = caa_container_of( + node, struct notification_client, client_id_ht_node); + + return client->id == id; } static @@ -326,18 +271,42 @@ int match_channel_info(struct cds_lfht_node *node, const void *key) } static -int match_condition(struct cds_lfht_node *node, const void *key) +int match_trigger(struct cds_lfht_node *node, const void *key) { - struct lttng_condition *condition_key = (struct lttng_condition *) key; - struct lttng_trigger_ht_element *trigger; - struct lttng_condition *condition; + bool match = false; + struct lttng_trigger *trigger_key = (struct lttng_trigger *) key; + struct lttng_trigger_ht_element *trigger_ht_element; + const struct lttng_credentials *creds_key; + const struct lttng_credentials *creds_node; - trigger = caa_container_of(node, struct lttng_trigger_ht_element, + trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element, node); - condition = lttng_trigger_get_condition(trigger->trigger); - assert(condition); - return !!lttng_condition_is_equal(condition_key, condition); + match = lttng_trigger_is_equal(trigger_key, trigger_ht_element->trigger); + if (!match) { + goto end; + } + + /* Validate credential */ + /* TODO: this could be moved to lttng_trigger_equal depending on how we + * handle root behaviour on disable and listing. + */ + creds_key = lttng_trigger_get_credentials(trigger_key); + creds_node = lttng_trigger_get_credentials(trigger_ht_element->trigger); + match = lttng_credentials_is_equal(creds_key, creds_node); +end: + return !!match; +} + +static +int match_trigger_token(struct cds_lfht_node *node, const void *key) +{ + const uint64_t *_key = key; + struct notification_trigger_tokens_ht_element *element; + + element = caa_container_of(node, struct notification_trigger_tokens_ht_element, + node); + return *_key == element->token ; } static @@ -350,7 +319,7 @@ int match_client_list_condition(struct cds_lfht_node *node, const void *key) assert(condition_key); client_list = caa_container_of(node, struct notification_client_list, - notification_trigger_ht_node); + notification_trigger_clients_ht_node); condition = lttng_trigger_get_const_condition(client_list->trigger); return !!lttng_condition_is_equal(condition_key, condition); @@ -366,6 +335,23 @@ int match_session(struct cds_lfht_node *node, const void *key) return !strcmp(session_info->name, name); } +/* + * Match function for string node. + */ +static int match_str(struct cds_lfht_node *node, const void *key) +{ + struct lttng_trigger_ht_element *trigger_ht_element; + const char *name; + + trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element, + node_by_name); + + /* TODO error checking */ + lttng_trigger_get_name(trigger_ht_element->trigger, &name); + + return hash_match_key_str(name, (void *) key); +} + static unsigned long lttng_condition_buffer_usage_hash( const struct lttng_condition *_condition) @@ -442,6 +428,22 @@ unsigned long lttng_condition_session_rotation_hash( return hash; } +static +unsigned long lttng_condition_event_rule_hash( + const struct lttng_condition *_condition) +{ + unsigned long hash, condition_type; + struct lttng_condition_event_rule *condition; + + condition = container_of(_condition, + struct lttng_condition_event_rule, parent); + condition_type = (unsigned long) condition->parent.type; + hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); + + /* TODO: further hasg using the event rule? on pattern maybe?*/ + return hash; +} + /* * The lttng_condition hashing code is kept in this file (rather than * condition.c) since it makes use of GPLv2 code (hashtable utils), which we @@ -459,6 +461,8 @@ unsigned long lttng_condition_hash(const struct lttng_condition *condition) case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: return lttng_condition_session_rotation_hash(condition); + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + return lttng_condition_event_rule_hash(condition); default: ERR("[notification-thread] Unexpected condition type caught"); abort(); @@ -475,6 +479,18 @@ unsigned long hash_channel_key(struct channel_key *key) return key_hash ^ domain_hash; } +static +unsigned long hash_client_socket(int socket) +{ + return hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed); +} + +static +unsigned long hash_client_id(notification_client_id id) +{ + return hash_key_u64(&id, lttng_ht_seed); +} + /* * Get the type of object to which a given condition applies. Bindings let * the notification system evaluate a trigger's condition when a given @@ -495,6 +511,8 @@ enum lttng_object_type get_condition_binding_object( case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: return LTTNG_OBJECT_TYPE_SESSION; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + return LTTNG_OBJECT_TYPE_NONE; default: return LTTNG_OBJECT_TYPE_UNKNOWN; } @@ -666,7 +684,90 @@ error: return NULL; } -/* RCU read lock must be held by the caller. */ +LTTNG_HIDDEN +bool notification_client_list_get(struct notification_client_list *list) +{ + return urcu_ref_get_unless_zero(&list->ref); +} + +static +void free_notification_client_list_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct notification_client_list, + rcu_node)); +} + +static +void notification_client_list_release(struct urcu_ref *list_ref) +{ + struct notification_client_list *list = + container_of(list_ref, typeof(*list), ref); + struct notification_client_list_element *client_list_element, *tmp; + + if (list->notification_trigger_clients_ht) { + rcu_read_lock(); + cds_lfht_del(list->notification_trigger_clients_ht, + &list->notification_trigger_clients_ht_node); + rcu_read_unlock(); + list->notification_trigger_clients_ht = NULL; + } + cds_list_for_each_entry_safe(client_list_element, tmp, + &list->list, node) { + free(client_list_element); + } + pthread_mutex_destroy(&list->lock); + call_rcu(&list->rcu_node, free_notification_client_list_rcu); +} + +static +struct notification_client_list *notification_client_list_create( + const struct lttng_trigger *trigger) +{ + struct notification_client_list *client_list = + zmalloc(sizeof(*client_list)); + + if (!client_list) { + goto error; + } + pthread_mutex_init(&client_list->lock, NULL); + urcu_ref_init(&client_list->ref); + cds_lfht_node_init(&client_list->notification_trigger_clients_ht_node); + CDS_INIT_LIST_HEAD(&client_list->list); + client_list->trigger = trigger; +error: + return client_list; +} + +static +void publish_notification_client_list( + struct notification_thread_state *state, + struct notification_client_list *list) +{ + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(list->trigger); + + assert(!list->notification_trigger_clients_ht); + + list->notification_trigger_clients_ht = + state->notification_trigger_clients_ht; + + rcu_read_lock(); + cds_lfht_add(state->notification_trigger_clients_ht, + lttng_condition_hash(condition), + &list->notification_trigger_clients_ht_node); + rcu_read_unlock(); +} + +LTTNG_HIDDEN +void notification_client_list_put(struct notification_client_list *list) +{ + if (!list) { + return; + } + return urcu_ref_put(&list->ref, notification_client_list_release); +} + +/* Provides a reference to the returned list. */ static struct notification_client_list *get_client_list_from_condition( struct notification_thread_state *state, @@ -674,20 +775,24 @@ struct notification_client_list *get_client_list_from_condition( { struct cds_lfht_node *node; struct cds_lfht_iter iter; + struct notification_client_list *list = NULL; + rcu_read_lock(); cds_lfht_lookup(state->notification_trigger_clients_ht, lttng_condition_hash(condition), match_client_list_condition, condition, &iter); node = cds_lfht_iter_get_node(&iter); - - return node ? caa_container_of(node, - struct notification_client_list, - notification_trigger_ht_node) : NULL; + if (node) { + list = container_of(node, struct notification_client_list, + notification_trigger_clients_ht_node); + list = notification_client_list_get(list) ? list : NULL; + } + rcu_read_unlock(); + return list; } -/* This function must be called with the RCU read lock held. */ static int evaluate_channel_condition_for_client( const struct lttng_condition *condition, @@ -703,6 +808,8 @@ int evaluate_channel_condition_for_client( struct channel_state_sample *last_sample = NULL; struct lttng_channel_trigger_list *channel_trigger_list = NULL; + rcu_read_lock(); + /* Find the channel associated with the condition. */ cds_lfht_for_each_entry(state->channel_triggers_ht, &iter, channel_trigger_list, channel_triggers_ht_node) { @@ -777,6 +884,7 @@ int evaluate_channel_condition_for_client( *session_uid = channel_info->session_info->uid; *session_gid = channel_info->session_info->gid; end: + rcu_read_unlock(); return ret; } @@ -812,7 +920,6 @@ end: return session_name; } -/* This function must be called with the RCU read lock held. */ static int evaluate_session_condition_for_client( const struct lttng_condition *condition, @@ -826,6 +933,7 @@ int evaluate_session_condition_for_client( const char *session_name; struct session_info *session_info = NULL; + rcu_read_lock(); session_name = get_condition_session_name(condition); /* Find the session associated with the trigger. */ @@ -879,10 +987,10 @@ int evaluate_session_condition_for_client( end_session_put: session_info_put(session_info); end: + rcu_read_unlock(); return ret; } -/* This function must be called with the RCU read lock held. */ static int evaluate_condition_for_client(const struct lttng_trigger *trigger, const struct lttng_condition *condition, @@ -891,7 +999,9 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger, { int ret; struct lttng_evaluation *evaluation = NULL; - struct notification_client_list client_list = { 0 }; + struct notification_client_list client_list = { + .lock = PTHREAD_MUTEX_INITIALIZER, + }; struct notification_client_list_element client_list_element = { 0 }; uid_t object_uid = 0; gid_t object_gid = 0; @@ -911,6 +1021,7 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger, &evaluation, &object_uid, &object_gid); break; case LTTNG_OBJECT_TYPE_NONE: + DBG("[notification-thread] Newly subscribed-to condition not binded to object, nothing to evaluate"); ret = 0; goto end; case LTTNG_OBJECT_TYPE_UNKNOWN: @@ -933,7 +1044,7 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger, * Create a temporary client list with the client currently * subscribing. */ - cds_lfht_node_init(&client_list.notification_trigger_ht_node); + cds_lfht_node_init(&client_list.notification_trigger_clients_ht_node); CDS_INIT_LIST_HEAD(&client_list.list); client_list.trigger = trigger; @@ -957,7 +1068,7 @@ int notification_thread_client_subscribe(struct notification_client *client, enum lttng_notification_channel_status *_status) { int ret = 0; - struct notification_client_list *client_list; + struct notification_client_list *client_list = NULL; struct lttng_condition_list_element *condition_list_element = NULL; struct notification_client_list_element *client_list_element = NULL; enum lttng_notification_channel_status status = @@ -986,8 +1097,6 @@ int notification_thread_client_subscribe(struct notification_client *client, goto error; } - rcu_read_lock(); - /* * Add the newly-subscribed condition to the client's subscription list. */ @@ -1003,20 +1112,24 @@ int notification_thread_client_subscribe(struct notification_client *client, * since this trigger is not registered yet. */ free(client_list_element); - goto end_unlock; + goto end; } /* * The condition to which the client just subscribed is evaluated * at this point so that conditions that are already TRUE result * in a notification being sent out. + * + * The client_list's trigger is used without locking the list itself. + * This is correct since the list doesn't own the trigger and the + * object is immutable. */ if (evaluate_condition_for_client(client_list->trigger, condition, client, state)) { WARN("[notification-thread] Evaluation of a condition on client subscription failed, aborting."); ret = -1; free(client_list_element); - goto end_unlock; + goto end; } /* @@ -1026,13 +1139,17 @@ int notification_thread_client_subscribe(struct notification_client *client, */ client_list_element->client = client; CDS_INIT_LIST_HEAD(&client_list_element->node); + + pthread_mutex_lock(&client_list->lock); cds_list_add(&client_list_element->node, &client_list->list); -end_unlock: - rcu_read_unlock(); + pthread_mutex_unlock(&client_list->lock); end: if (_status) { *_status = status; } + if (client_list) { + notification_client_list_put(client_list); + } return ret; error: free(condition_list_element); @@ -1088,23 +1205,24 @@ int notification_thread_client_unsubscribe( * Remove the client from the list of clients interested the trigger * matching the condition. */ - rcu_read_lock(); client_list = get_client_list_from_condition(state, condition); if (!client_list) { - goto end_unlock; + goto end; } + pthread_mutex_lock(&client_list->lock); cds_list_for_each_entry_safe(client_list_element, client_tmp, &client_list->list, node) { - if (client_list_element->client->socket != client->socket) { + if (client_list_element->client->id != client->id) { continue; } cds_list_del(&client_list_element->node); free(client_list_element); break; } -end_unlock: - rcu_read_unlock(); + pthread_mutex_unlock(&client_list->lock); + notification_client_list_put(client_list); + client_list = NULL; end: lttng_condition_destroy(condition); if (_status) { @@ -1123,24 +1241,22 @@ static void notification_client_destroy(struct notification_client *client, struct notification_thread_state *state) { - struct lttng_condition_list_element *condition_list_element, *tmp; - if (!client) { return; } - /* Release all conditions to which the client was subscribed. */ - cds_list_for_each_entry_safe(condition_list_element, tmp, - &client->condition_list, node) { - (void) notification_thread_client_unsubscribe(client, - condition_list_element->condition, state, NULL); - } - + /* + * The client object is not reachable by other threads, no need to lock + * the client here. + */ if (client->socket >= 0) { (void) lttcomm_close_unix_sock(client->socket); + client->socket = -1; } + client->communication.active = false; lttng_dynamic_buffer_reset(&client->communication.inbound.buffer); lttng_dynamic_buffer_reset(&client->communication.outbound.buffer); + pthread_mutex_destroy(&client->lock); call_rcu(&client->rcu_node, free_notification_client_rcu); } @@ -1157,8 +1273,8 @@ struct notification_client *get_client_from_socket(int socket, struct notification_client *client = NULL; cds_lfht_lookup(state->client_socket_ht, - hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed), - match_client, + hash_client_socket(socket), + match_client_socket, (void *) (unsigned long) socket, &iter); node = cds_lfht_iter_get_node(&iter); @@ -1172,6 +1288,34 @@ end: return client; } +/* + * Call with rcu_read_lock held (and hold for the lifetime of the returned + * client pointer). + */ +static +struct notification_client *get_client_from_id(notification_client_id id, + struct notification_thread_state *state) +{ + struct cds_lfht_iter iter; + struct cds_lfht_node *node; + struct notification_client *client = NULL; + + cds_lfht_lookup(state->client_id_ht, + hash_client_id(id), + match_client_id, + &id, + &iter); + node = cds_lfht_iter_get_node(&iter); + if (!node) { + goto end; + } + + client = caa_container_of(node, struct notification_client, + client_id_ht_node); +end: + return client; +} + static bool buffer_usage_condition_applies_to_channel( const struct lttng_condition *condition, @@ -1375,7 +1519,7 @@ void lttng_session_trigger_list_destroy(struct lttng_session_trigger_list *list) static int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list, - const struct lttng_trigger *trigger) + struct lttng_trigger *trigger) { int ret = 0; struct lttng_trigger_list_element *new_element = @@ -1762,6 +1906,7 @@ int handle_notification_thread_command_session_rotation( struct notification_client_list *client_list; struct lttng_evaluation *evaluation = NULL; enum lttng_condition_type condition_type; + bool client_list_is_empty; trigger = trigger_list_element->trigger; condition = lttng_trigger_get_const_condition(trigger); @@ -1785,7 +1930,10 @@ int handle_notification_thread_command_session_rotation( client_list = get_client_list_from_condition(state, condition); assert(client_list); - if (cds_list_empty(&client_list->list)) { + pthread_mutex_lock(&client_list->lock); + client_list_is_empty = cds_list_empty(&client_list->list); + pthread_mutex_unlock(&client_list->lock); + if (client_list_is_empty) { /* * No clients interested in the evaluation's result, * skip it. @@ -1805,7 +1953,7 @@ int handle_notification_thread_command_session_rotation( /* Internal error */ ret = -1; cmd_result = LTTNG_ERR_UNK; - goto end; + goto put_list; } /* Dispatch evaluation result to all clients. */ @@ -1814,8 +1962,10 @@ int handle_notification_thread_command_session_rotation( session_info->uid, session_info->gid); lttng_evaluation_destroy(evaluation); +put_list: + notification_client_list_put(client_list); if (caa_unlikely(ret)) { - goto end; + break; } } end: @@ -1826,59 +1976,294 @@ end: } static -int condition_is_supported(struct lttng_condition *condition) +int handle_notification_thread_command_add_application( + struct notification_thread_handle *handle, + struct notification_thread_state *state, + int read_side_trigger_event_application_pipe, + enum lttng_error_code *_cmd_result) { - int ret; + int ret = 0; + enum lttng_error_code cmd_result = LTTNG_OK; + struct notification_event_trigger_source_element *element = NULL; - switch (lttng_condition_get_type(condition)) { - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - { - enum lttng_domain_type domain; + element = zmalloc(sizeof(*element)); + if (!element) { + cmd_result = LTTNG_ERR_NOMEM; + ret = -1; + goto end; + } - ret = lttng_condition_buffer_usage_get_domain_type(condition, - &domain); - if (ret) { - ret = -1; - goto end; - } + CDS_INIT_LIST_HEAD(&element->node); + element->fd = read_side_trigger_event_application_pipe; - if (domain != LTTNG_DOMAIN_KERNEL) { - ret = 1; - goto end; - } + pthread_mutex_lock(&handle->event_trigger_sources.lock); + cds_list_add(&element->node, &handle->event_trigger_sources.list); + pthread_mutex_unlock(&handle->event_trigger_sources.lock); - /* - * Older kernel tracers don't expose the API to monitor their - * buffers. Therefore, we reject triggers that require that - * mechanism to be available to be evaluated. - */ - ret = kernel_supports_ring_buffer_snapshot_sample_positions(); - break; - } - default: - ret = 1; + /* TODO: remove on failure to add to list? */ + + /* Adding the read side pipe to the event poll */ + ret = lttng_poll_add(&state->events, + read_side_trigger_event_application_pipe, + LPOLLIN | LPOLLERR); + + DBG3("[notification-thread] Adding application event source from fd: %d", read_side_trigger_event_application_pipe); + if (ret < 0) { + /* TODO: what should be the value of cmd_result??? */ + ERR("[notification-thread] Failed to add event source pipe fd to pollset"); + goto end; } + end: + *_cmd_result = cmd_result; return ret; } -/* Must be called with RCU read lock held. */ static -int bind_trigger_to_matching_session(const struct lttng_trigger *trigger, - struct notification_thread_state *state) +int handle_notification_thread_command_remove_application( + struct notification_thread_handle *handle, + struct notification_thread_state *state, + int read_side_trigger_event_application_pipe, + enum lttng_error_code *_cmd_result) { int ret = 0; - const struct lttng_condition *condition; - const char *session_name; - struct lttng_session_trigger_list *trigger_list; + enum lttng_error_code cmd_result = LTTNG_OK; - condition = lttng_trigger_get_const_condition(trigger); - switch (lttng_condition_get_type(condition)) { - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - { - enum lttng_condition_status status; + /* TODO: missing a lock propably to revisit */ + struct notification_event_trigger_source_element *source_element, *tmp; + cds_list_for_each_entry_safe(source_element, tmp, + &handle->event_trigger_sources.list, node) { + if (source_element->fd != read_side_trigger_event_application_pipe) { + continue; + } + + DBG("[notification-thread] Removed event source from event source list"); + cds_list_del(&source_element->node); + break; + } + + DBG3("[notification-thread] Removing application event source from fd: %d", read_side_trigger_event_application_pipe); + /* Removing the read side pipe to the event poll */ + ret = lttng_poll_del(&state->events, + read_side_trigger_event_application_pipe); + if (ret < 0) { + /* TODO: what should be the value of cmd_result??? */ + ERR("[notification-thread] Failed to remove event source pipe fd from pollset"); + goto end; + } + +end: + *_cmd_result = cmd_result; + return ret; +} + +static int handle_notification_thread_command_get_tokens( + struct notification_thread_handle *handle, + struct notification_thread_state *state, + struct lttng_triggers **triggers, + enum lttng_error_code *_cmd_result) +{ + int ret = 0, i = 0; + enum lttng_error_code cmd_result = LTTNG_OK; + struct cds_lfht_iter iter; + struct notification_trigger_tokens_ht_element *element; + struct lttng_triggers *local_triggers = NULL; + + local_triggers = lttng_triggers_create(); + if (!local_triggers) { + cmd_result = LTTNG_ERR_NOMEM; + goto end; + } + + rcu_read_lock(); + cds_lfht_for_each_entry ( + state->trigger_tokens_ht, &iter, element, node) { + ret = lttng_triggers_add(local_triggers, element->trigger); + if (ret < 0) { + cmd_result = LTTNG_ERR_FATAL; + ret = -1; + goto end; + } + + /* Ownership is shared with the lttng_triggers object */ + lttng_trigger_get(element->trigger); + + i++; + } + + /* Passing ownership up */ + *triggers = local_triggers; + local_triggers = NULL; + +end: + rcu_read_unlock(); + lttng_triggers_destroy(local_triggers); + *_cmd_result = cmd_result; + return ret; +} + +static +int handle_notification_thread_command_list_triggers( + struct notification_thread_handle *handle, + struct notification_thread_state *state, + uid_t uid, + gid_t gid, + struct lttng_triggers **triggers, + enum lttng_error_code *_cmd_result) +{ + int ret = 0, i = 0; + enum lttng_error_code cmd_result = LTTNG_OK; + struct cds_lfht_iter iter; + struct lttng_trigger_ht_element *trigger_ht_element; + struct lttng_triggers *local_triggers = NULL; + const struct lttng_credentials *creds; + + long scb, sca; + unsigned long count; + + rcu_read_lock(); + cds_lfht_count_nodes(state->triggers_ht, &scb, &count, &sca); + + /* TODO check downcasting */ + local_triggers = lttng_triggers_create(); + if (!local_triggers) { + cmd_result = LTTNG_ERR_NOMEM; + goto end; + } + + cds_lfht_for_each_entry (state->triggers_ht, &iter, + trigger_ht_element, node) { + /* Only return the trigger for which the requestion client have + * access. For now the root user can only list its own + * triggers. + * TODO: root user behavior + */ + creds = lttng_trigger_get_credentials(trigger_ht_element->trigger); + if ((uid != creds->uid) || (gid != creds->gid)) { + continue; + } + + ret = lttng_triggers_add(local_triggers, trigger_ht_element->trigger); + if (ret < 0) { + ret = -1; + goto end; + } + /* Ownership is shared with the lttng_triggers object */ + lttng_trigger_get(trigger_ht_element->trigger); + + i++; + } + + /* Passing ownership up */ + *triggers = local_triggers; + local_triggers = NULL; + +end: + rcu_read_unlock(); + lttng_triggers_destroy(local_triggers); + *_cmd_result = cmd_result; + return ret; +} + +static +int condition_is_supported(struct lttng_condition *condition) +{ + int ret; + + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + { + enum lttng_domain_type domain; + + ret = lttng_condition_buffer_usage_get_domain_type(condition, + &domain); + if (ret) { + ret = -1; + goto end; + } + + if (domain != LTTNG_DOMAIN_KERNEL) { + ret = 1; + goto end; + } + + /* + * Older kernel tracers don't expose the API to monitor their + * buffers. Therefore, we reject triggers that require that + * mechanism to be available to be evaluated. + */ + ret = kernel_supports_ring_buffer_snapshot_sample_positions(); + break; + } + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + { + /* TODO: + * Check for kernel support. + * Check for ust support ?? + */ + ret = 1; + break; + } + default: + ret = 1; + } +end: + return ret; +} + +static +int action_is_supported(struct lttng_action *action) +{ + int ret; + + switch (lttng_action_get_type(action)) { + case LTTNG_ACTION_TYPE_NOTIFY: + case LTTNG_ACTION_TYPE_START_SESSION: + case LTTNG_ACTION_TYPE_STOP_SESSION: + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + { + /* TODO validate that this is true for kernel in regards to + * rotation and snapshot. Start stop is not a problem notify + * either. + */ + /* For now all type of actions are supported */ + ret = 1; + break; + } + case LTTNG_ACTION_TYPE_GROUP: + { + /* TODO: Iterate over all internal actions and validate that + * they are supported + */ + ret = 1; + break; + + } + default: + ret = 1; + } + + return ret; +} + +/* Must be called with RCU read lock held. */ +static +int bind_trigger_to_matching_session(struct lttng_trigger *trigger, + struct notification_thread_state *state) +{ + int ret = 0; + const struct lttng_condition *condition; + const char *session_name; + struct lttng_session_trigger_list *trigger_list; + + condition = lttng_trigger_get_const_condition(trigger); + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + { + enum lttng_condition_status status; status = lttng_condition_session_rotation_get_session_name( condition, &session_name); @@ -1911,7 +2296,7 @@ end: /* Must be called with RCU read lock held. */ static -int bind_trigger_to_matching_channels(const struct lttng_trigger *trigger, +int bind_trigger_to_matching_channels(struct lttng_trigger *trigger, struct notification_thread_state *state) { int ret = 0; @@ -1955,95 +2340,26 @@ end: return ret; } -/* - * FIXME A client's credentials are not checked when registering a trigger, nor - * are they stored alongside with the trigger. - * - * The effects of this are benign since: - * - The client will succeed in registering the trigger, as it is valid, - * - The trigger will, internally, be bound to the channel/session, - * - The notifications will not be sent since the client's credentials - * are checked against the channel at that moment. - * - * If this function returns a non-zero value, it means something is - * fundamentally broken and the whole subsystem/thread will be torn down. - * - * If a non-fatal error occurs, just set the cmd_result to the appropriate - * error code. - */ -static -int handle_notification_thread_command_register_trigger( +static int action_notify_register_trigger( struct notification_thread_state *state, - struct lttng_trigger *trigger, - enum lttng_error_code *cmd_result) + struct lttng_trigger *trigger) { + int ret = 0; struct lttng_condition *condition; struct notification_client *client; struct notification_client_list *client_list = NULL; - struct lttng_trigger_ht_element *trigger_ht_element = NULL; - struct notification_client_list_element *client_list_element, *tmp; - struct cds_lfht_node *node; struct cds_lfht_iter iter; - bool free_trigger = true; - - rcu_read_lock(); + struct notification_client_list_element *client_list_element, *tmp; condition = lttng_trigger_get_condition(trigger); assert(condition); - ret = condition_is_supported(condition); - if (ret < 0) { - goto error; - } else if (ret == 0) { - *cmd_result = LTTNG_ERR_NOT_SUPPORTED; - goto error; - } else { - /* Feature is supported, continue. */ - ret = 0; - } - - trigger_ht_element = zmalloc(sizeof(*trigger_ht_element)); - if (!trigger_ht_element) { - ret = -1; - goto error; - } - - /* Add trigger to the trigger_ht. */ - cds_lfht_node_init(&trigger_ht_element->node); - trigger_ht_element->trigger = trigger; - - node = cds_lfht_add_unique(state->triggers_ht, - lttng_condition_hash(condition), - match_condition, - condition, - &trigger_ht_element->node); - if (node != &trigger_ht_element->node) { - /* Not a fatal error, simply report it to the client. */ - *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; - goto error_free_ht_element; - } - - /* - * Ownership of the trigger and of its wrapper was transfered to - * the triggers_ht. - */ - trigger_ht_element = NULL; - free_trigger = false; - - /* - * The rest only applies to triggers that have a "notify" action. - * It is not skipped as this is the only action type currently - * supported. - */ - client_list = zmalloc(sizeof(*client_list)); + client_list = notification_client_list_create(trigger); if (!client_list) { ret = -1; - goto error_free_ht_element; + goto end; } - cds_lfht_node_init(&client_list->notification_trigger_ht_node); - CDS_INIT_LIST_HEAD(&client_list->list); - client_list->trigger = trigger; /* Build a list of clients to which this new trigger applies. */ cds_lfht_for_each_entry(state->client_socket_ht, &iter, client, @@ -2055,23 +2371,19 @@ int handle_notification_thread_command_register_trigger( client_list_element = zmalloc(sizeof(*client_list_element)); if (!client_list_element) { ret = -1; - goto error_free_client_list; + goto error_put_client_list; } CDS_INIT_LIST_HEAD(&client_list_element->node); client_list_element->client = client; cds_list_add(&client_list_element->node, &client_list->list); } - cds_lfht_add(state->notification_trigger_clients_ht, - lttng_condition_hash(condition), - &client_list->notification_trigger_ht_node); - switch (get_condition_binding_object(condition)) { case LTTNG_OBJECT_TYPE_SESSION: /* Add the trigger to the list if it matches a known session. */ ret = bind_trigger_to_matching_session(trigger, state); if (ret) { - goto error_free_client_list; + goto error_put_client_list; } break; case LTTNG_OBJECT_TYPE_CHANNEL: @@ -2081,7 +2393,7 @@ int handle_notification_thread_command_register_trigger( */ ret = bind_trigger_to_matching_channels(trigger, state); if (ret) { - goto error_free_client_list; + goto error_put_client_list; } break; case LTTNG_OBJECT_TYPE_NONE: @@ -2089,7 +2401,7 @@ int handle_notification_thread_command_register_trigger( default: ERR("[notification-thread] Unknown object type on which to bind a newly registered trigger was encountered"); ret = -1; - goto error_free_client_list; + goto error_put_client_list; } /* @@ -2117,13 +2429,15 @@ int handle_notification_thread_command_register_trigger( * current state. Otherwise, the next evaluation cycle may only see * that the evaluations remain the same (true for samples n-1 and n) and * the client will never know that the condition has been met. + * + * No need to lock the list here as it has not been published yet. */ cds_list_for_each_entry_safe(client_list_element, tmp, &client_list->list, node) { ret = evaluate_condition_for_client(trigger, condition, client_list_element->client, state); if (ret) { - goto error_free_client_list; + goto error_put_client_list; } } @@ -2131,25 +2445,252 @@ int handle_notification_thread_command_register_trigger( * Client list ownership transferred to the * notification_trigger_clients_ht. */ + publish_notification_client_list(state, client_list); client_list = NULL; +error_put_client_list: + notification_client_list_put(client_list); +end: + return ret; +} - *cmd_result = LTTNG_OK; -error_free_client_list: - if (client_list) { - cds_list_for_each_entry_safe(client_list_element, tmp, - &client_list->list, node) { - free(client_list_element); +static +bool trigger_name_taken(struct notification_thread_state *state, const char *name) +{ + struct cds_lfht_node *triggers_by_name_ht_node; + struct cds_lfht_iter iter; + /* TODO change hashing for trigger */ + cds_lfht_lookup(state->triggers_by_name_ht, + hash_key_str(name, lttng_ht_seed), + match_str, + name, + &iter); + triggers_by_name_ht_node = cds_lfht_iter_get_node(&iter); + if (triggers_by_name_ht_node) { + return true; + } else { + return false; + } + +} +static +void generate_trigger_name(struct notification_thread_state *state, struct lttng_trigger *trigger, const char **name) +{ + /* Here the offset criteria guarantee an end. This will be a nice + * bikeshedding conversation. I would simply generate uuid and use them + * as trigger name. + */ + bool taken = false; + do { + lttng_trigger_generate_name(trigger, state->trigger_id.name_offset); + /* TODO error checking */ + lttng_trigger_get_name(trigger, name); + taken = trigger_name_taken(state, *name); + if (taken) { + state->trigger_id.name_offset++; + } + } while (taken || state->trigger_id.name_offset == UINT32_MAX); +} + +static bool action_is_notify(const struct lttng_action *action) +{ + /* TODO for action groups we need to iterate over all of them */ + enum lttng_action_type type = lttng_action_get_type_const(action); + bool ret = false; + enum lttng_action_status status; + const struct lttng_action *tmp; + unsigned int i, count; + + switch (type) { + case LTTNG_ACTION_TYPE_NOTIFY: + ret = true; + break; + case LTTNG_ACTION_TYPE_GROUP: + status = lttng_action_group_get_count(action, &count); + if (status != LTTNG_ACTION_STATUS_OK) { + assert(0); + } + for (i = 0; i < count; i++) { + tmp = lttng_action_group_get_at_index_const(action, i); + assert(tmp); + ret = action_is_notify(tmp); + if (ret) { + break; + } + } + break; + default: + ret = false; + break; + } + + return ret; +} + +/* + * TODO: REVIEW THIS COMMENT. + * FIXME A client's credentials are not checked when registering a trigger, nor + * are they stored alongside with the trigger. + * + * The effects of this are benign since: + * - The client will succeed in registering the trigger, as it is valid, + * - The trigger will, internally, be bound to the channel/session, + * - The notifications will not be sent since the client's credentials + * are checked against the channel at that moment. + * + * If this function returns a non-zero value, it means something is + * fundamentally broken and the whole subsystem/thread will be torn down. + * + * If a non-fatal error occurs, just set the cmd_result to the appropriate + * error code. + */ +static +int handle_notification_thread_command_register_trigger( + struct notification_thread_state *state, + struct lttng_trigger *trigger, + enum lttng_error_code *cmd_result) +{ + int ret = 0; + int is_supported; + struct lttng_condition *condition; + struct lttng_action *action; + struct lttng_trigger_ht_element *trigger_ht_element = NULL; + struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL; + struct cds_lfht_node *node; + const char* trigger_name; + bool free_trigger = true; + + assert(trigger->creds.set); + + rcu_read_lock(); + + /* Set the trigger's key */ + lttng_trigger_set_key(trigger, state->trigger_id.token_generator); + + if (lttng_trigger_get_name(trigger, &trigger_name) == LTTNG_TRIGGER_STATUS_UNSET) { + generate_trigger_name(state, trigger, &trigger_name); + } else if (trigger_name_taken(state, trigger_name)) { + /* Not a fatal error */ + *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; + ret = 0; + goto error; + } + + condition = lttng_trigger_get_condition(trigger); + assert(condition); + + action = lttng_trigger_get_action(trigger); + assert(action); + + is_supported = condition_is_supported(condition); + if (is_supported < 0) { + goto error; + } else if (is_supported == 0) { + ret = 0; + *cmd_result = LTTNG_ERR_NOT_SUPPORTED; + goto error; + } + + is_supported = action_is_supported(action); + if (is_supported < 0) { + goto error; + } else if (is_supported == 0) { + ret = 0; + *cmd_result = LTTNG_ERR_NOT_SUPPORTED; + goto error; + } + + trigger_ht_element = zmalloc(sizeof(*trigger_ht_element)); + if (!trigger_ht_element) { + ret = -1; + goto error; + } + + /* Add trigger to the trigger_ht. */ + cds_lfht_node_init(&trigger_ht_element->node); + cds_lfht_node_init(&trigger_ht_element->node_by_name); + + /* + * This element own the trigger object from now own, this is why there + * is no lttng_trigger_get here. + * This thread is now the owner of the trigger object. + */ + trigger_ht_element->trigger = trigger; + + node = cds_lfht_add_unique(state->triggers_ht, + lttng_condition_hash(condition), + match_trigger, + trigger, + &trigger_ht_element->node); + if (node != &trigger_ht_element->node) { + /* Not a fatal error, simply report it to the client. */ + *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; + goto error_free_ht_element; + } + + node = cds_lfht_add_unique(state->triggers_by_name_ht, + hash_key_str(trigger_name, lttng_ht_seed), + match_str, + trigger_name, + &trigger_ht_element->node_by_name); + if (node != &trigger_ht_element->node_by_name) { + /* This should never happen */ + /* Not a fatal error, simply report it to the client. */ + /* TODO remove from the trigger_ht */ + *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; + goto error_free_ht_element; + } + + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { + trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element)); + if (!trigger_tokens_ht_element) { + ret = -1; + goto error; + } + + /* Add trigger token to the trigger_tokens_ht. */ + cds_lfht_node_init(&trigger_tokens_ht_element->node); + trigger_tokens_ht_element->token = trigger->key.value; + trigger_tokens_ht_element->trigger = trigger; + + node = cds_lfht_add_unique(state->trigger_tokens_ht, + hash_key_u64(&trigger_tokens_ht_element->token, lttng_ht_seed), + match_trigger_token, + &trigger_tokens_ht_element->token, + &trigger_tokens_ht_element->node); + if (node != &trigger_tokens_ht_element->node) { + /* TODO: THIS IS A FATAL ERROR... should never happen */ + /* Not a fatal error, simply report it to the client. */ + *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; + goto error_free_ht_element; + } + } + + /* + * Ownership of the trigger and of its wrapper was transfered to + * the triggers_ht. Same for token ht element if necessary. + */ + trigger_tokens_ht_element = NULL; + trigger_ht_element = NULL; + free_trigger = false; + + if (action_is_notify(action)) { + ret = action_notify_register_trigger(state, trigger); + if (ret < 0) { + /* TODO should cmd_result be set here? */ + ret = -1; + goto error_free_ht_element; } - free(client_list); } + + /* Increment the trigger unique id generator */ + state->trigger_id.token_generator++; + *cmd_result = LTTNG_OK; + error_free_ht_element: free(trigger_ht_element); + free(trigger_tokens_ht_element); error: if (free_trigger) { - struct lttng_action *action = lttng_trigger_get_action(trigger); - - lttng_condition_destroy(condition); - lttng_action_destroy(action); lttng_trigger_destroy(trigger); } rcu_read_unlock(); @@ -2157,16 +2698,16 @@ error: } static -void free_notification_client_list_rcu(struct rcu_head *node) +void free_lttng_trigger_ht_element_rcu(struct rcu_head *node) { - free(caa_container_of(node, struct notification_client_list, + free(caa_container_of(node, struct lttng_trigger_ht_element, rcu_node)); } static -void free_lttng_trigger_ht_element_rcu(struct rcu_head *node) +void free_notification_trigger_tokens_ht_element_rcu(struct rcu_head *node) { - free(caa_container_of(node, struct lttng_trigger_ht_element, + free(caa_container_of(node, struct notification_trigger_tokens_ht_element, rcu_node)); } @@ -2180,19 +2721,22 @@ int handle_notification_thread_command_unregister_trigger( struct cds_lfht_node *triggers_ht_node; struct lttng_channel_trigger_list *trigger_list; struct notification_client_list *client_list; - struct notification_client_list_element *client_list_element, *tmp; struct lttng_trigger_ht_element *trigger_ht_element = NULL; struct lttng_condition *condition = lttng_trigger_get_condition( trigger); - struct lttng_action *action; + struct lttng_action *action = lttng_trigger_get_action(trigger); enum lttng_error_code cmd_reply; rcu_read_lock(); + /* TODO change hashing for trigger */ + /* TODO Disabling for the root user is not complete, for now the root + * user cannot disable the trigger from another user. + */ cds_lfht_lookup(state->triggers_ht, lttng_condition_hash(condition), - match_condition, - condition, + match_trigger, + trigger, &iter); triggers_ht_node = cds_lfht_iter_get_node(&iter); if (!triggers_ht_node) { @@ -2209,13 +2753,7 @@ int handle_notification_thread_command_unregister_trigger( cds_list_for_each_entry_safe(trigger_element, tmp, &trigger_list->list, node) { - const struct lttng_condition *current_condition = - lttng_trigger_get_const_condition( - trigger_element->trigger); - - assert(current_condition); - if (!lttng_condition_is_equal(condition, - current_condition)) { + if (!lttng_trigger_is_equal(trigger, trigger_element->trigger)) { continue; } @@ -2226,30 +2764,45 @@ int handle_notification_thread_command_unregister_trigger( } } - /* - * Remove and release the client list from - * notification_trigger_clients_ht. - */ - client_list = get_client_list_from_condition(state, condition); - assert(client_list); + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { + struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element; + cds_lfht_for_each_entry(state->trigger_tokens_ht, &iter, trigger_tokens_ht_element, + node) { + if (!lttng_trigger_is_equal(trigger, trigger_tokens_ht_element->trigger)) { + continue; + } - cds_list_for_each_entry_safe(client_list_element, tmp, - &client_list->list, node) { - free(client_list_element); + /* TODO talk to all app and remove it */ + DBG("[notification-thread] Removed trigger from tokens_ht"); + cds_lfht_del(state->trigger_tokens_ht, + &trigger_tokens_ht_element->node); + call_rcu(&trigger_tokens_ht_element->rcu_node, free_notification_trigger_tokens_ht_element_rcu); + + break; + } + } + + if (action_is_notify(action)) { + /* + * Remove and release the client list from + * notification_trigger_clients_ht. + */ + client_list = get_client_list_from_condition(state, condition); + assert(client_list); + + /* Put new reference and the hashtable's reference. */ + notification_client_list_put(client_list); + notification_client_list_put(client_list); + client_list = NULL; } - cds_lfht_del(state->notification_trigger_clients_ht, - &client_list->notification_trigger_ht_node); - call_rcu(&client_list->rcu_node, free_notification_client_list_rcu); /* Remove trigger from triggers_ht. */ trigger_ht_element = caa_container_of(triggers_ht_node, struct lttng_trigger_ht_element, node); + cds_lfht_del(state->triggers_by_name_ht, &trigger_ht_element->node_by_name); cds_lfht_del(state->triggers_ht, triggers_ht_node); - condition = lttng_trigger_get_condition(trigger_ht_element->trigger); - lttng_condition_destroy(condition); - action = lttng_trigger_get_action(trigger_ht_element->trigger); - lttng_action_destroy(action); + /* Release the ownership of the trigger */ lttng_trigger_destroy(trigger_ht_element->trigger); call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu); end: @@ -2327,6 +2880,47 @@ int handle_notification_thread_command( cmd->parameters.session_rotation.location, &cmd->reply_code); break; + case NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION: + ret = handle_notification_thread_command_add_application( + handle, + state, + cmd->parameters.application.read_side_trigger_event_application_pipe, + &cmd->reply_code); + break; + case NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION: + ret = handle_notification_thread_command_remove_application( + handle, + state, + cmd->parameters.application.read_side_trigger_event_application_pipe, + &cmd->reply_code); + break; + case NOTIFICATION_COMMAND_TYPE_GET_TOKENS: + { + struct lttng_triggers *triggers = NULL; + ret = handle_notification_thread_command_get_tokens( + handle, state, &triggers, &cmd->reply_code); + cmd->reply.get_tokens.triggers = triggers; + ret = 0; + break; + + cmd->reply_code = LTTNG_OK; + ret = 0; + break; + } + case NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS: + { + struct lttng_triggers *triggers = NULL; + ret = handle_notification_thread_command_list_triggers( + handle, + state, + cmd->parameters.list_triggers.uid, + cmd->parameters.list_triggers.gid, + &triggers, + &cmd->reply_code); + cmd->reply.list_triggers.triggers = triggers; + ret = 0; + break; + } case NOTIFICATION_COMMAND_TYPE_QUIT: DBG("[notification-thread] Received quit command"); cmd->reply_code = LTTNG_OK; @@ -2342,7 +2936,12 @@ int handle_notification_thread_command( } end: cds_list_del(&cmd->cmd_list_node); - lttng_waiter_wake_up(&cmd->reply_waiter); + if (cmd->is_async) { + free(cmd); + cmd = NULL; + } else { + lttng_waiter_wake_up(&cmd->reply_waiter); + } pthread_mutex_unlock(&handle->cmd_queue.lock); return ret; error_unlock: @@ -2355,12 +2954,6 @@ error: return -1; } -static -unsigned long hash_client_socket(int socket) -{ - return hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed); -} - static int socket_set_non_blocking(int socket) { @@ -2384,11 +2977,14 @@ end: return ret; } +/* Client lock must be acquired by caller. */ static int client_reset_inbound_state(struct notification_client *client) { int ret; + ASSERT_LOCKED(client->lock); + ret = lttng_dynamic_buffer_set_size( &client->communication.inbound.buffer, 0); assert(!ret); @@ -2419,11 +3015,16 @@ int handle_notification_thread_client_connect( ret = -1; goto error; } + pthread_mutex_init(&client->lock, NULL); + client->id = state->next_notification_client_id++; CDS_INIT_LIST_HEAD(&client->condition_list); lttng_dynamic_buffer_init(&client->communication.inbound.buffer); lttng_dynamic_buffer_init(&client->communication.outbound.buffer); client->communication.inbound.expect_creds = true; + + pthread_mutex_lock(&client->lock); ret = client_reset_inbound_state(client); + pthread_mutex_unlock(&client->lock); if (ret) { ERR("[notification-thread] Failed to reset client communication's inbound state"); ret = 0; @@ -2467,6 +3068,9 @@ int handle_notification_thread_client_connect( cds_lfht_add(state->client_socket_ht, hash_client_socket(client->socket), &client->client_socket_ht_node); + cds_lfht_add(state->client_id_ht, + hash_client_id(client->id), + &client->client_id_ht_node); rcu_read_unlock(); return ret; @@ -2475,9 +3079,45 @@ error: return ret; } -int handle_notification_thread_client_disconnect( - int client_socket, +/* RCU read-lock must be held by the caller. */ +static +int notification_thread_client_disconnect( + struct notification_client *client, struct notification_thread_state *state) +{ + int ret; + struct lttng_condition_list_element *condition_list_element, *tmp; + + /* Acquire the client lock to disable its communication atomically. */ + pthread_mutex_lock(&client->lock); + client->communication.active = false; + ret = lttng_poll_del(&state->events, client->socket); + if (ret) { + ERR("[notification-thread] Failed to remove client socket %d from poll set", + client->socket); + } + pthread_mutex_unlock(&client->lock); + + cds_lfht_del(state->client_socket_ht, &client->client_socket_ht_node); + cds_lfht_del(state->client_id_ht, &client->client_id_ht_node); + + /* Release all conditions to which the client was subscribed. */ + cds_list_for_each_entry_safe(condition_list_element, tmp, + &client->condition_list, node) { + (void) notification_thread_client_unsubscribe(client, + condition_list_element->condition, state, NULL); + } + + /* + * Client no longer accessible to other threads (through the + * client lists). + */ + notification_client_destroy(client, state); + return ret; +} + +int handle_notification_thread_client_disconnect( + int client_socket, struct notification_thread_state *state) { int ret = 0; struct notification_client *client; @@ -2494,13 +3134,7 @@ int handle_notification_thread_client_disconnect( goto end; } - ret = lttng_poll_del(&state->events, client_socket); - if (ret) { - ERR("[notification-thread] Failed to remove client socket from poll set"); - } - cds_lfht_del(state->client_socket_ht, - &client->client_socket_ht_node); - notification_client_destroy(client, state); + ret = notification_thread_client_disconnect(client, state); end: rcu_read_unlock(); return ret; @@ -2516,11 +3150,11 @@ int handle_notification_thread_client_disconnect_all( rcu_read_lock(); DBG("[notification-thread] Closing all client connections"); cds_lfht_for_each_entry(state->client_socket_ht, &iter, client, - client_socket_ht_node) { + client_socket_ht_node) { int ret; - ret = handle_notification_thread_client_disconnect( - client->socket, state); + ret = notification_thread_client_disconnect( + client, state); if (ret) { error_encoutered = true; } @@ -2550,11 +3184,63 @@ int handle_notification_thread_trigger_unregister_all( } static -int client_flush_outgoing_queue(struct notification_client *client, +int client_handle_transmission_status( + struct notification_client *client, + enum client_transmission_status transmission_status, struct notification_thread_state *state) +{ + int ret = 0; + + ASSERT_LOCKED(client->lock); + + switch (transmission_status) { + case CLIENT_TRANSMISSION_STATUS_COMPLETE: + ret = lttng_poll_mod(&state->events, client->socket, + CLIENT_POLL_MASK_IN); + if (ret) { + goto end; + } + + client->communication.outbound.queued_command_reply = false; + client->communication.outbound.dropped_notification = false; + break; + case CLIENT_TRANSMISSION_STATUS_QUEUED: + /* + * We want to be notified whenever there is buffer space + * available to send the rest of the payload. + */ + ret = lttng_poll_mod(&state->events, client->socket, + CLIENT_POLL_MASK_IN_OUT); + if (ret) { + goto end; + } + break; + case CLIENT_TRANSMISSION_STATUS_FAIL: + ret = notification_thread_client_disconnect(client, state); + if (ret) { + goto end; + } + break; + case CLIENT_TRANSMISSION_STATUS_ERROR: + ret = -1; + goto end; + default: + abort(); + } +end: + return ret; +} + +/* Client lock must be acquired by caller. */ +static +enum client_transmission_status client_flush_outgoing_queue( + struct notification_client *client) { ssize_t ret; size_t to_send_count; + enum client_transmission_status status; + + ASSERT_LOCKED(client->lock); assert(client->communication.outbound.buffer.size != 0); to_send_count = client->communication.outbound.buffer.size; @@ -2580,25 +3266,12 @@ int client_flush_outgoing_queue(struct notification_client *client, if (ret) { goto error; } - - /* - * We want to be notified whenever there is buffer space - * available to send the rest of the payload. - */ - ret = lttng_poll_mod(&state->events, client->socket, - CLIENT_POLL_MASK_IN_OUT); - if (ret) { - goto error; - } + status = CLIENT_TRANSMISSION_STATUS_QUEUED; } else if (ret < 0) { /* Generic error, disconnect the client. */ - ERR("[notification-thread] Failed to send flush outgoing queue, disconnecting client (socket fd = %i)", + ERR("[notification-thread] Failed to flush outgoing queue, disconnecting client (socket fd = %i)", client->socket); - ret = handle_notification_thread_client_disconnect( - client->socket, state); - if (ret) { - goto error; - } + status = CLIENT_TRANSMISSION_STATUS_FAIL; } else { /* No error and flushed the queue completely. */ ret = lttng_dynamic_buffer_set_size( @@ -2606,21 +3279,15 @@ int client_flush_outgoing_queue(struct notification_client *client, if (ret) { goto error; } - ret = lttng_poll_mod(&state->events, client->socket, - CLIENT_POLL_MASK_IN); - if (ret) { - goto error; - } - - client->communication.outbound.queued_command_reply = false; - client->communication.outbound.dropped_notification = false; + status = CLIENT_TRANSMISSION_STATUS_COMPLETE; } - return 0; + return status; error: - return -1; + return CLIENT_TRANSMISSION_STATUS_ERROR; } +/* Client lock must be acquired by caller. */ static int client_send_command_reply(struct notification_client *client, struct notification_thread_state *state, @@ -2635,6 +3302,9 @@ int client_send_command_reply(struct notification_client *client, .size = sizeof(reply), }; char buffer[sizeof(msg) + sizeof(reply)]; + enum client_transmission_status transmission_status; + + ASSERT_LOCKED(client->lock); if (client->communication.outbound.queued_command_reply) { /* Protocol error. */ @@ -2650,22 +3320,214 @@ int client_send_command_reply(struct notification_client *client, &client->communication.outbound.buffer, buffer, sizeof(buffer)); if (ret) { - goto error; + goto error; + } + + transmission_status = client_flush_outgoing_queue(client); + ret = client_handle_transmission_status( + client, transmission_status, state); + if (ret) { + goto error; + } + + if (client->communication.outbound.buffer.size != 0) { + /* Queue could not be emptied. */ + client->communication.outbound.queued_command_reply = true; + } + + return 0; +error: + return -1; +} + +static +int client_handle_message_unknown(struct notification_client *client, + struct notification_thread_state *state) +{ + int ret; + + pthread_mutex_lock(&client->lock); + + /* + * Receiving message header. The function will be called again + * once the rest of the message as been received and can be + * interpreted. + */ + const struct lttng_notification_channel_message *msg; + + assert(sizeof(*msg) == client->communication.inbound.buffer.size); + msg = (const struct lttng_notification_channel_message *) + client->communication.inbound.buffer.data; + + if (msg->size == 0 || + msg->size > DEFAULT_MAX_NOTIFICATION_CLIENT_MESSAGE_PAYLOAD_SIZE) { + ERR("[notification-thread] Invalid notification channel message: length = %u", + msg->size); + ret = -1; + goto end; + } + + switch (msg->type) { + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE: + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE: + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE: + break; + default: + ret = -1; + ERR("[notification-thread] Invalid notification channel message: unexpected message type"); + goto end; + } + + client->communication.inbound.bytes_to_receive = msg->size; + client->communication.inbound.msg_type = + (enum lttng_notification_channel_message_type) msg->type; + ret = lttng_dynamic_buffer_set_size( + &client->communication.inbound.buffer, msg->size); +end: + pthread_mutex_unlock(&client->lock); + return ret; +} + +static +int client_handle_message_handshake(struct notification_client *client, + struct notification_thread_state *state) +{ + int ret; + struct lttng_notification_channel_command_handshake *handshake_client; + const struct lttng_notification_channel_command_handshake handshake_reply = { + .major = LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR, + .minor = LTTNG_NOTIFICATION_CHANNEL_VERSION_MINOR, + }; + const struct lttng_notification_channel_message msg_header = { + .type = LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE, + .size = sizeof(handshake_reply), + }; + enum lttng_notification_channel_status status = + LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; + char send_buffer[sizeof(msg_header) + sizeof(handshake_reply)]; + enum client_transmission_status transmission_status; + + pthread_mutex_lock(&client->lock); + + memcpy(send_buffer, &msg_header, sizeof(msg_header)); + memcpy(send_buffer + sizeof(msg_header), &handshake_reply, + sizeof(handshake_reply)); + + handshake_client = + (struct lttng_notification_channel_command_handshake *) + client->communication.inbound.buffer + .data; + client->major = handshake_client->major; + client->minor = handshake_client->minor; + if (!client->communication.inbound.creds_received) { + ERR("[notification-thread] No credentials received from client"); + ret = -1; + goto end; + } + + client->uid = LTTNG_SOCK_GET_UID_CRED( + &client->communication.inbound.creds); + client->gid = LTTNG_SOCK_GET_GID_CRED( + &client->communication.inbound.creds); + DBG("[notification-thread] Received handshake from client (uid = %u, gid = %u) with version %i.%i", + client->uid, client->gid, (int) client->major, + (int) client->minor); + + if (handshake_client->major != + LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_UNSUPPORTED_VERSION; + } + + ret = lttng_dynamic_buffer_append( + &client->communication.outbound.buffer, send_buffer, + sizeof(send_buffer)); + if (ret) { + ERR("[notification-thread] Failed to send protocol version to notification channel client"); + goto end; + } + + transmission_status = client_flush_outgoing_queue(client); + ret = client_handle_transmission_status( + client, transmission_status, state); + if (ret) { + goto end; + } + + ret = client_send_command_reply(client, state, status); + if (ret) { + ERR("[notification-thread] Failed to send reply to notification channel client"); + goto end; + } + + /* Set reception state to receive the next message header. */ + ret = client_reset_inbound_state(client); + if (ret) { + ERR("[notification-thread] Failed to reset client communication's inbound state"); + goto end; + } + client->validated = true; + client->communication.active = true; + +end: + pthread_mutex_unlock(&client->lock); + return ret; +} + +static +int client_handle_message_subscription( + struct notification_client *client, + enum lttng_notification_channel_message_type msg_type, + struct notification_thread_state *state) +{ + int ret; + struct lttng_condition *condition; + enum lttng_notification_channel_status status = + LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; + const struct lttng_buffer_view condition_view = + lttng_buffer_view_from_dynamic_buffer( + &client->communication.inbound.buffer, + 0, -1); + size_t expected_condition_size; + + pthread_mutex_lock(&client->lock); + expected_condition_size = client->communication.inbound.buffer.size; + pthread_mutex_unlock(&client->lock); + + ret = lttng_condition_create_from_buffer(&condition_view, &condition); + if (ret != expected_condition_size) { + ERR("[notification-thread] Malformed condition received from client"); + goto end; + } + + if (msg_type == LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE) { + ret = notification_thread_client_subscribe( + client, condition, state, &status); + } else { + ret = notification_thread_client_unsubscribe( + client, condition, state, &status); + } + if (ret) { + goto end; } - ret = client_flush_outgoing_queue(client, state); + pthread_mutex_lock(&client->lock); + ret = client_send_command_reply(client, state, status); if (ret) { - goto error; + ERR("[notification-thread] Failed to send reply to notification channel client"); + goto end_unlock; } - if (client->communication.outbound.buffer.size != 0) { - /* Queue could not be emptied. */ - client->communication.outbound.queued_command_reply = true; + /* Set reception state to receive the next message header. */ + ret = client_reset_inbound_state(client); + if (ret) { + ERR("[notification-thread] Failed to reset client communication's inbound state"); + goto end_unlock; } - return 0; -error: - return -1; +end_unlock: + pthread_mutex_unlock(&client->lock); +end: + return ret; } static @@ -2687,158 +3549,19 @@ int client_dispatch_message(struct notification_client *client, switch (client->communication.inbound.msg_type) { case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNKNOWN: { - /* - * Receiving message header. The function will be called again - * once the rest of the message as been received and can be - * interpreted. - */ - const struct lttng_notification_channel_message *msg; - - assert(sizeof(*msg) == - client->communication.inbound.buffer.size); - msg = (const struct lttng_notification_channel_message *) - client->communication.inbound.buffer.data; - - if (msg->size == 0 || msg->size > DEFAULT_MAX_NOTIFICATION_CLIENT_MESSAGE_PAYLOAD_SIZE) { - ERR("[notification-thread] Invalid notification channel message: length = %u", msg->size); - ret = -1; - goto end; - } - - switch (msg->type) { - case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE: - case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE: - case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE: - break; - default: - ret = -1; - ERR("[notification-thread] Invalid notification channel message: unexpected message type"); - goto end; - } - - client->communication.inbound.bytes_to_receive = msg->size; - client->communication.inbound.msg_type = - (enum lttng_notification_channel_message_type) msg->type; - ret = lttng_dynamic_buffer_set_size( - &client->communication.inbound.buffer, msg->size); - if (ret) { - goto end; - } + ret = client_handle_message_unknown(client, state); break; } case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE: { - struct lttng_notification_channel_command_handshake *handshake_client; - struct lttng_notification_channel_command_handshake handshake_reply = { - .major = LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR, - .minor = LTTNG_NOTIFICATION_CHANNEL_VERSION_MINOR, - }; - struct lttng_notification_channel_message msg_header = { - .type = LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE, - .size = sizeof(handshake_reply), - }; - enum lttng_notification_channel_status status = - LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; - char send_buffer[sizeof(msg_header) + sizeof(handshake_reply)]; - - memcpy(send_buffer, &msg_header, sizeof(msg_header)); - memcpy(send_buffer + sizeof(msg_header), &handshake_reply, - sizeof(handshake_reply)); - - handshake_client = - (struct lttng_notification_channel_command_handshake *) - client->communication.inbound.buffer.data; - client->major = handshake_client->major; - client->minor = handshake_client->minor; - if (!client->communication.inbound.creds_received) { - ERR("[notification-thread] No credentials received from client"); - ret = -1; - goto end; - } - - client->uid = LTTNG_SOCK_GET_UID_CRED( - &client->communication.inbound.creds); - client->gid = LTTNG_SOCK_GET_GID_CRED( - &client->communication.inbound.creds); - DBG("[notification-thread] Received handshake from client (uid = %u, gid = %u) with version %i.%i", - client->uid, client->gid, (int) client->major, - (int) client->minor); - - if (handshake_client->major != LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR) { - status = LTTNG_NOTIFICATION_CHANNEL_STATUS_UNSUPPORTED_VERSION; - } - - ret = lttng_dynamic_buffer_append(&client->communication.outbound.buffer, - send_buffer, sizeof(send_buffer)); - if (ret) { - ERR("[notification-thread] Failed to send protocol version to notification channel client"); - goto end; - } - - ret = client_flush_outgoing_queue(client, state); - if (ret) { - goto end; - } - - ret = client_send_command_reply(client, state, status); - if (ret) { - ERR("[notification-thread] Failed to send reply to notification channel client"); - goto end; - } - - /* Set reception state to receive the next message header. */ - ret = client_reset_inbound_state(client); - if (ret) { - ERR("[notification-thread] Failed to reset client communication's inbound state"); - goto end; - } - client->validated = true; + ret = client_handle_message_handshake(client, state); break; } case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE: case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE: { - struct lttng_condition *condition; - enum lttng_notification_channel_status status = - LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; - const struct lttng_buffer_view condition_view = - lttng_buffer_view_from_dynamic_buffer( - &client->communication.inbound.buffer, - 0, -1); - size_t expected_condition_size = - client->communication.inbound.buffer.size; - - ret = lttng_condition_create_from_buffer(&condition_view, - &condition); - if (ret != expected_condition_size) { - ERR("[notification-thread] Malformed condition received from client"); - goto end; - } - - if (client->communication.inbound.msg_type == - LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE) { - ret = notification_thread_client_subscribe(client, - condition, state, &status); - } else { - ret = notification_thread_client_unsubscribe(client, - condition, state, &status); - } - if (ret) { - goto end; - } - - ret = client_send_command_reply(client, state, status); - if (ret) { - ERR("[notification-thread] Failed to send reply to notification channel client"); - goto end; - } - - /* Set reception state to receive the next message header. */ - ret = client_reset_inbound_state(client); - if (ret) { - ERR("[notification-thread] Failed to reset client communication's inbound state"); - goto end; - } + ret = client_handle_message_subscription(client, + client->communication.inbound.msg_type, state); break; } default: @@ -2856,6 +3579,7 @@ int handle_notification_thread_client_in( struct notification_client *client; ssize_t recv_ret; size_t offset; + bool message_is_complete = false; client = get_client_from_socket(socket, state); if (!client) { @@ -2864,6 +3588,7 @@ int handle_notification_thread_client_in( goto end; } + pthread_mutex_lock(&client->lock); offset = client->communication.inbound.buffer.size - client->communication.inbound.bytes_to_receive; if (client->communication.inbound.expect_creds) { @@ -2880,12 +3605,17 @@ int handle_notification_thread_client_in( client->communication.inbound.buffer.data + offset, client->communication.inbound.bytes_to_receive); } + if (recv_ret >= 0) { + client->communication.inbound.bytes_to_receive -= recv_ret; + message_is_complete = client->communication.inbound + .bytes_to_receive == 0; + } + pthread_mutex_unlock(&client->lock); if (recv_ret < 0) { goto error_disconnect_client; } - client->communication.inbound.bytes_to_receive -= recv_ret; - if (client->communication.inbound.bytes_to_receive == 0) { + if (message_is_complete) { ret = client_dispatch_message(client, state); if (ret) { /* @@ -2894,13 +3624,11 @@ int handle_notification_thread_client_in( */ goto error_disconnect_client; } - } else { - goto end; } end: return ret; error_disconnect_client: - ret = handle_notification_thread_client_disconnect(socket, state); + ret = notification_thread_client_disconnect(client, state); return ret; } @@ -2910,6 +3638,7 @@ int handle_notification_thread_client_out( { int ret; struct notification_client *client; + enum client_transmission_status transmission_status; client = get_client_from_socket(socket, state); if (!client) { @@ -2918,7 +3647,11 @@ int handle_notification_thread_client_out( goto end; } - ret = client_flush_outgoing_queue(client, state); + pthread_mutex_lock(&client->lock); + transmission_status = client_flush_outgoing_queue(client); + ret = client_handle_transmission_status( + client, transmission_status, state); + pthread_mutex_unlock(&client->lock); if (ret) { goto end; } @@ -3089,33 +3822,77 @@ end: } static -int client_enqueue_dropped_notification(struct notification_client *client, - struct notification_thread_state *state) +int client_notification_overflow(struct notification_client *client) { - int ret; - struct lttng_notification_channel_message msg = { + int ret = 0; + const struct lttng_notification_channel_message msg = { .type = (int8_t) LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION_DROPPED, - .size = 0, }; + ASSERT_LOCKED(client->lock); + + DBG("Dropping notification addressed to client (socket fd = %i)", + client->socket); + if (client->communication.outbound.dropped_notification) { + /* + * The client already has a "notification dropped" message + * in its outgoing queue. Nothing to do since all + * of those messages are coalesced. + */ + goto end; + } + + client->communication.outbound.dropped_notification = true; ret = lttng_dynamic_buffer_append( &client->communication.outbound.buffer, &msg, sizeof(msg)); + if (ret) { + PERROR("Failed to enqueue \"dropped notification\" message in client's (socket fd = %i) outgoing queue", + client->socket); + } +end: return ret; } +static int client_handle_transmission_status_wrapper( + struct notification_client *client, + enum client_transmission_status status, + void *user_data) +{ + return client_handle_transmission_status(client, status, + (struct notification_thread_state *) user_data); +} + static int send_evaluation_to_clients(const struct lttng_trigger *trigger, const struct lttng_evaluation *evaluation, struct notification_client_list* client_list, struct notification_thread_state *state, - uid_t channel_uid, gid_t channel_gid) + uid_t object_uid, gid_t object_gid) +{ + return notification_client_list_send_evaluation(client_list, + lttng_trigger_get_const_condition(trigger), evaluation, + lttng_trigger_get_credentials(trigger), + &(struct lttng_credentials){ + .uid = object_uid, .gid = object_gid}, + client_handle_transmission_status_wrapper, state); +} + +LTTNG_HIDDEN +int notification_client_list_send_evaluation( + struct notification_client_list *client_list, + const struct lttng_condition *condition, + const struct lttng_evaluation *evaluation, + const struct lttng_credentials *trigger_creds, + const struct lttng_credentials *source_object_creds, + report_client_transmission_result_cb client_report, + void *user_data) { int ret = 0; struct lttng_dynamic_buffer msg_buffer; struct notification_client_list_element *client_list_element, *tmp; const struct lttng_notification notification = { - .condition = (struct lttng_condition *) lttng_trigger_get_const_condition(trigger), + .condition = (struct lttng_condition *) condition, .evaluation = (struct lttng_evaluation *) evaluation, }; struct lttng_notification_channel_message msg_header = { @@ -3141,16 +3918,40 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, ((struct lttng_notification_channel_message * ) msg_buffer.data)->size = (uint32_t) (msg_buffer.size - sizeof(msg_header)); + pthread_mutex_lock(&client_list->lock); cds_list_for_each_entry_safe(client_list_element, tmp, &client_list->list, node) { + enum client_transmission_status transmission_status; struct notification_client *client = client_list_element->client; - if (client->uid != channel_uid && client->gid != channel_gid && - client->uid != 0) { - /* Client is not allowed to monitor this channel. */ - DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this channel"); - continue; + ret = 0; + pthread_mutex_lock(&client->lock); + if (source_object_creds) { + if (client->uid != source_object_creds->uid && + client->gid != source_object_creds->gid && + client->uid != 0) { + /* + * Client is not allowed to monitor this + * object. + */ + DBG("[notification-thread] Skipping client at it does not have the object permission to receive notification for this trigger"); + goto unlock_client; + } + } + + /* TODO: what is the behavior for root client on non root + * trigger? Since multiple triggers (different user) can have the same condition + * but with different action group that can have each a notify. + * Does the root client receive multiple notification for all + * those triggers with the same condition or only notification + * for triggers the root user configured? + * For now we do the later. All users including the root user + * can only receive notification from trigger it registered. + */ + if (client->uid != trigger_creds->uid && client->gid != trigger_creds->gid) { + DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this trigger"); + goto unlock_client; } DBG("[notification-thread] Sending notification to client (fd = %i, %zu bytes)", @@ -3163,37 +3964,302 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, * notification since the socket spilled-over to the * queue. */ - DBG("[notification-thread] Dropping notification addressed to client (socket fd = %i)", - client->socket); - if (!client->communication.outbound.dropped_notification) { - client->communication.outbound.dropped_notification = true; - ret = client_enqueue_dropped_notification( - client, state); - if (ret) { - goto end; - } + ret = client_notification_overflow(client); + if (ret) { + goto unlock_client; } - continue; } ret = lttng_dynamic_buffer_append_buffer( &client->communication.outbound.buffer, &msg_buffer); if (ret) { - goto end; + goto unlock_client; } - ret = client_flush_outgoing_queue(client, state); + transmission_status = client_flush_outgoing_queue(client); + ret = client_report(client, transmission_status, user_data); if (ret) { - goto end; + goto unlock_client; + } +unlock_client: + pthread_mutex_unlock(&client->lock); + if (ret) { + goto end_unlock_list; } } ret = 0; + +end_unlock_list: + pthread_mutex_unlock(&client_list->lock); end: lttng_dynamic_buffer_reset(&msg_buffer); return ret; } +int perform_event_action_notify(struct notification_thread_state *state, + const struct lttng_trigger *trigger, + const struct lttng_trigger_notification *notification, + const struct lttng_action *action) +{ + int ret; + struct notification_client_list *client_list; + struct lttng_evaluation *evaluation = NULL; + const struct lttng_credentials *creds = lttng_trigger_get_credentials(trigger); + + /* + * Check if any client is subscribed to the result of this + * evaluation. + */ + client_list = get_client_list_from_condition(state, trigger->condition); + assert(client_list); + if (cds_list_empty(&client_list->list)) { + ret = 0; + goto end; + } + + evaluation = lttng_evaluation_event_rule_create(trigger->name); + if (!evaluation) { + ERR("Failed to create event rule hit evaluation"); + ret = -1; + goto end; + } + + /* Dispatch evaluation result to all clients. + * Note that here the passed credentials are the one from the trigger, + * this is because there is no internal object binded to the trigger per + * see and the credential validation is done at the registration level + * for the event rule based trigger. For a channel the credential + * validation can only be done on notify since the trigger can be + * registered before the channel/session creation. + */ + ret = send_evaluation_to_clients(trigger, + evaluation, client_list, state, + creds->uid, + creds->gid); + lttng_evaluation_destroy(evaluation); + if (caa_unlikely(ret)) { + goto end; + } +end: + return ret; + +} + +/* This can be called recursively, pass NULL for action on the first iteration */ +int perform_event_action(struct notification_thread_state *state, + const struct lttng_trigger *trigger, + const struct lttng_trigger_notification *notification, + const struct lttng_action *action) +{ + int ret = 0; + enum lttng_action_type action_type; + + assert(trigger); + + if (!action) { + action = lttng_trigger_get_const_action(trigger); + } + + action_type = lttng_action_get_type_const(action); + DBG("Handling action %s for trigger id %s (%" PRIu64 ")", + lttng_action_type_string(action_type), trigger->name, + trigger->key.value); + + switch (action_type) { + case LTTNG_ACTION_TYPE_GROUP: + { + /* Recurse into the group */ + const struct lttng_action *tmp = NULL; + unsigned int count = 0; + (void) lttng_action_group_get_count(action, &count); + for (int i = 0; i < count; i++) { + tmp = lttng_action_group_get_at_index_const(action, i); + assert(tmp); + ret = perform_event_action(state, trigger, notification, tmp); + if (ret < 0) { + goto end; + } + } + break; + } + case LTTNG_ACTION_TYPE_NOTIFY: + { + ret = perform_event_action_notify(state, trigger, notification, action); + break; + } + default: + break; + } +end: + return ret; +} + +int handle_notification_thread_event(struct notification_thread_state *state, + int pipe, + enum lttng_domain_type domain) +{ + int ret; + struct lttng_ust_trigger_notification ust_notification; + uint64_t kernel_notification; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + struct notification_trigger_tokens_ht_element *element; + struct lttng_trigger_notification notification; + void *reception_buffer; + size_t reception_size; + enum action_executor_status executor_status; + struct notification_client_list *client_list = NULL; + + notification.type = domain; + + switch(domain) { + case LTTNG_DOMAIN_UST: + reception_buffer = (void *) &ust_notification; + reception_size = sizeof(ust_notification); + notification.u.ust = &ust_notification; + break; + case LTTNG_DOMAIN_KERNEL: + reception_buffer = (void *) &kernel_notification; + reception_size = sizeof(kernel_notification); + notification.u.kernel = &kernel_notification; + break; + default: + assert(0); + } + + /* + * The monitoring pipe only holds messages smaller than PIPE_BUF, + * ensuring that read/write of sampling messages are atomic. + */ + /* TODO: should we read as much as we can ? EWOULDBLOCK? */ + + ret = lttng_read(pipe, reception_buffer, reception_size); + if (ret != reception_size) { + ERR("[notification-thread] Failed to read from event source pipe (fd = %i)", + pipe); + /* TODO: Should this error out completly. + * This can happen when an app is killed as of today + * ret = -1 cause the whole thread to die and fuck up + * everything. + */ + goto end; + } + + switch(domain) { + case LTTNG_DOMAIN_UST: + notification.id = ust_notification.id; + break; + case LTTNG_DOMAIN_KERNEL: + notification.id = kernel_notification; + break; + default: + assert(0); + } + + /* Find triggers associated with this token. */ + rcu_read_lock(); + cds_lfht_lookup(state->trigger_tokens_ht, + hash_key_u64(¬ification.id, lttng_ht_seed), match_trigger_token, + ¬ification.id, &iter); + node = cds_lfht_iter_get_node(&iter); + if (caa_unlikely(!node)) { + /* TODO: is this an error? This might happen if the receive side + * is slow to process event from source and that the trigger was + * removed but the app still kicking. This yield another + * question on the trigger lifetime and when we can remove a + * trigger. How to guarantee that all event with the token idea + * have be processed? Do we want to provide this guarantee? + * + * Update: I have encountered this when using a trigger on + * sched_switch and then removing it. The frequency is quite + * high hence we en up exactly in the mentionned scenario. + * AFAIK this might be the best way to handle this. + */ + ret = 0; + goto end_unlock; + } + element = caa_container_of(node, + struct notification_trigger_tokens_ht_element, + node); + + if (!lttng_trigger_is_ready_to_fire(element->trigger)) { + ret = 0; + goto end_unlock; + } + + client_list = get_client_list_from_condition(state, + lttng_trigger_get_const_condition(element->trigger)); + executor_status = action_executor_enqueue( + state->executor, element->trigger, client_list); + switch (executor_status) { + case ACTION_EXECUTOR_STATUS_OK: + ret = 0; + break; + case ACTION_EXECUTOR_STATUS_OVERFLOW: + { + struct notification_client_list_element *client_list_element, + *tmp; + + /* + * Not a fatal error; this is expected and simply means the + * executor has too much work queued already. + */ + ret = 0; + + if (!client_list) { + break; + } + + /* Warn clients that a notification (or more) was dropped. */ + pthread_mutex_lock(&client_list->lock); + cds_list_for_each_entry_safe(client_list_element, tmp, + &client_list->list, node) { + enum client_transmission_status transmission_status; + struct notification_client *client = + client_list_element->client; + + pthread_mutex_lock(&client->lock); + ret = client_notification_overflow(client); + if (ret) { + /* Fatal error. */ + goto next_client; + } + + transmission_status = + client_flush_outgoing_queue(client); + ret = client_handle_transmission_status( + client, transmission_status, state); + if (ret) { + /* Fatal error. */ + goto next_client; + } +next_client: + pthread_mutex_unlock(&client->lock); + if (ret) { + break; + } + } + pthread_mutex_lock(&client_list->lock); + break; + } + case ACTION_EXECUTOR_STATUS_ERROR: + /* Fatal error, shut down everything. */ + ERR("Fatal error encoutered while enqueuing action"); + ret = -1; + goto end_unlock; + default: + /* Unhandled error. */ + abort(); + } + +end_unlock: + notification_client_list_put(client_list); + rcu_read_unlock(); +end: + return ret; +} + int handle_notification_thread_channel_sample( struct notification_thread_state *state, int pipe, enum lttng_domain_type domain) @@ -3332,16 +4398,23 @@ int handle_notification_thread_channel_sample( node) { const struct lttng_condition *condition; const struct lttng_action *action; - const struct lttng_trigger *trigger; - struct notification_client_list *client_list; + struct lttng_trigger *trigger; + struct notification_client_list *client_list = NULL; struct lttng_evaluation *evaluation = NULL; + bool client_list_is_empty; + ret = 0; trigger = trigger_list_element->trigger; condition = lttng_trigger_get_const_condition(trigger); assert(condition); action = lttng_trigger_get_const_action(trigger); + + if (!lttng_trigger_is_ready_to_fire(trigger)) { + goto put_list; + } /* Notify actions are the only type currently supported. */ + /* TODO support other type of action */ assert(lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_NOTIFY); @@ -3351,12 +4424,13 @@ int handle_notification_thread_channel_sample( */ client_list = get_client_list_from_condition(state, condition); assert(client_list); - if (cds_list_empty(&client_list->list)) { + client_list_is_empty = cds_list_empty(&client_list->list); + if (client_list_is_empty) { /* * No clients interested in the evaluation's result, * skip it. */ - continue; + goto put_list; } ret = evaluate_buffer_condition(condition, &evaluation, state, @@ -3366,11 +4440,11 @@ int handle_notification_thread_channel_sample( latest_session_consumed_total, channel_info); if (caa_unlikely(ret)) { - goto end_unlock; + goto put_list; } if (caa_likely(!evaluation)) { - continue; + goto put_list; } /* Dispatch evaluation result to all clients. */ @@ -3379,8 +4453,10 @@ int handle_notification_thread_channel_sample( channel_info->session_info->uid, channel_info->session_info->gid); lttng_evaluation_destroy(evaluation); +put_list: + notification_client_list_put(client_list); if (caa_unlikely(ret)) { - goto end_unlock; + break; } } end_unlock: diff --git a/src/bin/lttng-sessiond/notification-thread-events.h b/src/bin/lttng-sessiond/notification-thread-events.h index 2f699bf45..7affb752d 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.h +++ b/src/bin/lttng-sessiond/notification-thread-events.h @@ -44,4 +44,8 @@ int handle_notification_thread_channel_sample( struct notification_thread_state *state, int pipe, enum lttng_domain_type domain); +int handle_notification_thread_event( + struct notification_thread_state *state, int pipe, + enum lttng_domain_type domain); + #endif /* NOTIFICATION_THREAD_EVENTS_H */ diff --git a/src/bin/lttng-sessiond/notification-thread-internal.h b/src/bin/lttng-sessiond/notification-thread-internal.h index 5242695f0..fc0e8858f 100644 --- a/src/bin/lttng-sessiond/notification-thread-internal.h +++ b/src/bin/lttng-sessiond/notification-thread-internal.h @@ -8,9 +8,13 @@ #ifndef NOTIFICATION_THREAD_INTERNAL_H #define NOTIFICATION_THREAD_INTERNAL_H +#include +#include #include -#include +#include #include +#include +#include struct channel_key { uint64_t key; @@ -64,4 +68,150 @@ struct channel_info { struct rcu_head rcu_node; }; +struct notification_client_list_element { + struct notification_client *client; + struct cds_list_head node; +}; + +/* + * Thread safety of notification_client and notification_client_list. + * + * The notification thread (main thread) and the action executor + * interact through client lists. Hence, when the action executor + * thread looks-up the list of clients subscribed to a given + * condition, it will acquire a reference to the list and lock it + * while attempting to communicate with the various clients. + * + * It is not necessary to reference-count clients as they are guaranteed + * to be 'alive' if they are present in a list and that list is locked. Indeed, + * removing references to the client from those subscription lists is part of + * the work performed on destruction of a client. + * + * No provision for other access scenarios are taken into account; + * this is the bare minimum to make these accesses safe and the + * notification thread's state is _not_ "thread-safe" in any general + * sense. + */ +struct notification_client_list { + pthread_mutex_t lock; + struct urcu_ref ref; + const struct lttng_trigger *trigger; + struct cds_list_head list; + /* Weak reference to container. */ + struct cds_lfht *notification_trigger_clients_ht; + struct cds_lfht_node notification_trigger_clients_ht_node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; +}; + +struct notification_client { + /* Nests within the notification_client_list lock. */ + pthread_mutex_t lock; + notification_client_id id; + int socket; + /* Client protocol version. */ + uint8_t major, minor; + uid_t uid; + gid_t gid; + /* + * Indicates if the credentials and versions of the client have been + * checked. + */ + bool validated; + /* + * Conditions to which the client's notification channel is subscribed. + * List of struct lttng_condition_list_node. The condition member is + * owned by the client. + */ + struct cds_list_head condition_list; + struct cds_lfht_node client_socket_ht_node; + struct cds_lfht_node client_id_ht_node; + struct { + /* + * If a client's communication is inactive, it means that a + * fatal error has occurred (could be either a protocol error or + * the socket API returned a fatal error). No further + * communication should be attempted; the client is queued for + * clean-up. + */ + bool active; + struct { + /* + * During the reception of a message, the reception + * buffers' "size" is set to contain the current + * message's complete payload. + */ + struct lttng_dynamic_buffer buffer; + /* Bytes left to receive for the current message. */ + size_t bytes_to_receive; + /* Type of the message being received. */ + enum lttng_notification_channel_message_type msg_type; + /* + * Indicates whether or not credentials are expected + * from the client. + */ + bool expect_creds; + /* + * Indicates whether or not credentials were received + * from the client. + */ + bool creds_received; + /* Only used during credentials reception. */ + lttng_sock_cred creds; + } inbound; + struct { + /* + * Indicates whether or not a notification addressed to + * this client was dropped because a command reply was + * already buffered. + * + * A notification is dropped whenever the buffer is not + * empty. + */ + bool dropped_notification; + /* + * Indicates whether or not a command reply is already + * buffered. In this case, it means that the client is + * not consuming command replies before emitting a new + * one. This could be caused by a protocol error or a + * misbehaving/malicious client. + */ + bool queued_command_reply; + struct lttng_dynamic_buffer buffer; + } outbound; + } communication; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; +}; + +enum client_transmission_status { + CLIENT_TRANSMISSION_STATUS_COMPLETE, + CLIENT_TRANSMISSION_STATUS_QUEUED, + /* Communication failure. */ + CLIENT_TRANSMISSION_STATUS_FAIL, + /* Fatal error. */ + CLIENT_TRANSMISSION_STATUS_ERROR, +}; + +LTTNG_HIDDEN +bool notification_client_list_get(struct notification_client_list *list); + +LTTNG_HIDDEN +void notification_client_list_put(struct notification_client_list *list); + +typedef int (*report_client_transmission_result_cb)( + struct notification_client *client, + enum client_transmission_status status, + void *user_data); + +LTTNG_HIDDEN +int notification_client_list_send_evaluation( + struct notification_client_list *list, + const struct lttng_condition *condition, + const struct lttng_evaluation *evaluation, + const struct lttng_credentials *trigger_creds, + const struct lttng_credentials *source_object_creds, + report_client_transmission_result_cb client_report, + void *user_data); + #endif /* NOTIFICATION_THREAD_INTERNAL_H */ diff --git a/src/bin/lttng-sessiond/notification-thread.c b/src/bin/lttng-sessiond/notification-thread.c index 7dadb2c67..a52756c5a 100644 --- a/src/bin/lttng-sessiond/notification-thread.c +++ b/src/bin/lttng-sessiond/notification-thread.c @@ -29,6 +29,9 @@ #include "health-sessiond.h" #include "thread.h" +#include "kernel.h" +#include + #include #include #include @@ -70,14 +73,27 @@ void notification_thread_handle_destroy( PERROR("close kernel consumer channel monitoring pipe"); } } + + /* TODO: refactor this if needed. Lifetime of the kernel notification + * event source. + * event_trigger_sources.kernel_tracer is owned by the main thread and + * is closed at this point. + */ + handle->event_trigger_sources.kernel_tracer = -1; end: free(handle); } +/* + * TODO: refactor this if needed. Lifetime of the kernel notification event source. + * The kernel_notification_monitor_fd ownwership remain to the main thread. + * This is because we need to close this fd before removing the modules. + */ struct notification_thread_handle *notification_thread_handle_create( struct lttng_pipe *ust32_channel_monitor_pipe, struct lttng_pipe *ust64_channel_monitor_pipe, - struct lttng_pipe *kernel_channel_monitor_pipe) + struct lttng_pipe *kernel_channel_monitor_pipe, + int kernel_notification_monitor_fd) { int ret; struct notification_thread_handle *handle; @@ -88,6 +104,8 @@ struct notification_thread_handle *notification_thread_handle_create( goto end; } + handle->event_trigger_sources.kernel_tracer = -1; + sem_init(&handle->ready, 0, 0); event_pipe = lttng_pipe_open(FD_CLOEXEC); @@ -135,6 +153,14 @@ struct notification_thread_handle *notification_thread_handle_create( } else { handle->channel_monitoring_pipes.kernel_consumer = -1; } + + handle->event_trigger_sources.kernel_tracer = kernel_notification_monitor_fd; + + CDS_INIT_LIST_HEAD(&handle->event_trigger_sources.list); + ret = pthread_mutex_init(&handle->event_trigger_sources.lock, NULL); + if (ret) { + goto error; + } end: return handle; error: @@ -266,14 +292,15 @@ int init_poll_set(struct lttng_poll_event *poll_set, int ret; /* - * Create pollset with size 5: + * Create pollset with size 6: * - notification channel socket (listen for new connections), * - command queue event fd (internal sessiond commands), * - consumerd (32-bit user space) channel monitor pipe, * - consumerd (64-bit user space) channel monitor pipe, * - consumerd (kernel) channel monitor pipe. + * - kernel trigger event pipe, */ - ret = lttng_poll_create(poll_set, 5, LTTNG_CLOEXEC); + ret = lttng_poll_create(poll_set, 6, LTTNG_CLOEXEC); if (ret < 0) { goto end; } @@ -305,7 +332,7 @@ int init_poll_set(struct lttng_poll_event *poll_set, goto error; } if (handle->channel_monitoring_pipes.kernel_consumer < 0) { - goto end; + goto skip_kernel_consumer; } ret = lttng_poll_add(poll_set, handle->channel_monitoring_pipes.kernel_consumer, @@ -314,6 +341,19 @@ int init_poll_set(struct lttng_poll_event *poll_set, ERR("[notification-thread] Failed to add kernel channel monitoring pipe fd to pollset"); goto error; } + +skip_kernel_consumer: + if (handle->event_trigger_sources.kernel_tracer < 0) { + goto end; + } + + ret = lttng_poll_add(poll_set, + handle->event_trigger_sources.kernel_tracer, + LPOLLIN | LPOLLERR); + if (ret < 0) { + ERR("[notification-thread] Failed to add kernel trigger notification monitoring pipe fd to pollset"); + goto error; + } end: return ret; error: @@ -332,6 +372,10 @@ void fini_thread_state(struct notification_thread_state *state) ret = cds_lfht_destroy(state->client_socket_ht, NULL); assert(!ret); } + if (state->client_id_ht) { + ret = cds_lfht_destroy(state->client_id_ht, NULL); + assert(!ret); + } if (state->triggers_ht) { ret = handle_notification_thread_trigger_unregister_all(state); assert(!ret); @@ -359,6 +403,14 @@ void fini_thread_state(struct notification_thread_state *state) ret = cds_lfht_destroy(state->sessions_ht, NULL); assert(!ret); } + if (state->trigger_tokens_ht) { + ret = cds_lfht_destroy(state->trigger_tokens_ht, NULL); + assert(!ret); + } + if (state->triggers_by_name_ht) { + ret = cds_lfht_destroy(state->triggers_by_name_ht, NULL); + assert(!ret); + } /* * Must be destroyed after all channels have been destroyed. * See comment in struct lttng_session_trigger_list. @@ -371,6 +423,9 @@ void fini_thread_state(struct notification_thread_state *state) notification_channel_socket_destroy( state->notification_channel_socket); } + if (state->executor) { + action_executor_destroy(state->executor); + } lttng_poll_clean(&state->events); } @@ -397,6 +452,7 @@ int init_thread_state(struct notification_thread_handle *handle, memset(state, 0, sizeof(*state)); state->notification_channel_socket = -1; + state->trigger_id.token_generator = 1; lttng_poll_init(&state->events); ret = notification_channel_socket_create(); @@ -424,6 +480,12 @@ int init_thread_state(struct notification_thread_handle *handle, goto error; } + state->client_id_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, + CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + if (!state->client_id_ht) { + goto error; + } + state->channel_triggers_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); if (!state->channel_triggers_ht) { @@ -463,6 +525,20 @@ int init_thread_state(struct notification_thread_handle *handle, if (!state->triggers_ht) { goto error; } + state->trigger_tokens_ht = cds_lfht_new(DEFAULT_HT_SIZE, + 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + if (!state->trigger_tokens_ht) { + goto error; + } + state->triggers_by_name_ht = cds_lfht_new(DEFAULT_HT_SIZE, + 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + if (!state->triggers_by_name_ht) { + goto error; + } + state->executor = action_executor_create(handle); + if (!state->executor) { + goto error; + } mark_thread_as_ready(handle); end: return 0; @@ -507,6 +583,54 @@ end: return ret; } +static int handle_trigger_event_pipe(int fd, + uint32_t revents, + struct notification_thread_handle *handle, + struct notification_thread_state *state) +{ + int ret = 0; + enum lttng_domain_type domain; + + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + ret = lttng_poll_del(&state->events, fd); + if (ret) { + ERR("[notification-thread] Failed to remove event monitoring pipe from poll set"); + } + goto end; + } + + if (fd == handle->event_trigger_sources.kernel_tracer) { + domain = LTTNG_DOMAIN_KERNEL; + } else { + domain = LTTNG_DOMAIN_UST; + } + + ret = handle_notification_thread_event(state, fd, domain); + if (ret) { + ERR("[notification-thread] Event sample handling error occurred for fd: %d", fd); + ret = -1; + goto end; + } +end: + return ret; +} + +static bool fd_is_event_source(struct notification_thread_handle *handle, int fd) +{ + struct notification_event_trigger_source_element *source_element, *tmp; + cds_list_for_each_entry_safe(source_element, tmp, + &handle->event_trigger_sources.list, node) { + if (source_element->fd != fd) { + continue; + } + return true; + } + if (fd == handle->event_trigger_sources.kernel_tracer) { + return true; + } + return false; +} + /* * This thread services notification channel clients and commands received * from various lttng-sessiond components over a command queue. @@ -594,6 +718,11 @@ void *thread_notification(void *data) if (ret) { goto error; } + } else if (fd_is_event_source(handle, fd)) { + ret = handle_trigger_event_pipe(fd, revents, handle, &state); + if (ret) { + goto error; + } } else { /* Activity on a client's socket. */ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { diff --git a/src/bin/lttng-sessiond/notification-thread.h b/src/bin/lttng-sessiond/notification-thread.h index 3d766780e..e23cc1a32 100644 --- a/src/bin/lttng-sessiond/notification-thread.h +++ b/src/bin/lttng-sessiond/notification-thread.h @@ -8,16 +8,32 @@ #ifndef NOTIFICATION_THREAD_H #define NOTIFICATION_THREAD_H -#include -#include -#include -#include -#include +#include "action-executor.h" +#include "thread.h" #include #include +#include +#include #include #include -#include "thread.h" +#include +#include +#include + +typedef uint64_t notification_client_id; + +struct notification_event_trigger_source_element { + int fd; + struct cds_list_head node; +}; + +struct notification_trigger_tokens_ht_element { + uint64_t token; + struct lttng_trigger *trigger; + struct cds_lfht_node node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; +}; struct notification_thread_handle { /* @@ -39,8 +55,20 @@ struct notification_thread_handle { int ust64_consumer; int kernel_consumer; } channel_monitoring_pipes; + /* + * Read side of pipes used to reveice event trigger generetated by + * registered applications + */ + struct { + /* List of notification_event_trigger_source_element */ + struct cds_list_head list; + pthread_mutex_t lock; + int kernel_tracer; + } event_trigger_sources; /* Used to wait for the launch of the notification thread. */ sem_t ready; + + pthread_mutex_t trigger_lock; }; /** @@ -49,9 +77,14 @@ struct notification_thread_handle { * In order to speed-up and simplify queries, hash tables providing the * following associations are maintained: * - * - client_socket_ht: associate a client's socket (fd) to its "struct client" - * This hash table owns the "struct client" which must thus be - * disposed-of on removal from the hash table. + * - client_socket_ht: associate a client's socket (fd) to its + * "struct notification_client". + * This hash table owns the "struct notification_client" which must + * thus be disposed-of on removal from the hash table. + * + * - client_id_ht: associate a client's id to its "struct notification_client" + * This hash table holds a _weak_ reference to the + * "struct notification_client". * * - channel_triggers_ht: * associates a channel key to a list of @@ -99,9 +132,13 @@ struct notification_thread_handle { * channels through their struct channel_info (ref-counting is used). * * - triggers_ht: - * associates a condition to a struct lttng_trigger_ht_element. + * associates a trigger to a struct lttng_trigger_ht_element. * The hash table holds the ownership of the * lttng_trigger_ht_elements along with the triggers themselves. + * - triggers_by_name_ht: + * associates a trigger name to a struct lttng_trigger_ht_element. + * The hash table does not hold any ownership and is used strictly + * for lookup on registration. * * The thread reacts to the following internal events: * 1) creation of a tracing channel, @@ -145,6 +182,7 @@ struct notification_thread_handle { * notification_trigger_clients_ht, * - add trigger to channel_triggers_ht (if applicable), * - add trigger to session_triggers_ht (if applicable), + * - add trigger to triggers_by_name_ht * - add trigger to triggers_ht * - evaluate the trigger's condition right away to react if that condition * is true from the beginning. @@ -154,6 +192,7 @@ struct notification_thread_handle { * - remove the trigger from the notification_trigger_clients_ht, * - remove trigger from channel_triggers_ht (if applicable), * - remove trigger from session_triggers_ht (if applicable), + * - remove trigger to triggers_by_name_ht * - remove trigger from triggers_ht * * 5) Reception of a channel monitor sample from the consumer daemon @@ -168,9 +207,11 @@ struct notification_thread_handle { * 7) Session rotation completed * * 8) Connection of a client - * - add client socket to the client_socket_ht. + * - add client socket to the client_socket_ht, + * - add client socket to the client_id_ht. * * 9) Disconnection of a client + * - remove client socket from the client_id_ht, * - remove client socket from the client_socket_ht, * - traverse all conditions to which the client is subscribed and remove * the client from the notification_trigger_clients_ht. @@ -191,6 +232,7 @@ struct notification_thread_state { int notification_channel_socket; struct lttng_poll_event events; struct cds_lfht *client_socket_ht; + struct cds_lfht *client_id_ht; struct cds_lfht *channel_triggers_ht; struct cds_lfht *session_triggers_ht; struct cds_lfht *channel_state_ht; @@ -198,13 +240,22 @@ struct notification_thread_state { struct cds_lfht *channels_ht; struct cds_lfht *sessions_ht; struct cds_lfht *triggers_ht; + struct cds_lfht *triggers_by_name_ht; + struct cds_lfht *trigger_tokens_ht; + struct { + uint64_t token_generator; + uint64_t name_offset; + } trigger_id; + notification_client_id next_notification_client_id; + struct action_executor *executor; }; /* notification_thread_data takes ownership of the channel monitor pipes. */ struct notification_thread_handle *notification_thread_handle_create( struct lttng_pipe *ust32_channel_monitor_pipe, struct lttng_pipe *ust64_channel_monitor_pipe, - struct lttng_pipe *kernel_channel_monitor_pipe); + struct lttng_pipe *kernel_channel_monitor_pipe, + int kernel_notification_monitor_fd); void notification_thread_handle_destroy( struct notification_thread_handle *handle); struct lttng_thread *launch_notification_thread( diff --git a/src/bin/lttng-sessiond/rotate.c b/src/bin/lttng-sessiond/rotate.c index e2a3ef9ea..6813ce8c2 100644 --- a/src/bin/lttng-sessiond/rotate.c +++ b/src/bin/lttng-sessiond/rotate.c @@ -88,6 +88,9 @@ int subscribe_session_consumed_size_rotation(struct ltt_session *session, uint64 goto end; } + lttng_trigger_set_credentials( + session->rotate_trigger, session->uid, session->gid); + nc_status = lttng_notification_channel_subscribe( rotate_notification_channel, session->rotate_condition); if (nc_status != LTTNG_NOTIFICATION_CHANNEL_STATUS_OK) { diff --git a/src/bin/lttng-sessiond/sessiond-config.c b/src/bin/lttng-sessiond/sessiond-config.c index d908da972..f0ab4ce04 100644 --- a/src/bin/lttng-sessiond/sessiond-config.c +++ b/src/bin/lttng-sessiond/sessiond-config.c @@ -31,7 +31,7 @@ struct sessiond_config sessiond_config_build_defaults = { .daemonize = false, .sig_parent = false, - .tracing_group_name.value = DEFAULT_TRACING_GROUP, + .tracing_group_name.value = (char *) DEFAULT_TRACING_GROUP, .kmod_probes_list.value = NULL, .kmod_extra_probes_list.value = NULL, diff --git a/src/bin/lttng-sessiond/thread.c b/src/bin/lttng-sessiond/thread.c index ed090f6d6..ae7f45fd9 100644 --- a/src/bin/lttng-sessiond/thread.c +++ b/src/bin/lttng-sessiond/thread.c @@ -61,9 +61,10 @@ void *launch_thread(void *data) void *ret; struct lttng_thread *thread = (struct lttng_thread *) data; - DBG("Launching \"%s\" thread", thread->name); + logger_set_thread_name(thread->name, true); + DBG("Entering thread entry point"); ret = thread->entry(thread->data); - DBG("Thread \"%s\" has returned", thread->name); + DBG("Thread entry point has returned"); return ret; } @@ -164,20 +165,22 @@ bool _lttng_thread_shutdown(struct lttng_thread *thread) result = false; goto end; } - /* Release the list's reference to the thread. */ - cds_list_del(&thread->node); - lttng_thread_put(thread); + DBG("Joined thread \"%s\"", thread->name); end: return result; } bool lttng_thread_shutdown(struct lttng_thread *thread) { - bool result; - - pthread_mutex_lock(&thread_list.lock); - result = _lttng_thread_shutdown(thread); - pthread_mutex_unlock(&thread_list.lock); + const bool result = _lttng_thread_shutdown(thread); + + if (result) { + /* Release the list's reference to the thread. */ + pthread_mutex_lock(&thread_list.lock); + cds_list_del(&thread->node); + lttng_thread_put(thread); + pthread_mutex_unlock(&thread_list.lock); + } return result; } diff --git a/src/bin/lttng-sessiond/trace-kernel.c b/src/bin/lttng-sessiond/trace-kernel.c index 52f819b30..7ad8f2320 100644 --- a/src/bin/lttng-sessiond/trace-kernel.c +++ b/src/bin/lttng-sessiond/trace-kernel.c @@ -15,7 +15,17 @@ #include #include #include - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -130,6 +140,29 @@ struct ltt_kernel_event *trace_kernel_get_event_by_name( } } +struct ltt_kernel_token_event_rule *trace_kernel_find_trigger_by_token( + struct ltt_kernel_token_event_rule_list *list, + uint64_t token) +{ + struct ltt_kernel_token_event_rule *token_event_rule; + int found = 0; + + assert(list); + + cds_list_for_each_entry(token_event_rule, &list->head, list) { + if (token_event_rule->token == token) { + found = 1; + } + break; + } + if (found) { + DBG("Found token event rule %" PRIu64, token); + return token_event_rule; + } else { + return NULL; + } +} + /* * Allocate and initialize a kernel session data structure. * @@ -478,6 +511,162 @@ error: return ret; } +/* + * Allocate and initialize a kernel token event rule. + * + * Return pointer to structure or NULL. + */ +enum lttng_error_code trace_kernel_create_token_event_rule( + struct lttng_event_rule *event_rule, + uint64_t token, + struct ltt_kernel_token_event_rule **kernel_token_event_rule) +{ + enum lttng_error_code ret = LTTNG_OK; + struct ltt_kernel_token_event_rule *local_kernel_token_event_rule; + + assert(kernel_token_event_rule); + + local_kernel_token_event_rule = zmalloc(sizeof(struct ltt_kernel_token_event_rule)); + if (local_kernel_token_event_rule == NULL) { + PERROR("Failed to allocate ltt_kernel_token_event_rule structure"); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + local_kernel_token_event_rule->fd = -1; + local_kernel_token_event_rule->enabled = 1; + local_kernel_token_event_rule->token = token; + + /* Get the reference of the event rule */ + if (!lttng_event_rule_get(event_rule)) { + assert(0); + } + + local_kernel_token_event_rule->event_rule = event_rule; + /* The event rule still own the filter and bytecode */ + local_kernel_token_event_rule->filter = lttng_event_rule_get_filter_bytecode(event_rule); + + DBG3("[trace] Kernel token event rule %" PRIu64 " allocated", local_kernel_token_event_rule->token); +error: + *kernel_token_event_rule = local_kernel_token_event_rule; + return ret; + +} + +/* + * Initialize a kernel trigger from an event rule. + */ +enum lttng_error_code trace_kernel_init_trigger_from_event_rule(const struct lttng_event_rule *rule, + struct lttng_kernel_trigger *kernel_trigger) +{ + enum lttng_error_code ret; + enum lttng_event_rule_status status; + const char *name = NULL; + + /* TODO: do this for now but have disucssion on if this could be the + * responsability of the event_rule itself ala + * "lttng_even_rule_generate_kernel_trigger" + */ + switch (lttng_event_rule_get_type(rule)) { + case LTTNG_EVENT_RULE_TYPE_KPROBE: + kernel_trigger->instrumentation = LTTNG_KERNEL_KPROBE; + kernel_trigger->u.kprobe.addr = lttng_event_rule_kprobe_get_address(rule); + kernel_trigger->u.kprobe.offset = lttng_event_rule_kprobe_get_offset(rule); + strncpy(kernel_trigger->u.kprobe.symbol_name, + lttng_event_rule_kprobe_get_symbol_name(rule), LTTNG_KERNEL_SYM_NAME_LEN); + kernel_trigger->u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + (void) lttng_event_rule_kprobe_get_name(rule, &name); + ret = LTTNG_OK; + break; + case LTTNG_EVENT_RULE_TYPE_UPROBE: + { + const struct lttng_userspace_probe_location* location = NULL; + const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + + status = lttng_event_rule_uprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + kernel_trigger->instrumentation = LTTNG_KERNEL_UPROBE; + + /* + * Only the elf lookup method is supported at the moment. + */ + lookup = lttng_userspace_probe_location_get_lookup_method( + location); + if (!lookup) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * From the kernel tracer's perspective, all userspace probe + * event types are all the same: a file and an offset. + */ + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + /* Get the file descriptor on the target binary. */ + kernel_trigger->u.uprobe.fd = + lttng_userspace_probe_location_function_get_binary_fd(location); + + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + /* Get the file descriptor on the target binary. */ + kernel_trigger->u.uprobe.fd = + lttng_userspace_probe_location_tracepoint_get_binary_fd(location); + break; + default: + DBG("Unsupported lookup method type"); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + (void) lttng_event_rule_uprobe_get_name(rule, &name); + + ret = LTTNG_OK; + break; + } + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + assert("Not supported" && 0); + kernel_trigger->instrumentation = LTTNG_KERNEL_KRETPROBE; + kernel_trigger->u.kretprobe.addr = lttng_event_rule_kretprobe_get_address(rule); + kernel_trigger->u.kretprobe.offset = lttng_event_rule_kretprobe_get_offset(rule); + strncpy(kernel_trigger->u.kretprobe.symbol_name, + lttng_event_rule_kretprobe_get_symbol_name(rule), LTTNG_KERNEL_SYM_NAME_LEN); + kernel_trigger->u.kretprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + (void) lttng_event_rule_kretprobe_get_name(rule, &name); + ret = LTTNG_OK; + break; + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + /* TODO: assert his is a kernel domain event-rule */ + kernel_trigger->instrumentation = LTTNG_KERNEL_TRACEPOINT; + (void) lttng_event_rule_tracepoint_get_pattern(rule, &name); + ret = LTTNG_OK; + break; + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + kernel_trigger->instrumentation = LTTNG_KERNEL_SYSCALL; + (void) lttng_event_rule_syscall_get_pattern(rule, &name); + ret = LTTNG_OK; + break; + default: + ERR("Unknown kernel event rule instrumentation type (%d)", lttng_event_rule_get_type(rule)); + ret = LTTNG_ERR_INVALID; + goto error; + } + + /* + * WTF is LTTNG_EVENT_ALL??? and LTTNG_EVENT_FUNTION_ENTRY????? + */ + + /* Copy event name */ + strncpy(kernel_trigger->name, name, LTTNG_KERNEL_SYM_NAME_LEN); + kernel_trigger->name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + +error: + return ret; +} /* * Allocate and initialize a kernel metadata. * @@ -607,6 +796,33 @@ void trace_kernel_destroy_event(struct ltt_kernel_event *event) free(event); } +/* + * Cleanup kernel event structure. + */ +void trace_kernel_destroy_token_event_rule(struct ltt_kernel_token_event_rule *event) +{ + /* TODO: review in depth to ensure adequate disposing */ + assert(event); + + /* Remove from event list */ + cds_list_del(&event->list); + + if (event->fd >= 0) { + int ret; + + DBG("[trace] Closing ,token event rule fd %d", event->fd); + /* Close kernel fd */ + ret = close(event->fd); + if (ret) { + PERROR("close"); + } + } else { + DBG("[trace] Tearing down token event rule (no associated fd)"); + } + + lttng_event_rule_put(event->event_rule); + free(event); +} /* * Cleanup kernel context structure. */ diff --git a/src/bin/lttng-sessiond/trace-kernel.h b/src/bin/lttng-sessiond/trace-kernel.h index b290ce492..88e356854 100644 --- a/src/bin/lttng-sessiond/trace-kernel.h +++ b/src/bin/lttng-sessiond/trace-kernel.h @@ -23,6 +23,11 @@ struct ltt_kernel_event_list { struct cds_list_head head; }; +/* Kernel event rule token list */ +struct ltt_kernel_token_event_rule_list { + struct cds_list_head head; +}; + /* Channel stream list */ struct ltt_kernel_stream_list { struct cds_list_head head; @@ -52,6 +57,18 @@ struct ltt_kernel_event { struct lttng_userspace_probe_location *userspace_probe_location; }; +/* Kernel event */ +struct ltt_kernel_token_event_rule { + int fd; + int enabled; + enum lttng_event_type type; + struct lttng_event_rule *event_rule; + struct cds_list_head list; + uint64_t token; + const struct lttng_filter_bytecode *filter; + struct lttng_userspace_probe_location *userspace_probe_location; +}; + /* Kernel channel */ struct ltt_kernel_channel { int fd; @@ -135,6 +152,10 @@ struct ltt_kernel_event *trace_kernel_find_event( struct ltt_kernel_channel *trace_kernel_get_channel_by_name( const char *name, struct ltt_kernel_session *session); +struct ltt_kernel_token_event_rule *trace_kernel_find_trigger_by_token( + struct ltt_kernel_token_event_rule_list *list, + uint64_t token); + /* * Create functions malloc() the data structure. */ @@ -149,8 +170,14 @@ struct ltt_kernel_stream *trace_kernel_create_stream(const char *name, unsigned int count); struct ltt_kernel_context *trace_kernel_create_context( struct lttng_kernel_context *ctx); +enum lttng_error_code trace_kernel_create_token_event_rule( + struct lttng_event_rule *event_rule, + uint64_t token, + struct ltt_kernel_token_event_rule **kernel_token_event_rule); struct ltt_kernel_context *trace_kernel_copy_context( struct ltt_kernel_context *ctx); +enum lttng_error_code trace_kernel_init_trigger_from_event_rule(const struct lttng_event_rule *rule, + struct lttng_kernel_trigger *kernel_trigger); /* * Destroy functions free() the data structure and remove from linked list if @@ -162,6 +189,7 @@ void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel); void trace_kernel_destroy_event(struct ltt_kernel_event *event); void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream); void trace_kernel_destroy_context(struct ltt_kernel_context *ctx); +void trace_kernel_destroy_token_event_rule(struct ltt_kernel_token_event_rule *rule); void trace_kernel_free_session(struct ltt_kernel_session *session); #endif /* _LTT_TRACE_KERNEL_H */ diff --git a/src/bin/lttng-sessiond/trace-ust.c b/src/bin/lttng-sessiond/trace-ust.c index 92fd0de81..ebb9ca474 100644 --- a/src/bin/lttng-sessiond/trace-ust.c +++ b/src/bin/lttng-sessiond/trace-ust.c @@ -981,6 +981,7 @@ int trace_ust_track_id(enum lttng_tracker_type tracker_type, } } if (should_update_apps && session->active) { + /* TODO: should tokens be affected by tracking? */ ust_app_global_update_all(session); } goto end; @@ -1081,6 +1082,7 @@ int trace_ust_untrack_id(enum lttng_tracker_type tracker_type, } } if (should_update_apps && session->active) { + /* TODO should tokens be affected by tracker */ ust_app_global_update_all(session); } goto end; diff --git a/src/bin/lttng-sessiond/ust-abi-internal.h b/src/bin/lttng-sessiond/ust-abi-internal.h index 99da583d5..ea33cd176 100644 --- a/src/bin/lttng-sessiond/ust-abi-internal.h +++ b/src/bin/lttng-sessiond/ust-abi-internal.h @@ -87,6 +87,29 @@ struct lttng_ust_stream { */ } LTTNG_PACKED; +#define LTTNG_UST_TRIGGER_PADDING1 16 +#define LTTNG_UST_TRIGGER_PADDING2 (LTTNG_UST_SYM_NAME_LEN + 32) +struct lttng_ust_trigger { + uint64_t id; + enum lttng_ust_instrumentation instrumentation; + char name[LTTNG_UST_SYM_NAME_LEN]; /* event name */ + + enum lttng_ust_loglevel_type loglevel_type; + int loglevel; /* value, -1: all */ + char padding[LTTNG_UST_TRIGGER_PADDING1]; + + /* Per instrumentation type configuration */ + union { + char padding[LTTNG_UST_TRIGGER_PADDING2]; + } u; +} LTTNG_PACKED; + +#define LTTNG_TRIGGER_NOTIFICATION_PADDING 32 +struct lttng_ust_trigger_notification { + uint64_t id; + char padding[LTTNG_TRIGGER_NOTIFICATION_PADDING]; +} LTTNG_PACKED; + #define LTTNG_UST_EVENT_PADDING1 16 #define LTTNG_UST_EVENT_PADDING2 (LTTNG_UST_SYM_NAME_LEN + 32) struct lttng_ust_event { @@ -305,6 +328,9 @@ struct lttng_ust_event_exclusion { #define LTTNG_UST_FILTER _UST_CMD(0xA0) #define LTTNG_UST_EXCLUSION _UST_CMD(0xA1) +#define LTTNG_UST_TRIGGER_SEND_FD _UST_CMD(0xB0) +#define LTTNG_UST_TRIGGER_CREATE _UST_CMDW(0xB1, struct lttng_ust_trigger) + #define LTTNG_UST_ROOT_HANDLE 0 struct lttng_ust_obj; diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index cf74703c3..8fc0efdea 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -20,6 +20,13 @@ #include #include +#include +#include +#include +#include +#include +#include +#include #include #include "buffer-registry.h" @@ -34,6 +41,7 @@ #include "lttng-sessiond.h" #include "notification-thread-commands.h" #include "rotate.h" +#include "event.h" struct lttng_ht *ust_app_ht; struct lttng_ht *ust_app_ht_by_sock; @@ -313,6 +321,34 @@ void delete_ust_app_event(int sock, struct ust_app_event *ua_event, free(ua_event); } +/* + * Delete ust app token event_rule safely. RCU read lock must be held before calling + * this function. TODO: or does it???? + */ +static +void delete_ust_app_token_event_rule(int sock, struct ust_app_token_event_rule *ua_token, + struct ust_app *app) +{ + int ret; + + assert(ua_token); + + if (ua_token->exclusion != NULL) + free(ua_token->exclusion); + if (ua_token->obj != NULL) { + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_release_object(sock, ua_token->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app sock %d release event obj failed with ret %d", + sock, ret); + } + free(ua_token->obj); + } + lttng_event_rule_put(ua_token->event_rule); + free(ua_token); +} + /* * Release ust data object of the given stream. * @@ -921,6 +957,10 @@ void delete_ust_app(struct ust_app *app) ht_cleanup_push(app->ust_sessions_objd); ht_cleanup_push(app->ust_objd); + ustctl_release_object(sock, app->token_communication.handle); + + lttng_pipe_close(app->token_communication.trigger_event_pipe); + /* * Wait until we have deleted the application from the sock hash table * before closing this socket, otherwise an application could re-use the @@ -1019,7 +1059,7 @@ error_free: * Alloc new UST app channel. */ static -struct ust_app_channel *alloc_ust_app_channel(char *name, +struct ust_app_channel *alloc_ust_app_channel(const char *name, struct ust_app_session *ua_sess, struct lttng_ust_channel_attr *attr) { @@ -1125,6 +1165,41 @@ error: return NULL; } +/* + * Alloc new UST app token event rule. + */ +static struct ust_app_token_event_rule *alloc_ust_app_token_event_rule( + struct lttng_event_rule *event_rule, uint64_t token) +{ + struct ust_app_token_event_rule *ua_token; + + ua_token = zmalloc(sizeof(struct ust_app_token_event_rule)); + if (ua_token == NULL) { + PERROR("Failed to allocate ust_app_token_event_rule structure"); + goto error; + } + + ua_token->enabled = 1; + ua_token->token = token; + lttng_ht_node_init_u64(&ua_token->node, token); + + /* Get reference of the event_rule */ + if (!lttng_event_rule_get(event_rule)) { + assert(0); + } + + ua_token->event_rule = event_rule; + ua_token->filter = lttng_event_rule_get_filter_bytecode(event_rule); + ua_token->exclusion = lttng_event_rule_generate_exclusions(event_rule); + + DBG3("UST app token event rule %" PRIu64 " allocated", ua_token->token); + + return ua_token; + +error: + return NULL; +} + /* * Alloc new UST app context. */ @@ -1165,36 +1240,13 @@ error: return NULL; } -/* - * Allocate a filter and copy the given original filter. - * - * Return allocated filter or NULL on error. - */ -static struct lttng_filter_bytecode *copy_filter_bytecode( - struct lttng_filter_bytecode *orig_f) -{ - struct lttng_filter_bytecode *filter = NULL; - - /* Copy filter bytecode */ - filter = zmalloc(sizeof(*filter) + orig_f->len); - if (!filter) { - PERROR("zmalloc alloc filter bytecode"); - goto error; - } - - memcpy(filter, orig_f, sizeof(*filter) + orig_f->len); - -error: - return filter; -} - /* * Create a liblttng-ust filter bytecode from given bytecode. * * Return allocated filter or NULL on error. */ static struct lttng_ust_filter_bytecode *create_ust_bytecode_from_bytecode( - struct lttng_filter_bytecode *orig_f) + const struct lttng_filter_bytecode *orig_f) { struct lttng_ust_filter_bytecode *filter = NULL; @@ -1297,6 +1349,32 @@ end: return event; } +/* + * Lookup for an ust app tokens based on a token id. + * + * Return an ust_app_token_event_rule object or NULL on error. + */ +static struct ust_app_token_event_rule *find_ust_app_token_event_rule(struct lttng_ht *ht, + uint64_t token) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_u64 *node; + struct ust_app_token_event_rule *token_event_rule = NULL; + + assert(ht); + + lttng_ht_lookup(ht, &token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (node == NULL) { + DBG2("UST app token %" PRIu64 " not found", token); + goto end; + } + + token_event_rule = caa_container_of(node, struct ust_app_token_event_rule, node); +end: + return token_event_rule; +} + /* * Create the channel context on the tracer. * @@ -1343,33 +1421,28 @@ error: /* * Set the filter on the tracer. */ -static -int set_ust_event_filter(struct ust_app_event *ua_event, - struct ust_app *app) +static int set_ust_filter(struct ust_app *app, + const struct lttng_filter_bytecode *bytecode, + struct lttng_ust_object_data *ust_object) { int ret; struct lttng_ust_filter_bytecode *ust_bytecode = NULL; health_code_update(); - if (!ua_event->filter) { - ret = 0; - goto error; - } - - ust_bytecode = create_ust_bytecode_from_bytecode(ua_event->filter); + ust_bytecode = create_ust_bytecode_from_bytecode(bytecode); if (!ust_bytecode) { ret = -LTTNG_ERR_NOMEM; goto error; } pthread_mutex_lock(&app->sock_lock); ret = ustctl_set_filter(app->sock, ust_bytecode, - ua_event->obj); + ust_object); pthread_mutex_unlock(&app->sock_lock); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app event %s filter failed for app (pid: %d) " - "with ret %d", ua_event->attr.name, app->pid, ret); + ERR("UST app set filter failed for object %p of app (pid: %d) " + "with ret %d", ust_object, app->pid, ret); } else { /* * This is normal behavior, an application can die during the @@ -1377,12 +1450,12 @@ int set_ust_event_filter(struct ust_app_event *ua_event, * continue normally. */ ret = 0; - DBG3("UST app filter event failed. Application is dead."); + DBG3("UST app set filter. Application is dead."); } goto error; } - DBG2("UST filter set successfully for event %s", ua_event->name); + DBG2("UST filter set for object %p successfully", ust_object); error: health_code_update(); @@ -1414,33 +1487,30 @@ end: /* * Set event exclusions on the tracer. */ -static -int set_ust_event_exclusion(struct ust_app_event *ua_event, - struct ust_app *app) +static int set_ust_exclusions(struct ust_app *app, + struct lttng_event_exclusion *exclusions, + struct lttng_ust_object_data *ust_object) { int ret; - struct lttng_ust_event_exclusion *ust_exclusion = NULL; + struct lttng_ust_event_exclusion *ust_exclusions = NULL; - health_code_update(); + assert(exclusions && exclusions->count > 0); - if (!ua_event->exclusion || !ua_event->exclusion->count) { - ret = 0; - goto error; - } + health_code_update(); - ust_exclusion = create_ust_exclusion_from_exclusion( - ua_event->exclusion); - if (!ust_exclusion) { + ust_exclusions = create_ust_exclusion_from_exclusion( + exclusions); + if (!ust_exclusions) { ret = -LTTNG_ERR_NOMEM; goto error; } pthread_mutex_lock(&app->sock_lock); - ret = ustctl_set_exclusion(app->sock, ust_exclusion, ua_event->obj); + ret = ustctl_set_exclusion(app->sock, ust_exclusions, ust_object); pthread_mutex_unlock(&app->sock_lock); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app event %s exclusions failed for app (pid: %d) " - "with ret %d", ua_event->attr.name, app->pid, ret); + ERR("UST app exclusions failed for object %p of app (pid: %d) " + "with ret %d", ust_object, app->pid, ret); } else { /* * This is normal behavior, an application can die during the @@ -1448,37 +1518,36 @@ int set_ust_event_exclusion(struct ust_app_event *ua_event, * continue normally. */ ret = 0; - DBG3("UST app event exclusion failed. Application is dead."); + DBG3("UST app set exclusions failed. Application is dead."); } goto error; } - DBG2("UST exclusion set successfully for event %s", ua_event->name); + DBG2("UST exclusions set successfully for object %p", ust_object); error: health_code_update(); - free(ust_exclusion); + free(ust_exclusions); return ret; } /* * Disable the specified event on to UST tracer for the UST session. */ -static int disable_ust_event(struct ust_app *app, - struct ust_app_session *ua_sess, struct ust_app_event *ua_event) +static int disable_ust_object(struct ust_app *app, + struct lttng_ust_object_data *object) { int ret; health_code_update(); pthread_mutex_lock(&app->sock_lock); - ret = ustctl_disable(app->sock, ua_event->obj); + ret = ustctl_disable(app->sock, object); pthread_mutex_unlock(&app->sock_lock); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app event %s disable failed for app (pid: %d) " - "and session handle %d with ret %d", - ua_event->attr.name, app->pid, ua_sess->handle, ret); + ERR("UST app disable failed for object %p app (pid: %d) with ret %d", + object, app->pid, ret); } else { /* * This is normal behavior, an application can die during the @@ -1491,8 +1560,8 @@ static int disable_ust_event(struct ust_app *app, goto error; } - DBG2("UST app event %s disabled successfully for app (pid: %d)", - ua_event->attr.name, app->pid); + DBG2("UST app object %p disabled successfully for app (pid: %d)", + object, app->pid); error: health_code_update(); @@ -1580,21 +1649,19 @@ error: /* * Enable the specified event on to UST tracer for the UST session. */ -static int enable_ust_event(struct ust_app *app, - struct ust_app_session *ua_sess, struct ust_app_event *ua_event) +static int enable_ust_object(struct ust_app *app, struct lttng_ust_object_data *ust_object) { int ret; health_code_update(); pthread_mutex_lock(&app->sock_lock); - ret = ustctl_enable(app->sock, ua_event->obj); + ret = ustctl_enable(app->sock, ust_object); pthread_mutex_unlock(&app->sock_lock); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app event %s enable failed for app (pid: %d) " - "and session handle %d with ret %d", - ua_event->attr.name, app->pid, ua_sess->handle, ret); + ERR("UST app enable failed for object %p app (pid: %d) with ret %d", + ust_object, app->pid, ret); } else { /* * This is normal behavior, an application can die during the @@ -1602,13 +1669,13 @@ static int enable_ust_event(struct ust_app *app, * continue normally. */ ret = 0; - DBG3("UST app enable event failed. Application is dead."); + DBG3("UST app enable failed. Application is dead."); } goto error; } - DBG2("UST app event %s enabled successfully for app (pid: %d)", - ua_event->attr.name, app->pid); + DBG2("UST app object %p enabled successfully for app (pid: %d)", + ust_object, app->pid); error: health_code_update(); @@ -1704,14 +1771,14 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, ua_event->handle = ua_event->obj->handle; - DBG2("UST app event %s created successfully for pid:%d", - ua_event->attr.name, app->pid); + DBG2("UST app event %s created successfully for pid:%d object: %p", + ua_event->attr.name, app->pid, ua_event->obj); health_code_update(); /* Set filter if one is present. */ if (ua_event->filter) { - ret = set_ust_event_filter(ua_event, app); + ret = set_ust_filter(app, ua_event->filter, ua_event->obj); if (ret < 0) { goto error; } @@ -1719,7 +1786,7 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, /* Set exclusions for the event */ if (ua_event->exclusion) { - ret = set_ust_event_exclusion(ua_event, app); + ret = set_ust_exclusions(app, ua_event->exclusion, ua_event->obj); if (ret < 0) { goto error; } @@ -1731,7 +1798,7 @@ int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, * We now need to explicitly enable the event, since it * is now disabled at creation. */ - ret = enable_ust_event(app, ua_sess, ua_event); + ret = enable_ust_object(app, ua_event->obj); if (ret < 0) { /* * If we hit an EPERM, something is wrong with our enable call. If @@ -1758,6 +1825,159 @@ error: return ret; } +static +void init_ust_trigger_from_event_rule(const struct lttng_event_rule *rule, struct lttng_ust_trigger *trigger) +{ + enum lttng_event_rule_status status; + enum lttng_loglevel_type loglevel_type; + enum lttng_ust_loglevel_type ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + int loglevel = -1; + const char *pattern; + + /* For now only LTTNG_EVENT_RULE_TYPE_TRACEPOINT are supported */ + assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + + memset(trigger, 0, sizeof(*trigger)); + + if (lttng_event_rule_is_agent(rule)) { + /* + * Special event for agents + * The actual meat of the event is in the filter that will be + * attached later on. + * Set the default values for the agent event. + */ + pattern = event_get_default_agent_ust_name(lttng_event_rule_get_domain_type(rule)); + loglevel = 0; + ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + } else { + status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + /* At this point this is a fatal error */ + assert(0); + } + + status = lttng_event_rule_tracepoint_get_loglevel_type( + rule, &loglevel_type); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + /* At this point this is a fatal error */ + assert(0); + } + + switch (loglevel_type) { + case LTTNG_EVENT_LOGLEVEL_ALL: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + break; + case LTTNG_EVENT_LOGLEVEL_RANGE: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_RANGE; + break; + case LTTNG_EVENT_LOGLEVEL_SINGLE: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE; + break; + } + + if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + status = lttng_event_rule_tracepoint_get_loglevel( + rule, &loglevel); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + } + } + + trigger->instrumentation = LTTNG_UST_TRACEPOINT; + strncpy(trigger->name, pattern, LTTNG_UST_SYM_NAME_LEN - 1); + trigger->loglevel_type = ust_loglevel_type; + trigger->loglevel = loglevel; +} + +/* + * Create the specified event rule token onto the UST tracer for a UST app. + * + */ +static +int create_ust_token_event_rule(struct ust_app *app, struct ust_app_token_event_rule *ua_token) +{ + int ret = 0; + struct lttng_ust_trigger trigger; + + health_code_update(); + + init_ust_trigger_from_event_rule(ua_token->event_rule, &trigger); + trigger.id = ua_token->token; + + /* Create UST trigger on tracer */ + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_create_trigger(app->sock, &trigger, app->token_communication.handle, &ua_token->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + abort(); + ERR("Error ustctl create trigger %s for app pid: %d with ret %d", + trigger.name, app->pid, ret); + } else { + /* + * This is normal behavior, an application can die during the + * creation process. Don't report an error so the execution can + * continue normally. + */ + ret = 0; + DBG3("UST app create event failed. Application is dead."); + } + goto error; + } + + ua_token->handle = ua_token->obj->handle; + + DBG2("UST app event %s created successfully for pid:%d object: %p", + trigger.name, app->pid, ua_token->obj); + + health_code_update(); + + /* Set filter if one is present. */ + if (ua_token->filter) { + ret = set_ust_filter(app, ua_token->filter, ua_token->obj); + if (ret < 0) { + goto error; + } + } + + /* Set exclusions for the event */ + if (ua_token->exclusion) { + ret = set_ust_exclusions(app, ua_token->exclusion, ua_token->obj); + if (ret < 0) { + goto error; + } + } + + /* + * We now need to explicitly enable the event, since it + * is disabled at creation. + */ + ret = enable_ust_object(app, ua_token->obj); + if (ret < 0) { + /* + * If we hit an EPERM, something is wrong with our enable call. If + * we get an EEXIST, there is a problem on the tracer side since we + * just created it. + */ + switch (ret) { + case -LTTNG_UST_ERR_PERM: + /* Code flow problem */ + assert(0); + case -LTTNG_UST_ERR_EXIST: + /* It's OK for our use case. */ + ret = 0; + break; + default: + break; + } + goto error; + } + ua_token->enabled = true; + +error: + health_code_update(); + return ret; +} + /* * Copy data between an UST app event and a LTT event. */ @@ -2341,7 +2561,7 @@ int enable_ust_app_event(struct ust_app_session *ua_sess, { int ret; - ret = enable_ust_event(app, ua_sess, ua_event); + ret = enable_ust_object(app, ua_event->obj); if (ret < 0) { goto error; } @@ -2360,7 +2580,7 @@ static int disable_ust_app_event(struct ust_app_session *ua_sess, { int ret; - ret = disable_ust_event(app, ua_sess, ua_event); + ret = disable_ust_object(app, ua_event->obj); if (ret < 0) { goto error; } @@ -3175,6 +3395,57 @@ error: return ret; } +/* + * Create UST app event and create it on the tracer side. + * + * Called with ust app session mutex held. + */ +static +int create_ust_app_token_event_rule(struct lttng_event_rule *rule, + struct ust_app *app, uint64_t token) +{ + int ret = 0; + struct ust_app_token_event_rule *ua_token; + + ua_token = alloc_ust_app_token_event_rule(rule, token); + if (ua_token == NULL) { + ret = -ENOMEM; + goto end; + } + + /* Create it on the tracer side */ + ret = create_ust_token_event_rule(app, ua_token); + if (ret < 0) { + /* + * Not found previously means that it does not exist on the + * tracer. If the application reports that the event existed, + * it means there is a bug in the sessiond or lttng-ust + * (or corruption, etc.) + */ + if (ret == -LTTNG_UST_ERR_EXIST) { + ERR("Tracer for application reported that a token event rule being created already existed: " + "token = \"%" PRIu64 "\", pid = %d, ppid = %d, uid = %d, gid = %d", + token, + app->pid, app->ppid, app->uid, + app->gid); + } + goto error; + } + + lttng_ht_add_unique_u64(app->tokens_ht, &ua_token->node); + + DBG2("UST app create token event rule %" PRIu64 " for PID %d completed", token, + app->pid); + +end: + return ret; + +error: + /* Valid. Calling here is already in a read side lock */ + delete_ust_app_token_event_rule(-1, ua_token, app); + return ret; +} + /* * Create UST metadata and open it on the tracer side. * @@ -3319,6 +3590,7 @@ error: struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) { struct ust_app *lta = NULL; + struct lttng_pipe *trigger_event_source_pipe = NULL; assert(msg); assert(sock >= 0); @@ -3335,12 +3607,20 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) goto error; } + trigger_event_source_pipe = lttng_pipe_open(FD_CLOEXEC); + if (!trigger_event_source_pipe) { + PERROR("Open trigger pipe"); + goto error; + } + lta = zmalloc(sizeof(struct ust_app)); if (lta == NULL) { PERROR("malloc"); goto error; } + lta->token_communication.trigger_event_pipe = trigger_event_source_pipe; + lta->ppid = msg->ppid; lta->uid = msg->uid; lta->gid = msg->gid; @@ -3359,6 +3639,7 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); lta->ust_sessions_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); lta->notify_sock = -1; + lta->tokens_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); /* Copy name and make sure it's NULL terminated. */ strncpy(lta->name, msg->name, sizeof(lta->name)); @@ -3445,6 +3726,47 @@ int ust_app_version(struct ust_app *app) return ret; } +/* + * Setup the base trigger group. + * + * Return 0 on success else a negative value either an errno code or a + * LTTng-UST error code. + */ +int ust_app_setup_trigger_group(struct ust_app *app) +{ + int ret; + int writefd; + struct lttng_ust_object_data *group = NULL; + enum lttng_error_code lttng_ret; + + assert(app); + + /* Get the write side of the pipe */ + writefd = lttng_pipe_get_writefd(app->token_communication.trigger_event_pipe); + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_create_trigger_group(app->sock, writefd, &group); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + ERR("UST app %d create_trigger_group failed with ret %d", app->sock, ret); + goto end; + } + + app->token_communication.handle = group; + + lttng_ret = notification_thread_command_add_application( + notification_thread_handle, app->token_communication.trigger_event_pipe); + if (lttng_ret != LTTNG_OK) { + /* TODO: error */ + ret = - 1; + ERR("Failed to add channel to notification thread"); + goto end; + } + +end: + return ret; +} + /* * Unregister app by removing it from the global traceable app list and freeing * the data struct. @@ -3453,6 +3775,7 @@ int ust_app_version(struct ust_app *app) */ void ust_app_unregister(int sock) { + enum lttng_error_code ret_code; struct ust_app *lta; struct lttng_ht_node_ulong *node; struct lttng_ht_iter ust_app_sock_iter; @@ -3558,6 +3881,13 @@ void ust_app_unregister(int sock) lta->pid); } + ret_code = notification_thread_command_remove_application( + notification_thread_handle, + lta->token_communication.trigger_event_pipe); + if (ret_code != LTTNG_OK) { + ERR("Failed to remove application from notification thread"); + } + /* Free memory */ call_rcu(<a->pid_n.head, delete_ust_app_rcu); @@ -4994,6 +5324,123 @@ end: return ret; } +static +void ust_app_synchronize_tokens(struct ust_app *app) +{ + int ret = 0; + enum lttng_error_code ret_code; + enum lttng_trigger_status t_status; + struct lttng_ht_iter app_trigger_iter; + struct lttng_triggers *triggers; + struct ust_app_token_event_rule *token_event_rule_element; + unsigned int count; + + rcu_read_lock(); + /* TODO: is this necessary to protect against new trigger being added ? + * notification_trigger_tokens_ht is still the backing data structure + * for this listing. Leave it there for now. + */ + pthread_mutex_lock(¬ification_trigger_tokens_ht_lock); + ret_code = notification_thread_command_get_tokens( + notification_thread_handle, &triggers); + if (ret_code != LTTNG_OK) { + ret = -1; + goto end; + } + + assert(triggers); + + t_status = lttng_triggers_get_count(triggers, &count); + if (t_status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + goto end; + } + + for (unsigned int i = 0; i < count; i++) { + struct lttng_condition *condition; + struct lttng_event_rule *event_rule; + struct lttng_trigger *trigger; + struct ust_app_token_event_rule *ua_token; + uint64_t token; + + trigger = lttng_triggers_get_pointer_of_index(triggers, i); + assert(trigger); + + /* TODO: error checking and type checking */ + token = lttng_trigger_get_key(trigger); + condition = lttng_trigger_get_condition(trigger); + (void) lttng_condition_event_rule_get_rule_no_const(condition, &event_rule); + + if (lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL) { + /* Skip kernel related trigger */ + continue; + } + + /* Iterate over all known token trigger */ + ua_token = find_ust_app_token_event_rule(app->tokens_ht, token); + if (!ua_token) { + ret = create_ust_app_token_event_rule(event_rule, app, token); + if (ret < 0) { + goto end; + } + } + } + + /* Remove all unknown trigger from the app + * TODO find a way better way then this, do it on the unregister command + * and be specific on the token to remove instead of going over all + * trigger known to the app. This is sub optimal. + */ + cds_lfht_for_each_entry (app->tokens_ht->ht, &app_trigger_iter.iter, + token_event_rule_element, node.node) { + uint64_t token; + bool found = false; + + token = token_event_rule_element->token; + + /* + * Check if the app event trigger still exists on the + * notification side. + * TODO: might want to change the backing data struct of the + * lttng_triggers object to allow quick lookup? + * For kernel mostly all of this can be removed once we delete + * on a per trigger basis. + */ + + for (unsigned int i = 0; i < count; i++) { + struct lttng_trigger *trigger; + uint64_t inner_token; + + trigger = lttng_triggers_get_pointer_of_index( + triggers, i); + assert(trigger); + + inner_token = lttng_trigger_get_key(trigger); + + if (inner_token == token) { + found = true; + break; + } + } + + if (found) { + /* Still valid */ + continue; + } + + /* TODO: This is fucking ugly API for fuck sake */ + assert(!lttng_ht_del(app->tokens_ht, &app_trigger_iter)); + + (void) disable_ust_object(app, token_event_rule_element->obj); + + delete_ust_app_token_event_rule(app->sock, token_event_rule_element, app); + } +end: + rcu_read_unlock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + return; +} + /* * The caller must ensure that the application is compatible and is tracked * by the PID tracker. @@ -5132,6 +5579,16 @@ void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app) } } +void ust_app_global_update_tokens(struct ust_app *app) +{ + DBG2("UST app global update token for app sock %d", app->sock); + + if (!app->compatible) { + return; + } + ust_app_synchronize_tokens(app); +} + /* * Called with session lock held. */ @@ -5147,6 +5604,18 @@ void ust_app_global_update_all(struct ltt_ust_session *usess) rcu_read_unlock(); } +void ust_app_global_update_all_tokens(void) +{ + struct lttng_ht_iter iter; + struct ust_app *app; + + rcu_read_lock(); + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + ust_app_global_update_tokens(app); + } + rcu_read_unlock(); +} + /* * Add context to a specific channel for global UST domain. */ diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h index 6f3588a54..a24b42ffd 100644 --- a/src/bin/lttng-sessiond/ust-app.h +++ b/src/bin/lttng-sessiond/ust-app.h @@ -112,6 +112,19 @@ struct ust_app_event { struct lttng_event_exclusion *exclusion; }; +struct ust_app_token_event_rule { + int enabled; + int handle; + struct lttng_ust_object_data *obj; + struct lttng_event_rule *event_rule; + uint64_t token; + struct lttng_ht_node_u64 node; + /* The event_rule object own this pointer */ + const struct lttng_filter_bytecode *filter; + /* The event_rule object own this pointer */ + struct lttng_event_exclusion *exclusion; +}; + struct ust_app_stream { int handle; char pathname[PATH_MAX]; @@ -292,6 +305,14 @@ struct ust_app { * Used for path creation */ time_t registration_time; + /* + * Trigger + */ + struct { + struct lttng_ust_object_data *handle; + struct lttng_pipe *trigger_event_pipe; + } token_communication; + struct lttng_ht *tokens_ht; }; #ifdef HAVE_LIBLTTNG_UST_CTL @@ -319,6 +340,8 @@ int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx); void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app); void ust_app_global_update_all(struct ltt_ust_session *usess); +void ust_app_global_update_tokens(struct ust_app *app); +void ust_app_global_update_all_tokens(void); void ust_app_clean_list(void); int ust_app_ht_alloc(void); @@ -355,6 +378,8 @@ int ust_app_release_object(struct ust_app *app, struct lttng_ust_object_data *data); enum lttng_error_code ust_app_clear_session(struct ltt_session *session); +int ust_app_setup_trigger_group(struct ust_app *app); + static inline int ust_app_supported(void) { @@ -443,6 +468,17 @@ static inline void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app) {} static inline +void ust_app_global_update_tokens(struct ust_app *app) +{} +static inline +void ust_app_global_update_all_tokens(void) +{} +static inline +int ust_app_setup_trigger_group(struct ust_app *app) +{ + return 0; +} +static inline int ust_app_disable_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan) { diff --git a/src/bin/lttng-sessiond/ust-ctl-internal.h b/src/bin/lttng-sessiond/ust-ctl-internal.h index 96099f8aa..acb061382 100644 --- a/src/bin/lttng-sessiond/ust-ctl-internal.h +++ b/src/bin/lttng-sessiond/ust-ctl-internal.h @@ -85,6 +85,14 @@ int ustctl_disable(int sock, struct lttng_ust_object_data *object); int ustctl_start_session(int sock, int handle); int ustctl_stop_session(int sock, int handle); +int ustctl_create_trigger_group(int sock, + int pipe_fd, + struct lttng_ust_object_data **trigger_group_handle); +int ustctl_create_trigger(int sock, + struct lttng_ust_trigger *trigger, + struct lttng_ust_object_data *trigger_group_handle, + struct lttng_ust_object_data **trigger_data); + /* * ustctl_tracepoint_list returns a tracepoint list handle, or negative * error value. diff --git a/src/bin/lttng-sessiond/utils.c b/src/bin/lttng-sessiond/utils.c index f84859fc6..2ae586b51 100644 --- a/src/bin/lttng-sessiond/utils.c +++ b/src/bin/lttng-sessiond/utils.c @@ -98,3 +98,26 @@ const char *consumer_output_get_base_path(const struct consumer_output *output) output->dst.session_root_path : output->dst.net.base_dir; } + +/* + * Allocate a filter and copy the given original filter. + * + * Return allocated filter or NULL on error. + */ +struct lttng_filter_bytecode *copy_filter_bytecode( + const struct lttng_filter_bytecode *orig_f) +{ + struct lttng_filter_bytecode *filter = NULL; + + /* Copy filter bytecode */ + filter = zmalloc(sizeof(*filter) + orig_f->len); + if (!filter) { + PERROR("zmalloc alloc filter bytecode"); + goto error; + } + + memcpy(filter, orig_f, sizeof(*filter) + orig_f->len); + +error: + return filter; +} diff --git a/src/bin/lttng-sessiond/utils.h b/src/bin/lttng-sessiond/utils.h index ff8deaec4..d5e506610 100644 --- a/src/bin/lttng-sessiond/utils.h +++ b/src/bin/lttng-sessiond/utils.h @@ -19,5 +19,7 @@ int loglevels_match(int a_loglevel_type, int a_loglevel_value, int b_loglevel_type, int b_loglevel_value, int loglevel_all_type); const char *session_get_base_path(const struct ltt_session *session); const char *consumer_output_get_base_path(const struct consumer_output *output); +struct lttng_filter_bytecode *copy_filter_bytecode( + const struct lttng_filter_bytecode *orig_f); #endif /* _LTT_UTILS_H */ diff --git a/src/bin/lttng/Makefile.am b/src/bin/lttng/Makefile.am index d094c5aaf..96d7114a2 100644 --- a/src/bin/lttng/Makefile.am +++ b/src/bin/lttng/Makefile.am @@ -29,7 +29,11 @@ lttng_SOURCES = command.h conf.c conf.h commands/start.c \ commands/enable_rotation.c \ commands/disable_rotation.c \ commands/clear.c \ - utils.c utils.h lttng.c + commands/add_trigger.c \ + commands/list_triggers.c \ + commands/remove_trigger.c \ + utils.c utils.h lttng.c \ + uprobe.c uprobe.h lttng_CFLAGS = $(AM_CFLAGS) $(POPT_CFLAGS) @@ -37,4 +41,6 @@ lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ $(top_builddir)/src/common/libcommon.la \ $(top_builddir)/src/common/config/libconfig.la \ $(top_builddir)/src/common/string-utils/libstring-utils.la \ + $(top_builddir)/src/common/filter/libfilter.la \ + $(top_builddir)/src/common/argpar/libargpar.la \ $(POPT_LIBS) diff --git a/src/bin/lttng/command.h b/src/bin/lttng/command.h index 8f1c7be40..bf0045210 100644 --- a/src/bin/lttng/command.h +++ b/src/bin/lttng/command.h @@ -77,6 +77,9 @@ DECL_COMMAND(rotate); DECL_COMMAND(enable_rotation); DECL_COMMAND(disable_rotation); DECL_COMMAND(clear); +DECL_COMMAND(add_trigger); +DECL_COMMAND(list_triggers); +DECL_COMMAND(remove_trigger); extern int cmd_help(int argc, const char **argv, const struct cmd_struct commands[]); diff --git a/src/bin/lttng/commands/add_context.c b/src/bin/lttng/commands/add_context.c index 412202572..994cd49eb 100644 --- a/src/bin/lttng/commands/add_context.c +++ b/src/bin/lttng/commands/add_context.c @@ -184,19 +184,19 @@ static struct poptOption long_options[] = { */ #define PERF_HW(optstr, name, type, hide) \ { \ - optstr, type, hide, \ + (char *) optstr, type, hide, \ .u.perf = { PERF_TYPE_HARDWARE, PERF_COUNT_HW_##name, },\ } #define PERF_SW(optstr, name, type, hide) \ { \ - optstr, type, hide, \ + (char *) optstr, type, hide, \ .u.perf = { PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##name, },\ } #define _PERF_HW_CACHE(optstr, name, type, op, result, hide) \ { \ - optstr, type, hide, \ + (char *) optstr, type, hide, \ .u.perf = { \ PERF_TYPE_HW_CACHE, \ (uint64_t) PERF_COUNT_HW_CACHE_##name \ @@ -235,45 +235,49 @@ const struct ctx_opts { } app_ctx; } u; } ctx_opts[] = { - { "pid", CONTEXT_PID }, - { "procname", CONTEXT_PROCNAME }, - { "prio", CONTEXT_PRIO }, - { "nice", CONTEXT_NICE }, - { "vpid", CONTEXT_VPID }, - { "tid", CONTEXT_TID }, - { "pthread_id", CONTEXT_PTHREAD_ID }, - { "vtid", CONTEXT_VTID }, - { "ppid", CONTEXT_PPID }, - { "vppid", CONTEXT_VPPID }, - { "hostname", CONTEXT_HOSTNAME }, - { "ip", CONTEXT_IP }, - { "interruptible", CONTEXT_INTERRUPTIBLE }, - { "preemptible", CONTEXT_PREEMPTIBLE }, - { "need_reschedule", CONTEXT_NEED_RESCHEDULE }, - { "migratable", CONTEXT_MIGRATABLE }, - { "callstack-kernel", CONTEXT_CALLSTACK_KERNEL }, + /* + * These (char *) casts (as well as those in the PERF_* macros) are + * safe because we never free these instances of `struct ctx_opts`. + */ + { (char *) "pid", CONTEXT_PID }, + { (char *) "procname", CONTEXT_PROCNAME }, + { (char *) "prio", CONTEXT_PRIO }, + { (char *) "nice", CONTEXT_NICE }, + { (char *) "vpid", CONTEXT_VPID }, + { (char *) "tid", CONTEXT_TID }, + { (char *) "pthread_id", CONTEXT_PTHREAD_ID }, + { (char *) "vtid", CONTEXT_VTID }, + { (char *) "ppid", CONTEXT_PPID }, + { (char *) "vppid", CONTEXT_VPPID }, + { (char *) "hostname", CONTEXT_HOSTNAME }, + { (char *) "ip", CONTEXT_IP }, + { (char *) "interruptible", CONTEXT_INTERRUPTIBLE }, + { (char *) "preemptible", CONTEXT_PREEMPTIBLE }, + { (char *) "need_reschedule", CONTEXT_NEED_RESCHEDULE }, + { (char *) "migratable", CONTEXT_MIGRATABLE }, + { (char *) "callstack-kernel", CONTEXT_CALLSTACK_KERNEL }, #if HAVE_MODULES_USERSPACE_CALLSTACK_CONTEXT - { "callstack-user", CONTEXT_CALLSTACK_USER }, + { (char *) "callstack-user", CONTEXT_CALLSTACK_USER }, #endif - { "cgroup_ns", CONTEXT_CGROUP_NS }, - { "ipc_ns", CONTEXT_IPC_NS }, - { "mnt_ns", CONTEXT_MNT_NS }, - { "net_ns", CONTEXT_NET_NS }, - { "pid_ns", CONTEXT_PID_NS }, - { "user_ns", CONTEXT_USER_NS }, - { "uts_ns", CONTEXT_UTS_NS }, - { "uid", CONTEXT_UID }, - { "euid", CONTEXT_EUID }, - { "suid", CONTEXT_SUID }, - { "gid", CONTEXT_GID }, - { "egid", CONTEXT_EGID }, - { "sgid", CONTEXT_SGID }, - { "vuid", CONTEXT_VUID }, - { "veuid", CONTEXT_VEUID }, - { "vsuid", CONTEXT_VSUID }, - { "vgid", CONTEXT_VGID }, - { "vegid", CONTEXT_VEGID }, - { "vsgid", CONTEXT_VSGID }, + { (char *) "cgroup_ns", CONTEXT_CGROUP_NS }, + { (char *) "ipc_ns", CONTEXT_IPC_NS }, + { (char *) "mnt_ns", CONTEXT_MNT_NS }, + { (char *) "net_ns", CONTEXT_NET_NS }, + { (char *) "pid_ns", CONTEXT_PID_NS }, + { (char *) "user_ns", CONTEXT_USER_NS }, + { (char *) "uts_ns", CONTEXT_UTS_NS }, + { (char *) "uid", CONTEXT_UID }, + { (char *) "euid", CONTEXT_EUID }, + { (char *) "suid", CONTEXT_SUID }, + { (char *) "gid", CONTEXT_GID }, + { (char *) "egid", CONTEXT_EGID }, + { (char *) "sgid", CONTEXT_SGID }, + { (char *) "vuid", CONTEXT_VUID }, + { (char *) "veuid", CONTEXT_VEUID }, + { (char *) "vsuid", CONTEXT_VSUID }, + { (char *) "vgid", CONTEXT_VGID }, + { (char *) "vegid", CONTEXT_VEGID }, + { (char *) "vsgid", CONTEXT_VSGID }, /* Perf options */ diff --git a/src/bin/lttng/commands/add_trigger.c b/src/bin/lttng/commands/add_trigger.c new file mode 100644 index 000000000..e0546e17e --- /dev/null +++ b/src/bin/lttng/commands/add_trigger.c @@ -0,0 +1,1727 @@ +#include + +#include "../command.h" +#include "../uprobe.h" + +#include "common/argpar/argpar.h" +#include "common/dynamic-array.h" +#include "common/string-utils/string-utils.h" +#include "common/utils.h" +#include "lttng/condition/event-rule.h" +#include "lttng/event-internal.h" +#include +#include "lttng/event-rule/kprobe.h" +#include "lttng/event-rule/syscall.h" +#include +#include "lttng/event-rule/uprobe.h" + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_LIST_OPTIONS, + + OPT_CONDITION, + OPT_ACTION, + OPT_ID, + OPT_FIRE_ONCE_AFTER, + OPT_FIRE_EVERY, + + OPT_ALL, + OPT_FILTER, + OPT_EXCLUDE, + OPT_LOGLEVEL, + OPT_LOGLEVEL_ONLY, + + OPT_USERSPACE, + OPT_KERNEL, + OPT_LOG4J, + OPT_JUL, + OPT_PYTHON, + + OPT_FUNCTION, + OPT_PROBE, + OPT_USERSPACE_PROBE, + OPT_SYSCALL, + OPT_TRACEPOINT, + + OPT_NAME, + OPT_MAX_SIZE, + OPT_DATA_URL, + OPT_CTRL_URL, +}; + +static const struct argpar_opt_descr event_rule_opt_descrs[] = { + { OPT_ALL, 'a', "all", false }, + { OPT_FILTER, 'f', "filter", true }, + { OPT_EXCLUDE, 'x', "exclude", true }, + { OPT_LOGLEVEL, '\0', "loglevel", true }, + { OPT_LOGLEVEL_ONLY, '\0', "loglevel-only", true }, + + /* Domains */ + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + { OPT_LOG4J, 'l', "log4j", false }, + { OPT_JUL, 'j', "jul", false }, + { OPT_PYTHON, 'p', "python", false }, + + /* Event rule types */ + { OPT_FUNCTION, '\0', "function", true }, + { OPT_PROBE, '\0', "probe", true }, + { OPT_USERSPACE_PROBE, '\0', "userspace-probe", true }, + { OPT_SYSCALL, '\0', "syscall" }, + { OPT_TRACEPOINT, '\0', "tracepoint" }, + + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +bool assign_domain_type(enum lttng_domain_type *dest, + enum lttng_domain_type src) +{ + bool ret; + + if (*dest == LTTNG_DOMAIN_NONE || *dest == src) { + *dest = src; + ret = true; + } else { + fprintf(stderr, "Error: Multiple domains specified.\n"); + ret = false; + } + + return ret; +} + +static +bool assign_event_rule_type(enum lttng_event_rule_type *dest, + enum lttng_event_rule_type src) +{ + bool ret; + + if (*dest == LTTNG_EVENT_RULE_TYPE_UNKNOWN || *dest == src) { + *dest = src; + ret = true; + } else { + fprintf(stderr, "Error: Multiple event type not supported.\n"); + ret = false; + } + + return ret; +} + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + fprintf(stderr, + "Duplicate %s given.\n", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + fprintf(stderr, "Failed to allocate %s string.\n", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +/* This is defined in enable_events.c. */ +LTTNG_HIDDEN +int create_exclusion_list_and_validate(const char *event_name, + const char *exclusions_arg, + char ***exclusion_list); + +/* + * Parse `str` as a log level in domain `domain_type`. Return -1 if the string + * is not recognized as a valid log level. + */ +static +int parse_loglevel_string(const char *str, enum lttng_domain_type domain_type) +{ + + switch (domain_type) { + case LTTNG_DOMAIN_UST: + return loglevel_str_to_value(str); + + case LTTNG_DOMAIN_LOG4J: + return loglevel_log4j_str_to_value(str); + + case LTTNG_DOMAIN_JUL: + return loglevel_jul_str_to_value(str); + + case LTTNG_DOMAIN_PYTHON: + return loglevel_python_str_to_value(str); + + default: + /* Invalid domain type. */ + abort(); + } +} + +static +struct lttng_event_rule *parse_event_rule(int *argc, const char ***argv) +{ + struct lttng_event_rule *er = NULL; + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + enum lttng_event_rule_type event_rule_type = LTTNG_EVENT_RULE_TYPE_UNKNOWN; + struct argpar_state *state; + struct argpar_item *item = NULL; + char *error = NULL; + int consumed_args = -1; + struct lttng_userspace_probe_location *userspace_probe_location = NULL; + + /* Was the -a/--all flag provided? */ + bool all_events = false; + + /* Tracepoint name (non-option argument) */ + const char *tracepoint_name = NULL; + + /* Holds the argument of --probe / --userspace-probe. */ + char *source = NULL; + + /* Filter */ + char *filter = NULL; + + /* Exclude */ + char *exclude = NULL; + char **exclusion_list = NULL; + + /* Log level */ + char *loglevel_str = NULL; + bool loglevel_only = false; + + state = argpar_state_create(*argc, *argv, event_rule_opt_descrs); + if (!state) { + fprintf(stderr, "Failed to allocate an argpar state.\n"); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + fprintf(stderr, "Error: %s\n", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + /* Domains */ + case OPT_USERSPACE: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_UST)) { + goto error; + } + break; + + case OPT_KERNEL: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_KERNEL)) { + goto error; + } + break; + + case OPT_LOG4J: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_LOG4J)) { + goto error; + } + break; + + case OPT_JUL: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_JUL)) { + goto error; + } + break; + + case OPT_PYTHON: + if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_PYTHON)) { + goto error; + } + break; + + /* Event rule types */ + case OPT_FUNCTION: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_KRETPROBE)) { + goto error; + } + break; + + case OPT_PROBE: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_KPROBE)) { + goto error; + } + + if (!assign_string(&source, item_opt->arg, "source")) { + goto error; + } + + break; + + case OPT_USERSPACE_PROBE: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_UPROBE)) { + goto error; + } + + if (!assign_string(&source, item_opt->arg, "source")) { + goto error; + } + break; + + case OPT_SYSCALL: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_SYSCALL)) { + goto error; + } + break; + + case OPT_TRACEPOINT: + if (!assign_event_rule_type(&event_rule_type, + LTTNG_EVENT_RULE_TYPE_TRACEPOINT)) { + goto error; + } + break; + + case OPT_ALL: + all_events = true; + break; + + case OPT_FILTER: + if (!assign_string(&filter, item_opt->arg, "--filter/-f")) { + goto error; + } + break; + + case OPT_EXCLUDE: + if (!assign_string(&exclude, item_opt->arg, "--exclude/-x")) { + goto error; + } + break; + + case OPT_LOGLEVEL: + case OPT_LOGLEVEL_ONLY: + if (!assign_string(&loglevel_str, item_opt->arg, "--loglevel/--loglevel-only")) { + goto error; + } + + loglevel_only = item_opt->descr->id == OPT_LOGLEVEL_ONLY; + break; + + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + /* + * Don't accept two non-option arguments/tracepoint + * names. + */ + if (tracepoint_name) { + fprintf(stderr, + "Error: Unexpected argument: %s\n", + item_non_opt->arg); + goto error; + } + + tracepoint_name = item_non_opt->arg; + } + } + + if (event_rule_type == LTTNG_EVENT_RULE_TYPE_UNKNOWN) { + event_rule_type = LTTNG_EVENT_RULE_TYPE_TRACEPOINT; + } + + /* + * Option -a is applicable to event rules of type tracepoint and + * syscall, and it is equivalent to using "*" as the tracepoint name. + */ + if (all_events) { + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + break; + default: + fprintf(stderr, "Error: Can't use -a/--all with event rule of type %s.\n", + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + + if (tracepoint_name) { + fprintf(stderr, "Error: Can't provide a tracepoint name with -a/--all.\n"); + goto error; + } + + /* In which case, it's equivalent to tracepoint name "*". */ + tracepoint_name = "*"; + } + + /* + * A tracepoint name (or -a, for the event rule types that accept it) + * is required. + */ + if (!tracepoint_name) { + fprintf(stderr, "Error: Need to provide either a tracepoint name or -a/--all.\n"); + goto error; + } + + /* + * We don't support multiple tracepoint names for now. + */ + if (strchr(tracepoint_name, ',')) { + fprintf(stderr, "Error: multiple tracepoint names are not supported at the moment.\n"); + goto error; + } + + /* + * Update *argc and *argv so our caller can keep parsing what follows. + */ + consumed_args = argpar_state_get_ingested_orig_args(state); + assert(consumed_args >= 0); + *argc -= consumed_args; + *argv += consumed_args; + + /* Need to specify a domain. */ + if (domain_type == LTTNG_DOMAIN_NONE) { + fprintf(stderr, "Error: Please specify a domain (-k/-u/-j).\n"); + goto error; + } + + /* Validate event rule type against domain. */ + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + if (domain_type != LTTNG_DOMAIN_KERNEL) { + fprintf(stderr, "Error: Event type not available for user-space tracing\n"); + goto error; + } + break; + + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + break; + + default: + abort(); + } + + /* + * Adding a filter to a probe, function or userspace-probe would be + * denied by the kernel tracer as it's not supported at the moment. We + * do an early check here to warn the user. + */ + if (filter && domain_type == LTTNG_DOMAIN_KERNEL) { + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + break; + default: + ERR("Filter expressions are not supported for %s events", + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + } + + /* If --exclude/-x was passed, split it into an exclusion list. */ + if (exclude) { + if (domain_type != LTTNG_DOMAIN_UST) { + fprintf(stderr, "Event name exclusions are not yet implemented for %s events", + get_domain_str(domain_type)); + goto error; + } + + + if (create_exclusion_list_and_validate(tracepoint_name, exclude, + &exclusion_list) != 0) { + fprintf(stderr, "Failed to create exclusion list.\n"); + goto error; + } + } + + if (loglevel_str && event_rule_type != LTTNG_EVENT_RULE_TYPE_TRACEPOINT) { + fprintf(stderr, "Log levels are only application to tracepoint event rules.\n"); + goto error; + } + + /* Finally, create the event rule object. */ + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + { + enum lttng_event_rule_status event_rule_status; + + er = lttng_event_rule_tracepoint_create(domain_type); + if (!er) { + fprintf(stderr, "Failed to create tracepoint event rule.\n"); + goto error; + } + + /* Set pattern. */ + event_rule_status = + lttng_event_rule_tracepoint_set_pattern(er, tracepoint_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set tracepoint pattern.\n"); + goto error; + } + + /* Set filter. */ + if (filter) { + event_rule_status = + lttng_event_rule_tracepoint_set_filter( + er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set tracepoint filter expression.\n"); + goto error; + } + } + + /* Set exclusion list. */ + if (exclusion_list) { + int n; + + /* Count number of items in exclusion list. */ + for (n = 0; exclusion_list[n]; n++); + + event_rule_status = + lttng_event_rule_tracepoint_set_exclusions(er, + n, (const char **) exclusion_list); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set tracepoint exclusion list.\n"); + goto error; + } + } + + if (loglevel_str) { + int loglevel; + + if (domain_type == LTTNG_DOMAIN_KERNEL) { + fprintf(stderr, "Log levels are not supported by the kernel tracer.\n"); + goto error; + } + + loglevel = parse_loglevel_string( + loglevel_str, domain_type); + if (loglevel < 0) { + fprintf(stderr, "Failed to parse `%s` as a log level.\n", loglevel_str); + goto error; + } + + if (loglevel_only) { + event_rule_status = + lttng_event_rule_tracepoint_set_loglevel( + er, loglevel); + } else { + event_rule_status = + lttng_event_rule_tracepoint_set_loglevel_range( + er, loglevel); + } + + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set log level.\n"); + goto error; + } + } + + break; + } + + case LTTNG_EVENT_RULE_TYPE_KPROBE: + { + enum lttng_event_rule_status event_rule_status; + + er = lttng_event_rule_kprobe_create(); + if (!er) { + fprintf(stderr, "Failed to create kprobe event rule.\n"); + goto error; + } + + event_rule_status = lttng_event_rule_kprobe_set_name(er, tracepoint_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set kprobe event rule's name.\n"); + goto error; + } + + assert(source); + event_rule_status = lttng_event_rule_kprobe_set_source(er, source); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set kprobe event rule's source.\n"); + goto error; + } + + break; + } + + case LTTNG_EVENT_RULE_TYPE_UPROBE: + { + int ret; + enum lttng_event_rule_status event_rule_status; + + ret = parse_userspace_probe_opts(source, &userspace_probe_location); + if (ret) { + fprintf(stderr, "Failed to parse userspace probe location.\n"); + goto error; + } + + er = lttng_event_rule_uprobe_create(); + if (!er) { + fprintf(stderr, "Failed to create userspace probe event rule.\n"); + goto error; + } + + event_rule_status = lttng_event_rule_uprobe_set_location(er, userspace_probe_location); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set userspace probe event rule's location.\n"); + goto error; + } + + event_rule_status = lttng_event_rule_uprobe_set_name(er, tracepoint_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set userspace probe event rule's name.\n"); + goto error; + } + + break; + } + + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + { + enum lttng_event_rule_status event_rule_status; + + er = lttng_event_rule_syscall_create(); + if (!er) { + fprintf(stderr, "Failed to create syscall event rule.\n"); + goto error; + } + + event_rule_status = lttng_event_rule_syscall_set_pattern(er, tracepoint_name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set syscall event rule's pattern.\n"); + goto error; + } + + if (filter) { + event_rule_status = lttng_event_rule_syscall_set_filter( + er, filter); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to set syscall event rule's filter expression.\n"); + goto error; + } + } + + break; + } + + default: + fprintf(stderr, "%s: I don't support event rules of type `%s` at the moment.\n", __func__, + lttng_event_rule_type_str(event_rule_type)); + goto error; + } + + goto end; + +error: + lttng_event_rule_destroy(er); + er = NULL; + +end: + argpar_item_destroy(item); + free(error); + argpar_state_destroy(state); + free(filter); + free(exclude); + free(loglevel_str); + strutils_free_null_terminated_array_of_strings(exclusion_list); + lttng_userspace_probe_location_destroy(userspace_probe_location); + return er; +} + +static +struct lttng_condition *handle_condition_event(int *argc, const char ***argv) +{ + struct lttng_event_rule *er; + struct lttng_condition *c; + + er = parse_event_rule(argc, argv); + if (!er) { + c = NULL; + goto end; + } + + c = lttng_condition_event_rule_create(er); + if (!c) { + goto end; + } + +end: + return c; +} + +static +struct lttng_condition *handle_condition_session_consumed_size(int *argc, const char ***argv) +{ + struct lttng_condition *cond = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *threshold_arg = NULL; + const char *session_name_arg = NULL; + uint64_t threshold; + char *error = NULL; + enum lttng_condition_status condition_status; + + state = argpar_state_create(*argc, *argv, event_rule_opt_descrs); + if (!state) { + fprintf(stderr, "Failed to allocate an argpar state.\n"); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + fprintf(stderr, "Error: %s\n", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt; + + assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + case 1: + threshold_arg = item_non_opt->arg; + break; + default: + fprintf(stderr, "Unexpected argument `%s`.\n", + item_non_opt->arg); + goto error; + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + fprintf(stderr, "Missing session name argument.\n"); + goto error; + } + + if (!threshold_arg) { + fprintf(stderr, "Missing threshold argument.\n"); + goto error; + } + + if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) { + fprintf(stderr, "Failed to parse `%s` as a size.\n", threshold_arg); + goto error; + } + + cond = lttng_condition_session_consumed_size_create(); + if (!cond) { + fprintf(stderr, "Failed to allocate a session consumed size condition.\n"); + goto error; + } + + condition_status = lttng_condition_session_consumed_size_set_session_name( + cond, session_name_arg); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fprintf(stderr, "Failed to set session consumed size condition session name.\n"); + goto error; + } + + + condition_status = lttng_condition_session_consumed_size_set_threshold( + cond, threshold); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fprintf(stderr, "Failed to set session consumed size condition threshold.\n"); + goto error; + } + + goto end; + +error: + lttng_condition_destroy(cond); + cond = NULL; + +end: + argpar_state_destroy(state); + argpar_item_destroy(item); + free(error); + return cond; +} + +static +struct lttng_condition *handle_condition_buffer_usage_high(int *argc, const char ***argv) +{ + struct lttng_condition *cond = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *threshold_arg = NULL; + const char *session_name_arg = NULL; + uint64_t threshold; + char *error = NULL; + enum lttng_condition_status condition_status; + + state = argpar_state_create(*argc, *argv, event_rule_opt_descrs); + if (!state) { + fprintf(stderr, "Failed to allocate an argpar state.\n"); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + fprintf(stderr, "Error: %s\n", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt; + + assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + case 1: + threshold_arg = item_non_opt->arg; + break; + default: + fprintf(stderr, "Unexpected argument `%s`.\n", + item_non_opt->arg); + goto error; + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + fprintf(stderr, "Missing session name argument.\n"); + goto error; + } + + if (!threshold_arg) { + fprintf(stderr, "Missing threshold argument.\n"); + goto error; + } + + if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) { + fprintf(stderr, "Failed to parse `%s` as a size.\n", threshold_arg); + goto error; + } + + cond = lttng_condition_session_consumed_size_create(); + if (!cond) { + fprintf(stderr, "Failed to allocate a session consumed size condition.\n"); + goto error; + } + + condition_status = lttng_condition_session_consumed_size_set_session_name( + cond, session_name_arg); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fprintf(stderr, "Failed to set session consumed size condition session name.\n"); + goto error; + } + + condition_status = lttng_condition_session_consumed_size_set_threshold( + cond, threshold); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fprintf(stderr, "Failed to set session consumed size condition threshold.\n"); + goto error; + } + + goto end; + +error: + lttng_condition_destroy(cond); + cond = NULL; + +end: + argpar_state_destroy(state); + argpar_item_destroy(item); + free(error); + return cond; +} + +static +struct lttng_condition *handle_condition_buffer_usage_low(int *argc, const char ***argv) +{ + return NULL; +} + +static +struct lttng_condition *handle_condition_session_rotation_ongoing(int *argc, const char ***argv) +{ + return NULL; +} + +static +struct lttng_condition *handle_condition_session_rotation_completed(int *argc, const char ***argv) +{ + return NULL; +} + +struct condition_descr { + const char *name; + struct lttng_condition *(*handler) (int *argc, const char ***argv); +}; + +static const +struct condition_descr condition_descrs[] = { + { "on-event", handle_condition_event }, + { "on-session-consumed-size", handle_condition_session_consumed_size }, + { "on-buffer-usage-high", handle_condition_buffer_usage_high }, + { "on-buffer-usage-low", handle_condition_buffer_usage_low }, + { "on-session-rotation-ongoing", handle_condition_session_rotation_ongoing }, + { "on-session-rotation-completed", handle_condition_session_rotation_completed }, +}; + +static +struct lttng_condition *parse_condition(int *argc, const char ***argv) +{ + int i; + struct lttng_condition *cond; + const char *condition_name; + const struct condition_descr *descr = NULL; + + if (*argc == 0) { + fprintf(stderr, "Missing condition name.\n"); + goto error; + } + + condition_name = (*argv)[0]; + + (*argc)--; + (*argv)++; + + for (i = 0; i < ARRAY_SIZE(condition_descrs); i++) { + if (strcmp(condition_name, condition_descrs[i].name) == 0) { + descr = &condition_descrs[i]; + break; + } + } + + if (!descr) { + fprintf(stderr, "Unknown condition name: %s\n", condition_name); + goto error; + } + + cond = descr->handler(argc, argv); + if (!cond) { + /* The handler has already printed an error message. */ + goto error; + } + + goto end; +error: + cond = NULL; +end: + return cond; +} + + +static +struct lttng_action *handle_action_notify(int *argc, const char ***argv) +{ + return lttng_action_notify_create(); +} + +static const struct argpar_opt_descr no_opt_descrs[] = { + ARGPAR_OPT_DESCR_SENTINEL +}; + +/* + * Generic handler for a kind of action that takes a session name as its sole + * argument. + */ + +static +struct lttng_action *handle_action_simple_session( + int *argc, const char ***argv, + struct lttng_action *(*create_action_cb)(void), + enum lttng_action_status (*set_session_name_cb)(struct lttng_action *, const char *), + const char *action_name) +{ + struct lttng_action *action = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *session_name_arg = NULL; + char *error = NULL; + enum lttng_action_status action_status; + + state = argpar_state_create(*argc, *argv, no_opt_descrs); + if (!state) { + fprintf(stderr, "Failed to allocate an argpar state.\n"); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + struct argpar_item_non_opt *item_non_opt; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + fprintf(stderr, "Error: %s\n", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + default: + fprintf(stderr, "Unexpected argument `%s`.\n", + item_non_opt->arg); + goto error; + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + fprintf(stderr, "Missing session name.\n"); + goto error; + } + + action = create_action_cb(); + if (!action) { + fprintf(stderr, + "Failed to allocate %s session action.\n", action_name); + goto error; + } + + action_status = set_session_name_cb(action, session_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + fprintf(stderr, + "Failed to set action %s session's session name.\n", + action_name); + goto error; + } + + goto end; + +error: + lttng_action_destroy(action); + action = NULL; + +end: + return action; +} + +static +struct lttng_action *handle_action_start_session(int *argc, + const char ***argv) +{ + return handle_action_simple_session(argc, argv, + lttng_action_start_session_create, + lttng_action_start_session_set_session_name, + "start"); +} + +static +struct lttng_action *handle_action_stop_session(int *argc, + const char ***argv) +{ + return handle_action_simple_session(argc, argv, + lttng_action_stop_session_create, + lttng_action_stop_session_set_session_name, + "stop"); +} + +static +struct lttng_action *handle_action_rotate_session(int *argc, + const char ***argv) +{ + return handle_action_simple_session(argc, argv, + lttng_action_rotate_session_create, + lttng_action_rotate_session_set_session_name, + "rotate"); +} + +static const struct argpar_opt_descr snapshot_action_opt_descrs[] = { + { OPT_NAME, 'n', "name", true }, + { OPT_MAX_SIZE, 'm', "max-size", true }, + { OPT_CTRL_URL, '\0', "ctrl-url", true }, + { OPT_DATA_URL, '\0', "data-url", true }, + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +struct lttng_action *handle_action_snapshot_session(int *argc, + const char ***argv) +{ + struct lttng_action *action = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + const char *session_name_arg = NULL; + char *snapshot_name_arg = NULL; + char *ctrl_url_arg = NULL; + char *data_url_arg = NULL; + char *max_size_arg = NULL; + const char *url_arg = NULL; + char *error = NULL; + enum lttng_action_status action_status; + struct lttng_snapshot_output *snapshot_output = NULL; + int ret; + + state = argpar_state_create(*argc, *argv, snapshot_action_opt_descrs); + if (!state) { + fprintf(stderr, "Failed to allocate an argpar state.\n"); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + fprintf(stderr, "Error: %s\n", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_NAME: + if (!assign_string(&snapshot_name_arg, item_opt->arg, "--name/-n")) { + goto error; + } + break; + + case OPT_MAX_SIZE: + if (!assign_string(&max_size_arg, item_opt->arg, "--max-size/-m")) { + goto error; + } + break; + + case OPT_CTRL_URL: + if (!assign_string(&ctrl_url_arg, item_opt->arg, "--ctrl-url")) { + goto error; + } + break; + + case OPT_DATA_URL: + if (!assign_string(&data_url_arg, item_opt->arg, "--data-url")) { + goto error; + } + break; + + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt; + + assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT); + + item_non_opt = (struct argpar_item_non_opt *) item; + + switch (item_non_opt->non_opt_index) { + case 0: + session_name_arg = item_non_opt->arg; + break; + + // FIXME: the use of a non-option argument for this is to + // follow the syntax of `lttng snapshot record`. But otherwise, + // I think an option argument would be best. + case 1: + url_arg = item_non_opt->arg; + break; + + default: + fprintf(stderr, "Unexpected argument `%s`.\n", + item_non_opt->arg); + goto error; + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + fprintf(stderr, "Missing session name.\n"); + goto error; + } + + /* --ctrl-url and --data-url must come in pair. */ + if (ctrl_url_arg && !data_url_arg) { + fprintf(stderr, "--ctrl-url is specified, but --data-url is missing.\n"); + goto error; + } + + if (!ctrl_url_arg && data_url_arg) { + fprintf(stderr, "--data-url is specified, but --ctrl-url is missing.\n"); + goto error; + } + + /* --ctrl-url/--data-url and the non-option URL are mutually exclusive. */ + if (ctrl_url_arg && url_arg) { + fprintf(stderr, "Both --ctrl-url/--data-url and the non-option URL argument " + "can't be used together.\n"); + goto error; + } + + /* + * Did the user specify an option that implies using a + * custom/unregistered output? + */ + if (url_arg || ctrl_url_arg) { + snapshot_output = lttng_snapshot_output_create(); + if (!snapshot_output) { + fprintf(stderr, "Failed to allocate a snapshot output.\n"); + goto error; + } + } + + action = lttng_action_snapshot_session_create(); + if (!action) { + fprintf(stderr, + "Failed to allocate snapshot session action.\n"); + goto error; + } + + action_status = lttng_action_snapshot_session_set_session_name( + action, session_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + fprintf(stderr, + "Failed to set action snapshot session's session name.\n"); + goto error; + } + + if (snapshot_name_arg) { + if (!snapshot_output) { + fprintf(stderr, "Can't provide a snapshot output name without a snapshot output destination.\n"); + goto error; + } + + ret = lttng_snapshot_output_set_name(snapshot_name_arg, snapshot_output); + if (ret != 0) { + fprintf(stderr, "Failed to set name of snapshot output.\n"); + goto error; + } + } + + if (max_size_arg) { + uint64_t max_size; + + if (!snapshot_output) { + fprintf(stderr, "Can't provide a snapshot output max size without a snapshot output destination.\n"); + goto error; + } + + ret = utils_parse_size_suffix(max_size_arg, &max_size); + if (ret != 0) { + fprintf(stderr, "Failed to parse `%s` as a size.\n", max_size_arg); + goto error; + } + + ret = lttng_snapshot_output_set_size(max_size, snapshot_output); + if (ret != 0) { + fprintf(stderr, "Failed to set snapshot output's max size.\n"); + goto error; + } + } + + if (url_arg) { + /* One argument form, either net:// / net6:// or a local file path. */ + + if (strncmp(url_arg, "net://", strlen("net://")) == 0 || + strncmp(url_arg, "net6://", strlen("net6://")) == 0) { + ret = lttng_snapshot_output_set_network_url( + url_arg, snapshot_output); + if (ret != 0) { + fprintf(stderr, "Failed to parse %s as a network URL.\n", url_arg); + goto error; + } + } else { + ret = lttng_snapshot_output_set_local_path( + url_arg, snapshot_output); + if (ret != 0) { + fprintf(stderr, "Failed to parse %s as a local path.\n", url_arg); + goto error; + } + } + } + + if (ctrl_url_arg) { + /* + * Two argument form, network output with separate control and + * data URLs. + */ + ret = lttng_snapshot_output_set_network_urls( + ctrl_url_arg, data_url_arg, snapshot_output); + if (ret != 0) { + fprintf(stderr, "Failed to parse `%s` and `%s` as control and data URLs.\n", + ctrl_url_arg, data_url_arg); + goto error; + } + } + + if (snapshot_output) { + action_status = lttng_action_snapshot_session_set_output( + action, snapshot_output); + if (action_status != LTTNG_ACTION_STATUS_OK) { + fprintf(stderr, "Failed to set snapshot session action's output.\n"); + goto error; + } + + /* Ownership of `snapshot_output` has been transferred to the action. */ + snapshot_output = NULL; + } + + goto end; + +error: + lttng_action_destroy(action); + action = NULL; + +end: + free(snapshot_name_arg); + free(ctrl_url_arg); + free(data_url_arg); + free(snapshot_output); + return action; +} + +struct action_descr { + const char *name; + struct lttng_action *(*handler) (int *argc, const char ***argv); +}; + +static const +struct action_descr action_descrs[] = { + { "notify", handle_action_notify }, + { "start-session", handle_action_start_session }, + { "stop-session", handle_action_stop_session }, + { "rotate-session", handle_action_rotate_session }, + { "snapshot-session", handle_action_snapshot_session }, +}; + +static +struct lttng_action *parse_action(int *argc, const char ***argv) +{ + int i; + struct lttng_action *action; + const char *action_name; + const struct action_descr *descr = NULL; + + if (*argc == 0) { + fprintf(stderr, "Missing action name.\n"); + goto error; + } + + action_name = (*argv)[0]; + + (*argc)--; + (*argv)++; + + for (i = 0; i < ARRAY_SIZE(action_descrs); i++) { + if (strcmp(action_name, action_descrs[i].name) == 0) { + descr = &action_descrs[i]; + break; + } + } + + if (!descr) { + fprintf(stderr, "Unknown action name: %s\n", action_name); + goto error; + } + + action = descr->handler(argc, argv); + if (!action) { + /* The handler has already printed an error message. */ + goto error; + } + + goto end; +error: + action = NULL; +end: + return action; +} + +static const +struct argpar_opt_descr add_trigger_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + { OPT_CONDITION, '\0', "condition", false }, + { OPT_ACTION, '\0', "action", false }, + { OPT_ID, '\0', "id", true }, + { OPT_FIRE_ONCE_AFTER, '\0', "fire-once-after", true }, + { OPT_FIRE_EVERY, '\0', "fire-every", true }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +void lttng_actions_destructor(void *p) +{ + struct lttng_action *action = p; + + lttng_action_destroy(action); +} + +int cmd_add_trigger(int argc, const char **argv) +{ + int ret; + int my_argc = argc - 1; + const char **my_argv = argv + 1; + struct lttng_condition *condition = NULL; + struct lttng_dynamic_pointer_array actions; + struct argpar_state *argpar_state = NULL; + struct argpar_item *argpar_item = NULL; + struct lttng_action *action_group = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + char *error = NULL; + char *id = NULL; + int i; + char *fire_once_after_str = NULL; + char *fire_every_str = NULL; + + lttng_dynamic_pointer_array_init(&actions, lttng_actions_destructor); + + while (true) { + enum argpar_state_parse_next_status status; + struct argpar_item_opt *item_opt; + int ingested_args; + + argpar_state_destroy(argpar_state); + argpar_state = argpar_state_create(my_argc, my_argv, + add_trigger_options); + if (!argpar_state) { + fprintf(stderr, "Failed to create argpar state.\n"); + goto error; + } + + status = argpar_state_parse_next(argpar_state, &argpar_item, &error); + + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + fprintf(stderr, "Error: %s\n", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + fprintf(stderr, "%s\n", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) argpar_item; + + fprintf(stderr, "Unexpected argument `%s`.\n", + item_non_opt->arg); + goto error; + } + + item_opt = (struct argpar_item_opt *) argpar_item; + + ingested_args = argpar_state_get_ingested_orig_args( + argpar_state); + + my_argc -= ingested_args; + my_argv += ingested_args; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, add_trigger_options); + ret = 0; + goto end; + + case OPT_CONDITION: + { + if (condition) { + fprintf(stderr, "A --condition was already given.\n"); + goto error; + } + + condition = parse_condition(&my_argc, &my_argv); + if (!condition) { + /* + * An error message was already printed by + * parse_condition. + */ + goto error; + } + + break; + } + + case OPT_ACTION: + { + action = parse_action(&my_argc, &my_argv); + if (!action) { + /* + * An error message was already printed by + * parse_condition. + */ + goto error; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &actions, action); + if (ret) { + fprintf(stderr, "Failed to add pointer to pointer array.\n"); + goto error; + } + + /* Ownership of the action was transferred to the group. */ + action = NULL; + + break; + } + + case OPT_ID: + { + if (!assign_string(&id, item_opt->arg, "--id")) { + goto error; + } + + break; + } + + case OPT_FIRE_ONCE_AFTER: + { + if (!assign_string(&fire_once_after_str, item_opt->arg, + "--fire-once-after")) { + goto error; + } + break; + } + + case OPT_FIRE_EVERY: + { + if (!assign_string(&fire_every_str, item_opt->arg, + "--fire-every")) { + goto error; + } + break; + } + + default: + abort(); + } + } + + if (!condition) { + fprintf(stderr, "Missing --condition.\n"); + goto error; + } + + if (lttng_dynamic_pointer_array_get_count(&actions) == 0) { + fprintf(stderr, "Need at least one --action.\n"); + goto error; + } + + if (fire_every_str && fire_once_after_str) { + fprintf(stderr, "Can't specify both --fire-once-after and --fire-every.\n"); + goto error; + } + + action_group = lttng_action_group_create(); + if (!action_group) { + goto error; + } + + for (i = 0; i < lttng_dynamic_pointer_array_get_count(&actions); i++) { + enum lttng_action_status status; + + action = lttng_dynamic_pointer_array_steal_pointer(&actions, i); + + status = lttng_action_group_add_action( + action_group, action); + if (status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + + /* Ownership of the action was transferred to the group. */ + action = NULL; + } + + + trigger = lttng_trigger_create(condition, action_group); + if (!trigger) { + goto error; + } + + /* + * Ownership of the condition and action group was transferred to the + * trigger. + */ + condition = NULL; + action_group = NULL; + + if (id) { + enum lttng_trigger_status trigger_status = + lttng_trigger_set_name(trigger, id); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to set trigger id.\n"); + goto error; + } + } + + if (fire_once_after_str) { + unsigned long long threshold; + enum lttng_trigger_status trigger_status; + + if (utils_parse_unsigned_long_long(fire_once_after_str, &threshold) != 0) { + fprintf(stderr, "Failed to parse `%s` as an integer.\n", fire_once_after_str); + goto error; + } + + trigger_status = lttng_trigger_set_firing_policy(trigger, + LTTNG_TRIGGER_FIRE_ONCE_AFTER_N, threshold); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to set trigger's firing policy.\n"); + goto error; + } + } + + if (fire_every_str) { + unsigned long long threshold; + enum lttng_trigger_status trigger_status; + + if (utils_parse_unsigned_long_long(fire_every_str, &threshold) != 0) { + fprintf(stderr, "Failed to parse `%s` as an integer.\n", fire_every_str); + goto error; + } + + trigger_status = lttng_trigger_set_firing_policy(trigger, + LTTNG_TRIGGER_FIRE_EVERY_N, threshold); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to set trigger's firing policy.\n"); + goto error; + } + } + + ret = lttng_register_trigger(trigger); + if (ret) { + fprintf(stderr, "Failed to register trigger: %s.\n", + lttng_strerror(ret)); + goto error; + } + + printf("Trigger registered successfully.\n"); + + goto end; + +error: + ret = 1; + +end: + argpar_state_destroy(argpar_state); + lttng_dynamic_pointer_array_reset(&actions); + lttng_condition_destroy(condition); + lttng_action_destroy(action_group); + lttng_trigger_destroy(trigger); + free(id); + free(fire_once_after_str); + free(fire_every_str); + // TODO: check what else to free + + return ret; +} diff --git a/src/bin/lttng/commands/create.c b/src/bin/lttng/commands/create.c index 406bfb62a..379ca0a66 100644 --- a/src/bin/lttng/commands/create.c +++ b/src/bin/lttng/commands/create.c @@ -501,7 +501,7 @@ error: * * Spawn a session daemon by forking and execv. */ -static int spawn_sessiond(char *pathname) +static int spawn_sessiond(const char *pathname) { int ret = 0; pid_t pid; @@ -580,7 +580,7 @@ end: static int launch_sessiond(void) { int ret; - char *pathname = NULL; + const char *pathname = NULL; ret = lttng_session_daemon_alive(); if (ret) { diff --git a/src/bin/lttng/commands/disable_events.c b/src/bin/lttng/commands/disable_events.c index 9909fec39..501c3cf3a 100644 --- a/src/bin/lttng/commands/disable_events.c +++ b/src/bin/lttng/commands/disable_events.c @@ -106,7 +106,7 @@ const char *print_event_type(const enum lttng_event_type ev_type) * enabled is 0 or 1 * success is 0 or 1 */ -static int mi_print_event(char *event_name, int enabled, int success) +static int mi_print_event(const char *event_name, int enabled, int success) { int ret; diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c index 0de6a0cd4..570294e8f 100644 --- a/src/bin/lttng/commands/enable_events.c +++ b/src/bin/lttng/commands/enable_events.c @@ -27,6 +27,7 @@ #include #include "../command.h" +#include "../uprobe.h" #if (LTTNG_SYMBOL_NAME_LEN == 256) #define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" @@ -174,397 +175,11 @@ end: return ret; } -/* - * Walk the directories in the PATH environment variable to find the target - * binary passed as parameter. - * - * On success, the full path of the binary is copied in binary_full_path out - * parameter. This buffer is allocated by the caller and must be at least - * LTTNG_PATH_MAX bytes long. - * On failure, returns -1; - */ -static int walk_command_search_path(const char *binary, char *binary_full_path) -{ - char *tentative_binary_path = NULL; - char *command_search_path = NULL; - char *curr_search_dir_end = NULL; - char *curr_search_dir = NULL; - struct stat stat_output; - int ret = 0; - - command_search_path = lttng_secure_getenv("PATH"); - if (!command_search_path) { - ret = -1; - goto end; - } - - /* - * Duplicate the $PATH string as the char pointer returned by getenv() should - * not be modified. - */ - command_search_path = strdup(command_search_path); - if (!command_search_path) { - ret = -1; - goto end; - } - - /* - * This char array is used to concatenate path to binary to look for - * the binary. - */ - tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); - if (!tentative_binary_path) { - ret = -1; - goto alloc_error; - } - - curr_search_dir = command_search_path; - do { - /* - * Split on ':'. The return value of this call points to the - * matching character. - */ - curr_search_dir_end = strchr(curr_search_dir, ':'); - if (curr_search_dir_end != NULL) { - /* - * Add a NULL byte to the end of the first token so it - * can be used as a string. - */ - curr_search_dir_end[0] = '\0'; - } - - /* Empty the tentative path */ - memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char)); - - /* - * Build the tentative path to the binary using the current - * search directory and the name of the binary. - */ - ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s", - curr_search_dir, binary); - if (ret < 0) { - goto free_binary_path; - } - if (ret < LTTNG_PATH_MAX) { - /* - * Use STAT(2) to see if the file exists. - */ - ret = stat(tentative_binary_path, &stat_output); - if (ret == 0) { - /* - * Verify that it is a regular file or a - * symlink and not a special file (e.g. - * device). - */ - if (S_ISREG(stat_output.st_mode) - || S_ISLNK(stat_output.st_mode)) { - /* - * Found a match, set the out parameter - * and return success. - */ - ret = lttng_strncpy(binary_full_path, - tentative_binary_path, - LTTNG_PATH_MAX); - if (ret == -1) { - ERR("Source path does not fit " - "in destination buffer."); - } - goto free_binary_path; - } - } - } - /* Go to the next entry in the $PATH variable. */ - curr_search_dir = curr_search_dir_end + 1; - } while (curr_search_dir_end != NULL); - -free_binary_path: - free(tentative_binary_path); -alloc_error: - free(command_search_path); -end: - return ret; -} - -/* - * Check if the symbol field passed by the user is in fact an address or an - * offset from a symbol. Those two instrumentation types are not supported yet. - * It's expected to be a common mistake because of the existing --probe option - * that does support these formats. - * - * Here are examples of these unsupported formats for the --userspace-probe - * option: - * elf:/path/to/binary:0x400430 - * elf:/path/to/binary:4194364 - * elf:/path/to/binary:my_symbol+0x323 - * elf:/path/to/binary:my_symbol+43 - */ -static int warn_userspace_probe_syntax(const char *symbol) -{ - int ret; - - /* Check if the symbol field is an hex address. */ - ret = sscanf(symbol, "0x%*x"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on address not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - /* Check if the symbol field is an decimal address. */ - ret = sscanf(symbol, "%*u"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on address not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - /* Check if the symbol field is symbol+hex_offset. */ - ret = sscanf(symbol, "%*[^+]+0x%*x"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on symbol+offset not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - /* Check if the symbol field is symbol+decimal_offset. */ - ret = sscanf(symbol, "%*[^+]+%*u"); - if (ret > 0) { - /* If there is a match, print a warning and return an error. */ - ERR("Userspace probe on symbol+offset not supported yet."); - ret = CMD_UNSUPPORTED; - goto error; - } - - ret = 0; - -error: - return ret; -} - -/* - * Parse userspace probe options - * Set the userspace probe fields in the lttng_event struct and set the - * target_path to the path to the binary. - */ -static int parse_userspace_probe_opts(struct lttng_event *ev, char *opt) -{ - int ret = CMD_SUCCESS; - int num_token; - char **tokens; - char *target_path = NULL; - char *unescaped_target_path = NULL; - char *real_target_path = NULL; - char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL; - struct lttng_userspace_probe_location *probe_location = NULL; - struct lttng_userspace_probe_location_lookup_method *lookup_method = - NULL; - - if (opt == NULL) { - ret = CMD_ERROR; - goto end; - } - - switch (ev->type) { - case LTTNG_EVENT_USERSPACE_PROBE: - break; - default: - assert(0); - } - - /* - * userspace probe fields are separated by ':'. - */ - tokens = strutils_split(opt, ':', 1); - num_token = strutils_array_of_strings_len(tokens); - - /* - * Early sanity check that the number of parameter is between 2 and 4 - * inclusively. - * elf:PATH:SYMBOL - * std:PATH:PROVIDER_NAME:PROBE_NAME - * PATH:SYMBOL (same behavior as ELF) - */ - if (num_token < 2 || num_token > 4) { - ret = CMD_ERROR; - goto end_string; - } - - /* - * Looking up the first parameter will tell the technique to use to - * interpret the userspace probe/function description. - */ - switch (num_token) { - case 2: - /* When the probe type is omitted we assume ELF for now. */ - case 3: - if (num_token == 3 && strcmp(tokens[0], "elf") == 0) { - target_path = tokens[1]; - symbol_name = tokens[2]; - } else if (num_token == 2) { - target_path = tokens[0]; - symbol_name = tokens[1]; - } else { - ret = CMD_ERROR; - goto end_string; - } - lookup_method = - lttng_userspace_probe_location_lookup_method_function_elf_create(); - if (!lookup_method) { - WARN("Failed to create ELF lookup method"); - ret = CMD_ERROR; - goto end_string; - } - break; - case 4: - if (strcmp(tokens[0], "sdt") == 0) { - target_path = tokens[1]; - provider_name = tokens[2]; - probe_name = tokens[3]; - } else { - ret = CMD_ERROR; - goto end_string; - } - lookup_method = - lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); - if (!lookup_method) { - WARN("Failed to create SDT lookup method"); - ret = CMD_ERROR; - goto end_string; - } - break; - default: - ret = CMD_ERROR; - goto end_string; - } - - /* strutils_unescape_string allocates a new char *. */ - unescaped_target_path = strutils_unescape_string(target_path, 0); - if (!unescaped_target_path) { - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* - * If there is not forward slash in the path. Walk the $PATH else - * expand. - */ - if (strchr(unescaped_target_path, '/') == NULL) { - /* Walk the $PATH variable to find the targeted binary. */ - real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); - if (!real_target_path) { - PERROR("Error allocating path buffer"); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - ret = walk_command_search_path(unescaped_target_path, real_target_path); - if (ret) { - ERR("Binary not found."); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - } else { - /* - * Expand references to `/./` and `/../`. This function does not check - * if the file exists. This call returns an allocated buffer on - * success. - */ - real_target_path = utils_expand_path_keep_symlink(unescaped_target_path); - if (!real_target_path) { - ERR("Error expanding the path to binary."); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* - * Check if the file exists using access(2), If it does not, - * return an error. - */ - ret = access(real_target_path, F_OK); - if (ret) { - ERR("Cannot find binary at path: %s.", real_target_path); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - } - - switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - /* - * Check for common mistakes in userspace probe description syntax. - */ - ret = warn_userspace_probe_syntax(symbol_name); - if (ret) { - goto end_destroy_lookup_method; - } - - probe_location = lttng_userspace_probe_location_function_create( - real_target_path, symbol_name, lookup_method); - if (!probe_location) { - WARN("Failed to create function probe location"); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* Ownership transferred to probe_location. */ - lookup_method = NULL; - - ret = lttng_event_set_userspace_probe_location(ev, probe_location); - if (ret) { - WARN("Failed to set probe location on event"); - ret = CMD_ERROR; - goto end_destroy_location; - } - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - probe_location = lttng_userspace_probe_location_tracepoint_create( - real_target_path, provider_name, probe_name, lookup_method); - if (!probe_location) { - WARN("Failed to create function probe location"); - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* Ownership transferred to probe_location. */ - lookup_method = NULL; - - ret = lttng_event_set_userspace_probe_location(ev, probe_location); - if (ret) { - WARN("Failed to set probe location on event"); - ret = CMD_ERROR; - goto end_destroy_location; - } - break; - default: - ret = CMD_ERROR; - goto end_destroy_lookup_method; - } - - /* Successful parsing, now clean up everything and return. */ - goto end_string; - -end_destroy_location: - lttng_userspace_probe_location_destroy(probe_location); -end_destroy_lookup_method: - lttng_userspace_probe_location_lookup_method_destroy(lookup_method); -end_string: - strutils_free_null_terminated_array_of_strings(tokens); - /* - * Freeing both char * here makes the error handling simplier. free() - * performs not action if the pointer is NULL. - */ - free(real_target_path); - free(unescaped_target_path); -end: - return ret; -} - /* * Maps LOG4j loglevel from string to value */ -static int loglevel_log4j_str_to_value(const char *inputstr) +LTTNG_HIDDEN +int loglevel_log4j_str_to_value(const char *inputstr) { int i = 0; char str[LTTNG_SYMBOL_NAME_LEN]; @@ -607,7 +222,8 @@ static int loglevel_log4j_str_to_value(const char *inputstr) /* * Maps JUL loglevel from string to value */ -static int loglevel_jul_str_to_value(const char *inputstr) +LTTNG_HIDDEN +int loglevel_jul_str_to_value(const char *inputstr) { int i = 0; char str[LTTNG_SYMBOL_NAME_LEN]; @@ -652,7 +268,8 @@ static int loglevel_jul_str_to_value(const char *inputstr) /* * Maps Python loglevel from string to value */ -static int loglevel_python_str_to_value(const char *inputstr) +LTTNG_HIDDEN +int loglevel_python_str_to_value(const char *inputstr) { int i = 0; char str[LTTNG_SYMBOL_NAME_LEN]; @@ -691,7 +308,7 @@ static int loglevel_python_str_to_value(const char *inputstr) /* * Maps loglevel from string to value */ -static +LTTNG_HIDDEN int loglevel_str_to_value(const char *inputstr) { int i = 0; @@ -891,7 +508,7 @@ end: return ret; } -static +LTTNG_HIDDEN int create_exclusion_list_and_validate(const char *event_name, const char *exclusions_arg, char ***exclusion_list) @@ -969,6 +586,7 @@ static int enable_events(char *session_name) struct lttng_event *ev; struct lttng_domain dom; char **exclusion_list = NULL; + struct lttng_userspace_probe_location *uprobe_loc = NULL; memset(&dom, 0, sizeof(dom)); @@ -1356,7 +974,9 @@ static int enable_events(char *session_name) } break; case LTTNG_EVENT_USERSPACE_PROBE: - ret = parse_userspace_probe_opts(ev, opt_userspace_probe); + assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); + + ret = parse_userspace_probe_opts(opt_userspace_probe, &uprobe_loc); if (ret) { switch (ret) { case CMD_UNSUPPORTED: @@ -1373,6 +993,16 @@ static int enable_events(char *session_name) } goto error; } + + ret = lttng_event_set_userspace_probe_location(ev, uprobe_loc); + if (ret) { + WARN("Failed to set probe location on event"); + ret = CMD_ERROR; + goto error; + } + + /* Ownership of the uprobe location was transferred to the event. */ + uprobe_loc = NULL; break; case LTTNG_EVENT_FUNCTION: ret = parse_probe_opts(ev, opt_function); @@ -1689,6 +1319,7 @@ error: } lttng_destroy_handle(handle); strutils_free_null_terminated_array_of_strings(exclusion_list); + lttng_userspace_probe_location_destroy(uprobe_loc); /* Overwrite ret with error_holder if there was an actual error with * enabling an event. diff --git a/src/bin/lttng/commands/list_triggers.c b/src/bin/lttng/commands/list_triggers.c new file mode 100644 index 000000000..89296c704 --- /dev/null +++ b/src/bin/lttng/commands/list_triggers.c @@ -0,0 +1,541 @@ +#include + +#include "../command.h" + +#include "common/argpar/argpar.h" +#include "common/mi-lttng.h" +#include "lttng/condition/condition-internal.h" +#include "lttng/condition/event-rule.h" +#include "lttng/domain-internal.h" +#include "lttng/event-rule/event-rule-internal.h" +#include "lttng/event-rule/kprobe.h" +#include "lttng/event-rule/kprobe-internal.h" +#include "lttng/event-rule/syscall.h" +#include "lttng/event-rule/tracepoint.h" +#include "lttng/event-rule/uprobe.h" +#include "lttng/trigger/trigger-internal.h" + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_LIST_OPTIONS, +}; + +static const +struct argpar_opt_descr list_trigger_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +void print_event_rule_tracepoint(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + enum lttng_domain_type domain_type; + const char *pattern; + const char *filter; + int loglevel; + unsigned int exclusions_count; + int i; + + event_rule_status = lttng_event_rule_tracepoint_get_pattern( + event_rule, &pattern); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + event_rule_status = lttng_event_rule_tracepoint_get_domain_type( + event_rule, &domain_type); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + printf(" rule: %s (type: tracepoint, domain: %s", pattern, + lttng_domain_type_str(domain_type)); + + event_rule_status = lttng_event_rule_tracepoint_get_filter( + event_rule, &filter); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + printf(", filter: %s", filter); + } else { + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + event_rule_status = lttng_event_rule_tracepoint_get_loglevel( + event_rule, &loglevel); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_loglevel_type loglevel_type; + const char *loglevel_op; + + event_rule_status = lttng_event_rule_tracepoint_get_loglevel_type( + event_rule, &loglevel_type); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + assert(loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE || + loglevel_type == LTTNG_EVENT_LOGLEVEL_SINGLE); + + loglevel_op = (loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE ? "<=" : "=="); + + printf(", log level %s %s", loglevel_op, + mi_lttng_loglevel_string(loglevel, domain_type)); + } else { + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + event_rule_status = lttng_event_rule_tracepoint_get_exclusions_count( + event_rule, &exclusions_count); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + if (exclusions_count > 0) { + printf(", exclusions: "); + for (i = 0; i < exclusions_count; i++) { + const char *exclusion; + + event_rule_status = lttng_event_rule_tracepoint_get_exclusion_at_index( + event_rule, i, &exclusion); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + printf("%s%s", i > 0 ? "," : "", exclusion); + } + } + + + printf(")\n"); +} + +static +void print_event_rule_kprobe(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + const char *name, *symbol_name; + uint64_t offset; + + event_rule_status = lttng_event_rule_kprobe_get_name(event_rule, &name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to get kprobe event rule's name.\n"); + goto end; + } + + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KPROBE); + + printf(" rule: %s (type: probe, location: ", name); + + // FIXME: When the location has been specified by address, this field + // contains the address as a string. The only downside is that we are + // missing a `0x` prefix. + symbol_name = lttng_event_rule_kprobe_get_symbol_name(event_rule); + printf("%s", symbol_name); + + offset = lttng_event_rule_kprobe_get_offset(event_rule); + if (offset > 0) { + printf("+0x%" PRIx64, offset); + } + + printf(")\n"); + +end: + return; +} + +static +void print_event_rule_uprobe(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_status event_rule_status; + const char *name; + const struct lttng_userspace_probe_location *location; + enum lttng_userspace_probe_location_type userspace_probe_location_type; + + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_UPROBE); + + event_rule_status = lttng_event_rule_uprobe_get_name(event_rule, &name); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to get uprobe event rule's name.\n"); + goto end; + } + + event_rule_status = lttng_event_rule_uprobe_get_location(event_rule, &location); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + fprintf(stderr, "Failed to get uprobe event rule's location.\n"); + goto end; + } + + printf(" rule: %s (type: userspace probe, location: ", name); + + userspace_probe_location_type = + lttng_userspace_probe_location_get_type(location); + + switch (userspace_probe_location_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + { + const char *binary_path, *function_name; + + binary_path = lttng_userspace_probe_location_function_get_binary_path(location); + function_name = lttng_userspace_probe_location_function_get_function_name(location); + + printf("%s:%s", binary_path, function_name); + break; + } + + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + printf("SDT not implemented yet"); + break; + + default: + abort(); + } + + printf(")\n"); + +end: + return; +} + +static +void print_event_rule_syscall(const struct lttng_event_rule *event_rule) +{ + const char *pattern, *filter; + enum lttng_event_rule_status event_rule_status; + + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_SYSCALL); + + event_rule_status = lttng_event_rule_syscall_get_pattern(event_rule, &pattern); + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); + + printf(" - rule: %s (type: syscall", pattern); + + event_rule_status = lttng_event_rule_syscall_get_filter( + event_rule, &filter); + if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { + printf(", filter: %s", filter); + } else { + assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); + } + + printf(")\n"); +} + +static +void print_event_rule(const struct lttng_event_rule *event_rule) +{ + enum lttng_event_rule_type event_rule_type = + lttng_event_rule_get_type(event_rule); + + switch (event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + print_event_rule_tracepoint(event_rule); + break; + + case LTTNG_EVENT_RULE_TYPE_KPROBE: + print_event_rule_kprobe(event_rule); + break; + + case LTTNG_EVENT_RULE_TYPE_UPROBE: + print_event_rule_uprobe(event_rule); + break; + + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + print_event_rule_syscall(event_rule); + break; + + default: + abort(); + } +} + +static +void print_condition_event_rule_hit(const struct lttng_condition *condition) +{ + const struct lttng_event_rule *event_rule; + enum lttng_condition_status condition_status; + + condition_status = + lttng_condition_event_rule_get_rule(condition, &event_rule); + assert(condition_status == LTTNG_CONDITION_STATUS_OK); + + print_event_rule(event_rule); +} + +static +void print_one_action(const struct lttng_action *action) +{ + enum lttng_action_type action_type; + enum lttng_action_status action_status; + const char *value; + + action_type = lttng_action_get_type(action); + assert(action_type != LTTNG_ACTION_TYPE_GROUP); + + switch (action_type) { + case LTTNG_ACTION_TYPE_NOTIFY: + printf("notify\n"); + break; + + case LTTNG_ACTION_TYPE_START_SESSION: + action_status = lttng_action_start_session_get_session_name( + action, &value); + assert(action_status == LTTNG_ACTION_STATUS_OK); + printf("start session `%s`\n", value); + break; + + case LTTNG_ACTION_TYPE_STOP_SESSION: + action_status = lttng_action_stop_session_get_session_name( + action, &value); + assert(action_status == LTTNG_ACTION_STATUS_OK); + printf("stop session `%s`\n", value); + break; + + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + action_status = lttng_action_rotate_session_get_session_name( + action, &value); + assert(action_status == LTTNG_ACTION_STATUS_OK); + printf("rotate session `%s`\n", value); + break; + + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + { + const struct lttng_snapshot_output *output; + + action_status = lttng_action_snapshot_session_get_session_name( + action, &value); + assert(action_status == LTTNG_ACTION_STATUS_OK); + printf("snapshot session `%s`", value); + + action_status = lttng_action_snapshot_session_get_output_const( + action, &output); + if (action_status == LTTNG_ACTION_STATUS_OK) { + const char *name; + uint64_t max_size; + const char *ctrl_url, *data_url; + bool starts_with_file, starts_with_net, starts_with_net6; + + ctrl_url = lttng_snapshot_output_get_ctrl_url(output); + assert(ctrl_url && strlen(ctrl_url) > 0); + + data_url = lttng_snapshot_output_get_data_url(output); + assert(data_url); + + starts_with_file = strncmp(ctrl_url, "file://", strlen("file://")) == 0; + starts_with_net = strncmp(ctrl_url, "net://", strlen("net://")) == 0; + starts_with_net6 = strncmp(ctrl_url, "net6://", strlen("net6://")) == 0; + + if (ctrl_url[0] == '/' || starts_with_file) { + if (starts_with_file) { + ctrl_url += strlen("file://"); + } + + printf(", path: %s", ctrl_url); + } else if (starts_with_net || starts_with_net6) { + printf(", url: %s", ctrl_url); + } else { + assert(strlen(data_url) > 0); + + printf(", control url: %s, data url: %s", ctrl_url, data_url); + } + + name = lttng_snapshot_output_get_name(output); + assert(name); + if (strlen(name) > 0) { + printf(", name: %s", name); + } + + max_size = lttng_snapshot_output_get_maxsize(output); + if (max_size != -1ULL) { + printf(", max size: %" PRIu64, max_size); + } + } + + printf("\n"); + break; + } + + default: + abort(); + } +} + +static +void print_one_trigger(const struct lttng_trigger *trigger) +{ + const struct lttng_condition *condition; + enum lttng_condition_type condition_type; + const struct lttng_action *action; + enum lttng_action_type action_type; + enum lttng_trigger_status trigger_status; + const char *name; + enum lttng_trigger_firing_policy_type policy_type; + unsigned long long threshold; + + trigger_status = lttng_trigger_get_name(trigger, &name); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + printf("- id: %s\n", name); + + trigger_status = lttng_trigger_get_firing_policy(trigger, + &policy_type, &threshold); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to get trigger's firing policy.\n"); + goto end; + } + + switch (policy_type) { + case LTTNG_TRIGGER_FIRE_EVERY_N: + if (threshold > 1) { + printf(" firing policy: after every %llu occurences\n", threshold); + } + break; + + case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N: + printf(" firing policy: once after %llu occurences\n", threshold); + break; + + default: + abort(); + } + + condition = lttng_trigger_get_const_condition(trigger); + condition_type = lttng_condition_get_type(condition); + printf(" condition: %s\n", + lttng_condition_type_str(condition_type)); + + switch (condition_type) { + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + print_condition_event_rule_hit(condition); + break; + + default: + printf(" (condition type not handled in %s)\n", __func__); + break; + } + + action = lttng_trigger_get_const_action(trigger); + action_type = lttng_action_get_type(action); + if (action_type == LTTNG_ACTION_TYPE_GROUP) { + enum lttng_action_status action_status; + unsigned int count, i; + + printf(" actions:\n"); + + action_status = lttng_action_group_get_count(action, &count); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + for (i = 0; i < count; i++) { + const struct lttng_action *subaction = + lttng_action_group_get_at_index_const(action, i); + + printf(" "); + print_one_action(subaction); + } + } else { + printf(" action:"); + print_one_action(action); + } + +end: + return; +} + +static +int compare_triggers_by_name(const void *a, const void *b) +{ + const struct lttng_trigger *trigger_a = *((const struct lttng_trigger **) a); + const struct lttng_trigger *trigger_b = *((const struct lttng_trigger **) b); + const char *name_a, *name_b; + enum lttng_trigger_status trigger_status; + + trigger_status = lttng_trigger_get_name(trigger_a, &name_a); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + trigger_status = lttng_trigger_get_name(trigger_b, &name_b); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + return strcmp(name_a, name_b); +} + +int cmd_list_triggers(int argc, const char **argv) +{ + int ret; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + struct lttng_triggers *triggers = NULL; + int i; + const struct lttng_trigger **sorted_triggers = NULL; + enum lttng_trigger_status trigger_status; + unsigned int num_triggers; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + list_trigger_options, true); + if (!argpar_parse_ret.items) { + fprintf(stderr, "Error: %s\n", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, + list_trigger_options); + ret = 0; + goto end; + + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + fprintf(stderr, "Unexpected argument: %s\n", item_non_opt->arg); + } + } + + ret = lttng_list_triggers(&triggers); + if (ret != 0) { + fprintf(stderr, "Error listing triggers: %s.\n", + lttng_strerror(ret)); + goto error; + } + + trigger_status = lttng_triggers_get_count(triggers, &num_triggers); + if (trigger_status != LTTNG_TRIGGER_STATUS_OK) { + fprintf(stderr, "Failed to get trigger count.\n"); + goto error; + } + + sorted_triggers = calloc(num_triggers, sizeof(struct lttng_trigger *)); + if (!sorted_triggers) { + fprintf(stderr, "Failed to allocate array of struct lttng_trigger *.\n"); + goto error; + } + + for (i = 0; i < num_triggers; i++) { + sorted_triggers[i] = lttng_triggers_get_at_index(triggers, i); + } + + qsort(sorted_triggers, num_triggers, sizeof(struct lttng_trigger *), + compare_triggers_by_name); + + for (i = 0; i < num_triggers; i++) { + print_one_trigger(sorted_triggers[i]); + } + + goto end; + +error: + ret = 1; + +end: + argpar_parse_ret_fini(&argpar_parse_ret); + lttng_triggers_destroy(triggers); + + return ret; +} diff --git a/src/bin/lttng/commands/remove_trigger.c b/src/bin/lttng/commands/remove_trigger.c new file mode 100644 index 000000000..6d34c00f4 --- /dev/null +++ b/src/bin/lttng/commands/remove_trigger.c @@ -0,0 +1,131 @@ +#include + +#include "../command.h" + +#include "common/argpar/argpar.h" + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_LIST_OPTIONS, +}; + +static const +struct argpar_opt_descr remove_trigger_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +int cmd_remove_trigger(int argc, const char **argv) +{ + int ret; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + const char *id = NULL; + int i; + struct lttng_triggers *triggers = NULL; + unsigned int triggers_count; + enum lttng_trigger_status trigger_status; + const struct lttng_trigger *trigger_to_remove = NULL; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + remove_trigger_options, true); + if (!argpar_parse_ret.items) { + fprintf(stderr, "Error: %s\n", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, + remove_trigger_options); + ret = 0; + goto end; + + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (id) { + fprintf(stderr, "Unexpected argument: %s\n", item_non_opt->arg); + goto error; + } + + id = item_non_opt->arg; + } + } + + if (!id) { + fprintf(stderr, "Missing `id` argument.\n"); + goto error; + } + + ret = lttng_list_triggers(&triggers); + if (ret != 0) { + fprintf(stderr, "Failed to get the list of triggers.\n"); + goto error; + } + + trigger_status = lttng_triggers_get_count(triggers, &triggers_count); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + for (i = 0; i < triggers_count; i++) { + const struct lttng_trigger *trigger; + const char *trigger_name; + + trigger = lttng_triggers_get_at_index(triggers, i); + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + if (strcmp(trigger_name, id) == 0) { + trigger_to_remove = trigger; + break; + } + } + + if (!trigger_to_remove) { + fprintf(stderr, "Couldn't find trigger with id `%s`.\n", id); + goto error; + } + + ret = lttng_unregister_trigger(trigger_to_remove); + if (ret != 0) { + fprintf(stderr, "Failed to unregister trigger `%s`.\n", id); + goto error; + } + + printf("Removed trigger `%s`.\n", id); + + ret = 0; + goto end; + +error: + ret = 1; + +end: + argpar_parse_ret_fini(&argpar_parse_ret); + lttng_triggers_destroy(triggers); + + return ret; +} diff --git a/src/bin/lttng/commands/track-untrack.c b/src/bin/lttng/commands/track-untrack.c index 7f8007dab..24e2d02ae 100644 --- a/src/bin/lttng/commands/track-untrack.c +++ b/src/bin/lttng/commands/track-untrack.c @@ -451,7 +451,7 @@ static enum cmd_error_code track_untrack_id(enum cmd_type cmd_type, ret = cmd_func(handle, tracker_type, item); if (ret) { - char *msg = NULL; + const char *msg = NULL; switch (-ret) { case LTTNG_ERR_ID_TRACKED: diff --git a/src/bin/lttng/commands/view.c b/src/bin/lttng/commands/view.c index 0dd1623ea..b9f3553b3 100644 --- a/src/bin/lttng/commands/view.c +++ b/src/bin/lttng/commands/view.c @@ -180,8 +180,8 @@ static char **alloc_argv_from_local_opts(const char **opts, size_t opts_len, memcpy(argv, opts, sizeof(char *) * opts_len); if (session_live_mode) { - argv[opts_len] = "-i"; - argv[opts_len + 1] = "lttng-live"; + argv[opts_len] = (char *) "-i"; + argv[opts_len + 1] = (char *) "lttng-live"; argv[opts_len + 2] = (char *) trace_path; argv[opts_len + 3] = NULL; } else { diff --git a/src/bin/lttng/conf.c b/src/bin/lttng/conf.c index 9e1773bb4..8e703b058 100644 --- a/src/bin/lttng/conf.c +++ b/src/bin/lttng/conf.c @@ -266,7 +266,7 @@ char *config_read_session_name_quiet(const char *path) int config_add_session_name(const char *path, const char *name) { int ret; - char *attr = "session="; + const char *attr = "session="; /* Max name len accepted plus attribute's len and the NULL byte. */ char session_name[NAME_MAX + strlen(attr) + 1]; diff --git a/src/bin/lttng/lttng.c b/src/bin/lttng/lttng.c index 84dc3a748..437a9cc4c 100644 --- a/src/bin/lttng/lttng.c +++ b/src/bin/lttng/lttng.c @@ -33,7 +33,7 @@ NULL ; /* Variables */ -static char *progname; +static const char *progname; int opt_no_sessiond; char *opt_sessiond_path; @@ -65,6 +65,7 @@ static struct option long_options[] = { /* First level command */ static struct cmd_struct commands[] = { { "add-context", cmd_add_context}, + { "add-trigger", cmd_add_trigger}, { "create", cmd_create}, { "clear", cmd_clear}, { "destroy", cmd_destroy}, @@ -74,9 +75,11 @@ static struct cmd_struct commands[] = { { "enable-event", cmd_enable_events}, { "help", NULL}, { "list", cmd_list}, + { "list-triggers", cmd_list_triggers}, { "load", cmd_load}, { "metadata", cmd_metadata}, { "regenerate", cmd_regenerate}, + { "remove-trigger", cmd_remove_trigger}, { "rotate", cmd_rotate}, { "enable-rotation", cmd_enable_rotation}, { "disable-rotation", cmd_disable_rotation}, diff --git a/src/bin/lttng/uprobe.c b/src/bin/lttng/uprobe.c new file mode 100644 index 000000000..c51c44af0 --- /dev/null +++ b/src/bin/lttng/uprobe.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "uprobe.h" + +#include +#include +#include + +#include "common/compat/getenv.h" +#include "common/string-utils/string-utils.h" +#include "common/utils.h" +#include "lttng/constant.h" + +#include "command.h" + +/* + * Walk the directories in the PATH environment variable to find the target + * binary passed as parameter. + * + * On success, the full path of the binary is copied in binary_full_path out + * parameter. This buffer is allocated by the caller and must be at least + * LTTNG_PATH_MAX bytes long. + * On failure, returns -1; + */ +static +int walk_command_search_path(const char *binary, char *binary_full_path) +{ + char *tentative_binary_path = NULL; + char *command_search_path = NULL; + char *curr_search_dir_end = NULL; + char *curr_search_dir = NULL; + struct stat stat_output; + int ret = 0; + + command_search_path = lttng_secure_getenv("PATH"); + if (!command_search_path) { + ret = -1; + goto end; + } + + /* + * Duplicate the $PATH string as the char pointer returned by getenv() should + * not be modified. + */ + command_search_path = strdup(command_search_path); + if (!command_search_path) { + ret = -1; + goto end; + } + + /* + * This char array is used to concatenate path to binary to look for + * the binary. + */ + tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); + if (!tentative_binary_path) { + ret = -1; + goto alloc_error; + } + + curr_search_dir = command_search_path; + do { + /* + * Split on ':'. The return value of this call points to the + * matching character. + */ + curr_search_dir_end = strchr(curr_search_dir, ':'); + if (curr_search_dir_end != NULL) { + /* + * Add a NULL byte to the end of the first token so it + * can be used as a string. + */ + curr_search_dir_end[0] = '\0'; + } + + /* Empty the tentative path */ + memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char)); + + /* + * Build the tentative path to the binary using the current + * search directory and the name of the binary. + */ + ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s", + curr_search_dir, binary); + if (ret < 0) { + goto free_binary_path; + } + if (ret < LTTNG_PATH_MAX) { + /* + * Use STAT(2) to see if the file exists. + */ + ret = stat(tentative_binary_path, &stat_output); + if (ret == 0) { + /* + * Verify that it is a regular file or a + * symlink and not a special file (e.g. + * device). + */ + if (S_ISREG(stat_output.st_mode) + || S_ISLNK(stat_output.st_mode)) { + /* + * Found a match, set the out parameter + * and return success. + */ + ret = lttng_strncpy(binary_full_path, + tentative_binary_path, + LTTNG_PATH_MAX); + if (ret == -1) { + ERR("Source path does not fit " + "in destination buffer."); + } + goto free_binary_path; + } + } + } + /* Go to the next entry in the $PATH variable. */ + curr_search_dir = curr_search_dir_end + 1; + } while (curr_search_dir_end != NULL); + +free_binary_path: + free(tentative_binary_path); +alloc_error: + free(command_search_path); +end: + return ret; +} + +/* + * Check if the symbol field passed by the user is in fact an address or an + * offset from a symbol. Those two instrumentation types are not supported yet. + * It's expected to be a common mistake because of the existing --probe option + * that does support these formats. + * + * Here are examples of these unsupported formats for the --userspace-probe + * option: + * elf:/path/to/binary:0x400430 + * elf:/path/to/binary:4194364 + * elf:/path/to/binary:my_symbol+0x323 + * elf:/path/to/binary:my_symbol+43 + */ +static +int warn_userspace_probe_syntax(const char *symbol) +{ + int ret; + + /* Check if the symbol field is an hex address. */ + ret = sscanf(symbol, "0x%*x"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on address not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is an decimal address. */ + ret = sscanf(symbol, "%*u"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on address not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is symbol+hex_offset. */ + ret = sscanf(symbol, "%*[^+]+0x%*x"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on symbol+offset not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is symbol+decimal_offset. */ + ret = sscanf(symbol, "%*[^+]+%*u"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on symbol+offset not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + ret = 0; + +error: + return ret; +} + +/* + * Parse userspace probe options + * Set the userspace probe fields in the lttng_event struct and set the + * target_path to the path to the binary. + */ +LTTNG_HIDDEN +int parse_userspace_probe_opts(const char *opt, + struct lttng_userspace_probe_location **probe_location) +{ + int ret = CMD_SUCCESS; + int num_token; + char **tokens = NULL; + char *target_path = NULL; + char *unescaped_target_path = NULL; + char *real_target_path = NULL; + char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL; + struct lttng_userspace_probe_location *probe_location_local = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; + + assert(opt); + + /* + * userspace probe fields are separated by ':'. + */ + tokens = strutils_split(opt, ':', 1); + num_token = strutils_array_of_strings_len(tokens); + + /* + * Early sanity check that the number of parameter is between 2 and 4 + * inclusively. + * elf:PATH:SYMBOL + * std:PATH:PROVIDER_NAME:PROBE_NAME + * PATH:SYMBOL (same behavior as ELF) + */ + if (num_token < 2 || num_token > 4) { + ret = CMD_ERROR; + goto end; + } + + /* + * Looking up the first parameter will tell the technique to use to + * interpret the userspace probe/function description. + */ + switch (num_token) { + case 2: + /* When the probe type is omitted we assume ELF for now. */ + case 3: + if (num_token == 3 && strcmp(tokens[0], "elf") == 0) { + target_path = tokens[1]; + symbol_name = tokens[2]; + } else if (num_token == 2) { + target_path = tokens[0]; + symbol_name = tokens[1]; + } else { + ret = CMD_ERROR; + goto end; + } + lookup_method = + lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!lookup_method) { + WARN("Failed to create ELF lookup method"); + ret = CMD_ERROR; + goto end; + } + break; + case 4: + if (strcmp(tokens[0], "sdt") == 0) { + target_path = tokens[1]; + provider_name = tokens[2]; + probe_name = tokens[3]; + } else { + ret = CMD_ERROR; + goto end; + } + lookup_method = + lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); + if (!lookup_method) { + WARN("Failed to create SDT lookup method"); + ret = CMD_ERROR; + goto end; + } + break; + default: + ret = CMD_ERROR; + goto end; + } + + /* strutils_unescape_string allocates a new char *. */ + unescaped_target_path = strutils_unescape_string(target_path, 0); + if (!unescaped_target_path) { + ret = CMD_ERROR; + goto end; + } + + /* + * If there is not forward slash in the path. Walk the $PATH else + * expand. + */ + if (strchr(unescaped_target_path, '/') == NULL) { + /* Walk the $PATH variable to find the targeted binary. */ + real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); + if (!real_target_path) { + PERROR("Error allocating path buffer"); + ret = CMD_ERROR; + goto end; + } + ret = walk_command_search_path(unescaped_target_path, real_target_path); + if (ret) { + ERR("Binary not found."); + ret = CMD_ERROR; + goto end; + } + } else { + /* + * Expand references to `/./` and `/../`. This function does not check + * if the file exists. This call returns an allocated buffer on + * success. + */ + real_target_path = utils_expand_path_keep_symlink(unescaped_target_path); + if (!real_target_path) { + ERR("Error expanding the path to binary."); + ret = CMD_ERROR; + goto end; + } + + /* + * Check if the file exists using access(2), If it does not, + * return an error. + */ + ret = access(real_target_path, F_OK); + if (ret) { + ERR("Cannot find binary at path: %s.", real_target_path); + ret = CMD_ERROR; + goto end; + } + } + + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + /* + * Check for common mistakes in userspace probe description syntax. + */ + ret = warn_userspace_probe_syntax(symbol_name); + if (ret) { + goto end; + } + + probe_location_local = lttng_userspace_probe_location_function_create( + real_target_path, symbol_name, lookup_method); + if (!probe_location_local) { + WARN("Failed to create function probe location"); + ret = CMD_ERROR; + goto end; + } + + /* Ownership transferred to probe_location. */ + lookup_method = NULL; + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + probe_location_local = lttng_userspace_probe_location_tracepoint_create( + real_target_path, provider_name, probe_name, lookup_method); + if (!probe_location_local) { + WARN("Failed to create function probe location"); + ret = CMD_ERROR; + goto end; + } + + /* Ownership transferred to probe_location. */ + lookup_method = NULL; + break; + default: + ret = CMD_ERROR; + goto end; + } + + /* + * Everything went fine, transfer ownership of probe location to + * caller. + */ + *probe_location = probe_location_local; + probe_location_local = NULL; + +end: + lttng_userspace_probe_location_destroy(probe_location_local); + lttng_userspace_probe_location_lookup_method_destroy(lookup_method); + strutils_free_null_terminated_array_of_strings(tokens); + /* + * Freeing both char * here makes the error handling simplier. free() + * performs not action if the pointer is NULL. + */ + free(real_target_path); + free(unescaped_target_path); + + return ret; +} diff --git a/src/bin/lttng/uprobe.h b/src/bin/lttng/uprobe.h new file mode 100644 index 000000000..852f36642 --- /dev/null +++ b/src/bin/lttng/uprobe.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 EfficiOS, Inc. + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef SRC_BIN_LTTNG_UPROBE_H +#define SRC_BIN_LTTNG_UPROBE_H + +#include + +struct lttng_userspace_probe_location; + +LTTNG_HIDDEN +int parse_userspace_probe_opts(const char *opt, + struct lttng_userspace_probe_location **uprobe_loc); + +#endif /* SRC_BIN_LTTNG_UPROBE_H */ diff --git a/src/bin/lttng/utils.c b/src/bin/lttng/utils.c index e88166a43..680d24134 100644 --- a/src/bin/lttng/utils.c +++ b/src/bin/lttng/utils.c @@ -128,6 +128,25 @@ void list_cmd_options(FILE *ofp, struct poptOption *options) } } +/* + * Same as list_cmd_options, but for options specified for argpar. + */ +void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options) +{ + int i; + const struct argpar_opt_descr *option = NULL; + + for (i = 0; options[i].long_name != NULL; i++) { + option = &options[i]; + + fprintf(ofp, "--%s\n", option->long_name); + + if (isprint(option->short_name)) { + fprintf(ofp, "-%c\n", option->short_name); + } + } +} + /* * fls: returns the position of the most significant bit. * Returns 0 if no bit is set, else returns the position of the most diff --git a/src/bin/lttng/utils.h b/src/bin/lttng/utils.h index fe82364a0..ff5206397 100644 --- a/src/bin/lttng/utils.h +++ b/src/bin/lttng/utils.h @@ -9,6 +9,7 @@ #define _LTTNG_UTILS_H #include +#include "common/argpar/argpar.h" #include @@ -23,6 +24,7 @@ char *get_session_name(void); char *get_session_name_quiet(void); void list_commands(struct cmd_struct *commands, FILE *ofp); void list_cmd_options(FILE *ofp, struct poptOption *options); +void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options); /* * Return the minimum order for which x <= (1UL << order). diff --git a/src/common/Makefile.am b/src/common/Makefile.am index a50123989..de091b89f 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -2,7 +2,10 @@ AUTOMAKE_OPTIONS = subdir-objects -SUBDIRS = string-utils +SUBDIRS = \ + string-utils \ + filter \ + argpar # Make sure to always distribute all folders # since SUBDIRS is decided at configure time. @@ -20,41 +23,55 @@ DIST_SUBDIRS = \ config \ consumer \ string-utils \ - fd-tracker - + fd-tracker \ + filter # Common library noinst_LTLIBRARIES = libcommon.la EXTRA_DIST = mi-lttng-4.0.xsd libcommon_la_SOURCES = \ - action.c \ - buffer-usage.c \ + actions/action.c \ + actions/group.c \ + actions/notify.c \ + actions/rotate-session.c \ + actions/snapshot-session.c \ + actions/start-session.c \ + actions/stop-session.c \ buffer-view.h buffer-view.c \ common.h \ - condition.c \ + conditions/buffer-usage.c \ + conditions/condition.c \ + conditions/event-rule.c \ + conditions/session-consumed-size.c \ + conditions/session-rotation.c \ context.c context.h \ - credentials.h \ + credentials.c credentials.h \ daemonize.c daemonize.h \ defaults.c \ + domain.c \ dynamic-array.c dynamic-array.h \ dynamic-buffer.c dynamic-buffer.h \ endpoint.c \ error.c error.h \ evaluation.c \ event.c \ + event-rule.c \ + event-rule-kprobe.c \ + event-rule-kretprobe.c \ + event-rule-syscall.c \ + event-rule-uprobe.c \ + event-rule-tracepoint.c \ filter.c filter.h \ futex.c futex.h \ location.c \ mi-lttng.c mi-lttng.h \ notification.c \ - notify.c \ optional.h \ pipe.c pipe.h \ readwrite.c readwrite.h \ runas.c runas.h \ - session-consumed-size.c \ session-descriptor.c \ - session-rotation.c \ + snapshot.c snapshot.h \ time.c \ trace-chunk.c trace-chunk.h \ trace-chunk-registry.h \ @@ -77,7 +94,8 @@ libcommon_la_LIBADD = \ $(top_builddir)/src/common/config/libconfig.la \ $(top_builddir)/src/common/compat/libcompat.la \ $(top_builddir)/src/common/hashtable/libhashtable.la \ - $(top_builddir)/src/common/fd-tracker/libfd-tracker.la + $(top_builddir)/src/common/fd-tracker/libfd-tracker.la \ + $(top_builddir)/src/common/filter/libfilter.la if BUILD_LIB_COMPAT SUBDIRS += compat diff --git a/src/common/action.c b/src/common/action.c deleted file mode 100644 index dc72d37b4..000000000 --- a/src/common/action.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include - -enum lttng_action_type lttng_action_get_type(struct lttng_action *action) -{ - return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN; -} - -LTTNG_HIDDEN -enum lttng_action_type lttng_action_get_type_const( - const struct lttng_action *action) -{ - return action->type; -} - -void lttng_action_destroy(struct lttng_action *action) -{ - if (!action) { - return; - } - - assert(action->destroy); - action->destroy(action); -} - -LTTNG_HIDDEN -bool lttng_action_validate(struct lttng_action *action) -{ - bool valid; - - if (!action) { - valid = false; - goto end; - } - - if (!action->validate) { - /* Sub-class guarantees that it can never be invalid. */ - valid = true; - goto end; - } - - valid = action->validate(action); -end: - return valid; -} - -LTTNG_HIDDEN -int lttng_action_serialize(struct lttng_action *action, - struct lttng_dynamic_buffer *buf) -{ - int ret; - struct lttng_action_comm action_comm = { - .action_type = (int8_t) action->type, - }; - - ret = lttng_dynamic_buffer_append(buf, &action_comm, - sizeof(action_comm)); - if (ret) { - goto end; - } - - ret = action->serialize(action, buf); - if (ret) { - goto end; - } -end: - return ret; -} - -LTTNG_HIDDEN -ssize_t lttng_action_create_from_buffer(const struct lttng_buffer_view *view, - struct lttng_action **_action) -{ - ssize_t ret, action_size = sizeof(struct lttng_action_comm); - struct lttng_action *action; - const struct lttng_action_comm *action_comm; - - if (!view || !_action) { - ret = -1; - goto end; - } - - action_comm = (const struct lttng_action_comm *) view->data; - DBG("Deserializing action from buffer"); - switch (action_comm->action_type) { - case LTTNG_ACTION_TYPE_NOTIFY: - action = lttng_action_notify_create(); - break; - default: - ret = -1; - goto end; - } - - if (!action) { - ret = -1; - goto end; - } - ret = action_size; - *_action = action; -end: - return ret; -} diff --git a/src/common/actions/action.c b/src/common/actions/action.c new file mode 100644 index 000000000..cab49ae0d --- /dev/null +++ b/src/common/actions/action.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LTTNG_HIDDEN +const char *lttng_action_type_string(enum lttng_action_type action_type) +{ + switch (action_type) { + case LTTNG_ACTION_TYPE_UNKNOWN: + return "UNKNOWN"; + + case LTTNG_ACTION_TYPE_GROUP: + return "GROUP"; + + case LTTNG_ACTION_TYPE_NOTIFY: + return "NOTIFY"; + + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + return "ROTATE_SESSION"; + + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + return "SNAPSHOT_SESSION"; + + case LTTNG_ACTION_TYPE_START_SESSION: + return "START_SESSION"; + + case LTTNG_ACTION_TYPE_STOP_SESSION: + return "STOP_SESSION"; + + default: + return "???"; + } +} + +enum lttng_action_type lttng_action_get_type(const struct lttng_action *action) +{ + return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN; +} + +LTTNG_HIDDEN +enum lttng_action_type lttng_action_get_type_const( + const struct lttng_action *action) +{ + return action->type; +} + +LTTNG_HIDDEN +void lttng_action_init( + struct lttng_action *action, + enum lttng_action_type type, + action_validate_cb validate, + action_serialize_cb serialize, + action_equal_cb equal, + action_destroy_cb destroy) +{ + urcu_ref_init(&action->ref); + action->type = type; + action->validate = validate; + action->serialize = serialize; + action->equal = equal; + action->destroy = destroy; +} + +static +void action_destroy_ref(struct urcu_ref *ref) +{ + struct lttng_action *action = + container_of(ref, struct lttng_action, ref); + + action->destroy(action); +} + +LTTNG_HIDDEN +void lttng_action_get(struct lttng_action *action) +{ + urcu_ref_get(&action->ref); +} + +LTTNG_HIDDEN +void lttng_action_put(struct lttng_action *action) +{ + if (!action) { + return; + } + + assert(action->destroy); + urcu_ref_put(&action->ref, action_destroy_ref); +} + +void lttng_action_destroy(struct lttng_action *action) +{ + lttng_action_put(action); +} + +LTTNG_HIDDEN +bool lttng_action_validate(struct lttng_action *action) +{ + bool valid; + + if (!action) { + valid = false; + goto end; + } + + if (!action->validate) { + /* Sub-class guarantees that it can never be invalid. */ + valid = true; + goto end; + } + + valid = action->validate(action); +end: + return valid; +} + +LTTNG_HIDDEN +int lttng_action_serialize(struct lttng_action *action, + struct lttng_dynamic_buffer *buf) +{ + int ret; + struct lttng_action_comm action_comm = { + .action_type = (int8_t) action->type, + }; + + ret = lttng_dynamic_buffer_append(buf, &action_comm, + sizeof(action_comm)); + if (ret) { + goto end; + } + + ret = action->serialize(action, buf); + if (ret) { + goto end; + } +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_action_create_from_buffer(const struct lttng_buffer_view *view, + struct lttng_action **action) +{ + ssize_t consumed_len, specific_action_consumed_len; + const struct lttng_action_comm *action_comm; + action_create_from_buffer_cb create_from_buffer_cb; + struct lttng_buffer_view specific_action_view; + + if (!view || !action) { + consumed_len = -1; + goto end; + } + + action_comm = (const struct lttng_action_comm *) view->data; + + DBG("Create action from buffer: action-type=%s", + lttng_action_type_string(action_comm->action_type)); + + switch (action_comm->action_type) { + case LTTNG_ACTION_TYPE_NOTIFY: + create_from_buffer_cb = lttng_action_notify_create_from_buffer; + break; + + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + create_from_buffer_cb = + lttng_action_rotate_session_create_from_buffer; + break; + + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + create_from_buffer_cb = + lttng_action_snapshot_session_create_from_buffer; + break; + + case LTTNG_ACTION_TYPE_START_SESSION: + create_from_buffer_cb = + lttng_action_start_session_create_from_buffer; + break; + + case LTTNG_ACTION_TYPE_STOP_SESSION: + create_from_buffer_cb = + lttng_action_stop_session_create_from_buffer; + break; + + case LTTNG_ACTION_TYPE_GROUP: + create_from_buffer_cb = lttng_action_group_create_from_buffer; + break; + + default: + ERR("Failed to create action from buffer, unhandled action type: action-type=%u (%s)", + action_comm->action_type, + lttng_action_type_string( + action_comm->action_type)); + consumed_len = -1; + goto end; + } + + /* Create buffer view for the action-type-specific data. */ + specific_action_view = lttng_buffer_view_from_view(view, + sizeof(struct lttng_action_comm), + view->size - sizeof(struct lttng_action_comm)); + + specific_action_consumed_len = + create_from_buffer_cb(&specific_action_view, action); + if (specific_action_consumed_len < 0) { + ERR("Failed to create specific action from buffer."); + consumed_len = -1; + goto end; + } + + assert(*action); + + consumed_len = sizeof(struct lttng_action_comm) + + specific_action_consumed_len; + +end: + return consumed_len; +} + +LTTNG_HIDDEN +bool lttng_action_is_equal(const struct lttng_action *a, + const struct lttng_action *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + diff --git a/src/common/actions/group.c b/src/common/actions/group.c new file mode 100644 index 000000000..055bb5e5c --- /dev/null +++ b/src/common/actions/group.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +struct lttng_action_group { + struct lttng_action parent; + + /* Owned by this. */ + struct lttng_action **elements; + size_t n_elements; + size_t n_elements_allocated; +}; + +struct lttng_action_group_comm { + uint32_t n_elements; + + /* + * Variable data: each element serialized sequentially. + */ + char data[]; +} LTTNG_PACKED; + +static struct lttng_action_group *action_group_from_action( + const struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_group, parent); +} + +static bool lttng_action_group_validate(struct lttng_action *action) +{ + size_t i; + struct lttng_action_group *action_group; + bool valid; + + assert(lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP); + + action_group = action_group_from_action(action); + + for (i = 0; i < action_group->n_elements; i++) { + struct lttng_action *child = action_group->elements[i]; + + assert(child); + + if (!lttng_action_validate(child)) { + valid = false; + goto end; + } + } + + valid = true; + +end: + return valid; +} + +static bool lttng_action_group_is_equal(const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + struct lttng_action_group *a, *b; + size_t i; + + assert(lttng_action_get_type_const(_a) == LTTNG_ACTION_TYPE_GROUP); + assert(lttng_action_get_type_const(_b) == LTTNG_ACTION_TYPE_GROUP); + + a = action_group_from_action(_a); + b = action_group_from_action(_b); + + if (a->n_elements != b->n_elements) { + goto end; + } + + for (i = 0; i < a->n_elements; i++) { + struct lttng_action *child_a = a->elements[i]; + struct lttng_action *child_b = b->elements[i]; + + assert(child_a); + assert(child_b); + + if (!lttng_action_is_equal(child_a, child_b)) { + goto end; + } + } + + is_equal = true; +end: + return is_equal; +} + +static int lttng_action_group_serialize( + struct lttng_action *action, struct lttng_dynamic_buffer *buf) +{ + struct lttng_action_group *action_group; + struct lttng_action_group_comm comm; + int ret; + size_t i; + + assert(action); + assert(buf); + assert(lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP); + + action_group = action_group_from_action(action); + + DBG("Serializing action group"); + + comm.n_elements = action_group->n_elements; + + ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + for (i = 0; i < action_group->n_elements; i++) { + struct lttng_action *child = action_group->elements[i]; + + assert(child); + + ret = lttng_action_serialize(child, buf); + if (ret) { + goto end; + } + } + + ret = 0; + +end: + return ret; +} + +static void lttng_action_group_destroy(struct lttng_action *action) +{ + struct lttng_action_group *action_group; + size_t i; + + if (!action) { + goto end; + } + + action_group = action_group_from_action(action); + + for (i = 0; i < action_group->n_elements; i++) { + struct lttng_action *child = action_group->elements[i]; + + assert(child); + + lttng_action_destroy(child); + } + + free(action_group->elements); + free(action_group); + +end: + return; +} + +ssize_t lttng_action_group_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len; + struct lttng_action_group_comm *comm; + struct lttng_action *group; + struct lttng_action *child_action = NULL; + enum lttng_action_status status; + size_t i; + + group = lttng_action_group_create(); + if (!group) { + consumed_len = -1; + goto end; + } + + comm = (struct lttng_action_group_comm *) view->data; + + consumed_len = sizeof(struct lttng_action_group_comm); + + for (i = 0; i < comm->n_elements; i++) { + ssize_t consumed_len_child; + struct lttng_buffer_view child_view; + + child_view = lttng_buffer_view_from_view( + view, consumed_len, view->size - consumed_len); + consumed_len_child = lttng_action_create_from_buffer( + &child_view, &child_action); + + status = lttng_action_group_add_action(group, child_action); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + child_action = NULL; + + consumed_len += consumed_len_child; + } + + *p_action = group; + group = NULL; + +end: + lttng_action_group_destroy(group); + lttng_action_destroy(child_action); + + return consumed_len; +} + +struct lttng_action *lttng_action_group_create(void) +{ + struct lttng_action_group *action_group; + struct lttng_action *action; + + action_group = zmalloc(sizeof(struct lttng_action_group)); + if (!action_group) { + action = NULL; + goto end; + } + + action = &action_group->parent; + + lttng_action_init(action, LTTNG_ACTION_TYPE_GROUP, + lttng_action_group_validate, + lttng_action_group_serialize, + lttng_action_group_is_equal, + lttng_action_group_destroy); + + action_group->n_elements = 0; + action_group->n_elements_allocated = 10; + action_group->elements = zmalloc(10 * sizeof(struct lttng_action *)); + if (!action_group->elements) { + goto error; + } + + goto end; + +error: + lttng_action_group_destroy(action); + action = NULL; + +end: + return action; +} + +enum lttng_action_status lttng_action_group_add_action( + struct lttng_action *group, struct lttng_action *action) +{ + struct lttng_action_group *action_group; + enum lttng_action_status status; + + if (!group || + (lttng_action_get_type(group) != + LTTNG_ACTION_TYPE_GROUP) || + !action) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + /* + * Don't allow adding groups in groups for now, since we're afraid of + * cycles. + */ + if (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_group = action_group_from_action(group); + + if (action_group->n_elements == action_group->n_elements_allocated) { + // TODO: handle resize. + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + action_group->elements[action_group->n_elements] = action; + action_group->n_elements++; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_group_get_count( + const struct lttng_action *group, unsigned int *count) +{ + struct lttng_action_group *action_group; + enum lttng_action_status status = LTTNG_ACTION_STATUS_OK; + + if (!group || (lttng_action_get_type_const(group) != + LTTNG_ACTION_TYPE_GROUP)) { + status = LTTNG_ACTION_STATUS_INVALID; + *count = 0; + goto end; + } + + action_group = action_group_from_action(group); + + + *count = action_group->n_elements; +end: + return status; +} + +const struct lttng_action *lttng_action_group_get_at_index_const( + const struct lttng_action *group, + unsigned int index) +{ + struct lttng_action_group *action_group; + const struct lttng_action *action = NULL; + + if (!group || (lttng_action_get_type_const(group) != + LTTNG_ACTION_TYPE_GROUP)) { + goto end; + } + + action_group = action_group_from_action(group); + + if (index >= action_group->n_elements) { + goto end; + } + + action = action_group->elements[index]; +end: + return action; +} diff --git a/src/common/notify.c b/src/common/actions/notify.c similarity index 52% rename from src/common/notify.c rename to src/common/actions/notify.c index 00d1a0ef4..03dcce73c 100644 --- a/src/common/notify.c +++ b/src/common/actions/notify.c @@ -23,6 +23,14 @@ int lttng_action_notify_serialize(struct lttng_action *action, return 0; } +static +bool lttng_action_notify_is_equal(const struct lttng_action *a, + const struct lttng_action *b) +{ + /* TODO check type ??? */ + return true; +} + struct lttng_action *lttng_action_notify_create(void) { struct lttng_action_notify *notify; @@ -32,9 +40,27 @@ struct lttng_action *lttng_action_notify_create(void) goto end; } - notify->parent.type = LTTNG_ACTION_TYPE_NOTIFY; - notify->parent.serialize = lttng_action_notify_serialize; - notify->parent.destroy = lttng_action_notify_destroy; + lttng_action_init(¬ify->parent, LTTNG_ACTION_TYPE_NOTIFY, NULL, + lttng_action_notify_serialize, + lttng_action_notify_is_equal, + lttng_action_notify_destroy); end: return ¬ify->parent; } + +ssize_t lttng_action_notify_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **action) +{ + ssize_t consumed_length; + + *action = lttng_action_notify_create(); + if (!*action) { + consumed_length = -1; + goto end; + } + + consumed_length = 0; +end: + return consumed_length; +} diff --git a/src/common/actions/rotate-session.c b/src/common/actions/rotate-session.c new file mode 100644 index 000000000..4f8199205 --- /dev/null +++ b/src/common/actions/rotate-session.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +struct lttng_action_rotate_session { + struct lttng_action parent; + + /* Owned by this. */ + char *session_name; +}; + +struct lttng_action_rotate_session_comm { + /* Includes the trailing \0. */ + uint32_t session_name_len; + + /* + * Variable data: + * + * - session name (null terminated) + */ + char data[]; +} LTTNG_PACKED; + +static struct lttng_action_rotate_session *action_rotate_session_from_action( + struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_rotate_session, parent); +} + +static const struct lttng_action_rotate_session *action_rotate_session_from_action_const( + const struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_rotate_session, parent); +} + +static bool lttng_action_rotate_session_validate(struct lttng_action *action) +{ + bool valid; + struct lttng_action_rotate_session *action_rotate_session; + + if (!action) { + valid = false; + goto end; + } + + action_rotate_session = action_rotate_session_from_action(action); + + /* A non-empty session name is mandatory. */ + if (!action_rotate_session->session_name || + strlen(action_rotate_session->session_name) == 0) { + valid = false; + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_rotate_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + const struct lttng_action_rotate_session *a, *b; + + a = action_rotate_session_from_action_const(_a); + b = action_rotate_session_from_action_const(_b); + + /* Action is not valid if this is not true. */ + assert(a->session_name); + assert(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} +static int lttng_action_rotate_session_serialize( + struct lttng_action *action, struct lttng_dynamic_buffer *buf) +{ + struct lttng_action_rotate_session *action_rotate_session; + struct lttng_action_rotate_session_comm comm; + size_t session_name_len; + int ret; + + assert(action); + assert(buf); + + action_rotate_session = action_rotate_session_from_action(action); + + assert(action_rotate_session->session_name); + + DBG("Serializing rotate session action: session-name: %s", + action_rotate_session->session_name); + + session_name_len = strlen(action_rotate_session->session_name) + 1; + comm.session_name_len = session_name_len; + + ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(buf, + action_rotate_session->session_name, session_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +static void lttng_action_rotate_session_destroy(struct lttng_action *action) +{ + struct lttng_action_rotate_session *action_rotate_session; + + if (!action) { + goto end; + } + + action_rotate_session = action_rotate_session_from_action(action); + + free(action_rotate_session->session_name); + free(action_rotate_session); + +end: + return; +} + +ssize_t lttng_action_rotate_session_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len; + struct lttng_action_rotate_session_comm *comm; + const char *session_name; + struct lttng_action *action; + enum lttng_action_status status; + + action = lttng_action_rotate_session_create(); + if (!action) { + consumed_len = -1; + goto end; + } + + comm = (struct lttng_action_rotate_session_comm *) view->data; + session_name = (const char *) &comm->data; + + if (!lttng_buffer_view_validate_string( + view, session_name, comm->session_name_len)) { + consumed_len = -1; + goto end; + } + + status = lttng_action_rotate_session_set_session_name( + action, session_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + consumed_len = sizeof(struct lttng_action_rotate_session_comm) + + comm->session_name_len; + *p_action = action; + action = NULL; + +end: + lttng_action_rotate_session_destroy(action); + + return consumed_len; +} + +struct lttng_action *lttng_action_rotate_session_create(void) +{ + struct lttng_action *action; + + action = zmalloc(sizeof(struct lttng_action_rotate_session)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_ROTATE_SESSION, + lttng_action_rotate_session_validate, + lttng_action_rotate_session_serialize, + lttng_action_rotate_session_is_equal, + lttng_action_rotate_session_destroy); + +end: + return action; +} + +enum lttng_action_status lttng_action_rotate_session_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_rotate_session *action_rotate_session; + enum lttng_action_status status; + + if (!action || !session_name || strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_rotate_session = action_rotate_session_from_action(action); + + free(action_rotate_session->session_name); + + action_rotate_session->session_name = strdup(session_name); + if (!action_rotate_session->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_rotate_session_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_rotate_session *action_rotate_session; + enum lttng_action_status status; + + if (!action || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_rotate_session = action_rotate_session_from_action_const(action); + + *session_name = action_rotate_session->session_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} diff --git a/src/common/actions/snapshot-session.c b/src/common/actions/snapshot-session.c new file mode 100644 index 000000000..a02cf80ca --- /dev/null +++ b/src/common/actions/snapshot-session.c @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct lttng_action_snapshot_session { + struct lttng_action parent; + + /* Owned by this. */ + char *session_name; + + /* + * When non-NULL, use this custom output when taking the snapshot, + * rather than the session's registered snapshot output. + * + * Owned by this. + */ + struct lttng_snapshot_output *output; +}; + +struct lttng_action_snapshot_session_comm { + /* All string lengths include the trailing \0. */ + uint32_t session_name_len; + uint32_t snapshot_output_len; + + /* + * Variable data (all strings are null-terminated): + * + * - session name string + * - snapshot output object + * + */ + char data[]; +} LTTNG_PACKED; + +static struct lttng_action_snapshot_session * +action_snapshot_session_from_action(struct lttng_action *action) +{ + assert(action); + + return container_of( + action, struct lttng_action_snapshot_session, parent); +} + +static const struct lttng_action_snapshot_session * +action_snapshot_session_from_action_const(const struct lttng_action *action) +{ + assert(action); + + return container_of( + action, struct lttng_action_snapshot_session, parent); +} + +static bool lttng_action_snapshot_session_validate(struct lttng_action *action) +{ + bool valid = false; + struct lttng_action_snapshot_session *action_snapshot_session; + + if (!action) { + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + /* A non-empty session name is mandatory. */ + if (!action_snapshot_session->session_name || + strlen(action_snapshot_session->session_name) == 0) { + goto end; + } + + if (action_snapshot_session->output && + !lttng_snapshot_output_validate(action_snapshot_session->output)) { + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_snapshot_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + const struct lttng_action_snapshot_session *a, *b; + + a = action_snapshot_session_from_action_const(_a); + b = action_snapshot_session_from_action_const(_b); + + /* Action is not valid if this is not true. */ + assert(a->session_name); + assert(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + if (a->output && b->output && + !lttng_snapshot_output_is_equal(a->output, b->output)) { + goto end; + } else if (!!a->output != !!b->output) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + +static size_t serialize_strlen(const char *s) +{ + + size_t len = 0; + + if (s) { + len = strlen(s) + 1; + } + + return len; +} + +static int lttng_action_snapshot_session_serialize( + struct lttng_action *action, struct lttng_dynamic_buffer *buf) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + struct lttng_action_snapshot_session_comm comm; + struct lttng_dynamic_buffer snapshot_output_buf = { 0 }; + int ret; + + assert(action); + assert(buf); + + lttng_dynamic_buffer_init(&snapshot_output_buf); + + action_snapshot_session = action_snapshot_session_from_action(action); + + assert(action_snapshot_session->session_name); + DBG("Serializing snapshot session action: session-name: %s", + action_snapshot_session->session_name); + + /* Serialize the snapshot output object first, so we know its length. */ + if (action_snapshot_session->output) { + ret = lttng_snapshot_output_serialize( + action_snapshot_session->output, &snapshot_output_buf); + if (ret) { + goto end; + } + } + + comm.session_name_len = + serialize_strlen(action_snapshot_session->session_name); + comm.snapshot_output_len = snapshot_output_buf.size; + + ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(buf, + action_snapshot_session->session_name, + comm.session_name_len); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append_buffer(buf, &snapshot_output_buf); + if (ret) { + goto end; + } + +end: + lttng_dynamic_buffer_reset(&snapshot_output_buf); + return ret; +} + +static void lttng_action_snapshot_session_destroy(struct lttng_action *action) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + + if (!action) { + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + free(action_snapshot_session->session_name); + lttng_snapshot_output_destroy(action_snapshot_session->output); + free(action_snapshot_session); + +end: + return; +} + +ssize_t lttng_action_snapshot_session_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len; + struct lttng_action_snapshot_session_comm *comm; + const char *variable_data; + struct lttng_action *action; + enum lttng_action_status status; + struct lttng_snapshot_output *snapshot_output = NULL; + + action = lttng_action_snapshot_session_create(); + if (!action) { + goto error; + } + + comm = (struct lttng_action_snapshot_session_comm *) view->data; + variable_data = (const char *) &comm->data; + + consumed_len = sizeof(struct lttng_action_snapshot_session_comm); + + if (!lttng_buffer_view_validate_string( + view, variable_data, comm->session_name_len)) { + goto error; + } + + status = lttng_action_snapshot_session_set_session_name( + action, variable_data); + if (status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + + variable_data += comm->session_name_len; + consumed_len += comm->session_name_len; + + /* If there is a snapshot output object, deserialize it. */ + if (comm->snapshot_output_len > 0) { + ssize_t snapshot_output_consumed_len; + enum lttng_action_status action_status; + struct lttng_buffer_view snapshot_output_buffer_view = + lttng_buffer_view_from_view(view, consumed_len, + comm->snapshot_output_len); + if (!snapshot_output_buffer_view.data) { + fprintf(stderr, "Failed to create buffer view for snapshot output.\n"); + goto error; + } + + snapshot_output_consumed_len = + lttng_snapshot_output_create_from_buffer( + &snapshot_output_buffer_view, &snapshot_output); + if (snapshot_output_consumed_len != comm->snapshot_output_len) { + fprintf(stderr, "Failed to deserialize snapshot output object: " + "consumed-len: %zd, expected-len: %" PRIu32, + snapshot_output_consumed_len, + comm->snapshot_output_len); + goto error; + } + + action_status = lttng_action_snapshot_session_set_output( + action, snapshot_output); + if (action_status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + + /* Ownership has been transferred to the action. */ + snapshot_output = NULL; + } + + variable_data += comm->snapshot_output_len; + consumed_len += comm->snapshot_output_len; + + *p_action = action; + action = NULL; + + goto end; + +error: + consumed_len = -1; + +end: + lttng_action_snapshot_session_destroy(action); + lttng_snapshot_output_destroy(snapshot_output); + + return consumed_len; +} + +struct lttng_action *lttng_action_snapshot_session_create(void) +{ + struct lttng_action *action; + + action = zmalloc(sizeof(struct lttng_action_snapshot_session)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION, + lttng_action_snapshot_session_validate, + lttng_action_snapshot_session_serialize, + lttng_action_snapshot_session_is_equal, + lttng_action_snapshot_session_destroy); + +end: + return action; +} + +enum lttng_action_status lttng_action_snapshot_session_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !session_name || strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + free(action_snapshot_session->session_name); + + action_snapshot_session->session_name = strdup(session_name); + if (!action_snapshot_session->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action_const(action); + + if (action_snapshot_session->session_name) { + *session_name = action_snapshot_session->session_name; + status = LTTNG_ACTION_STATUS_OK; + } else { + status = LTTNG_ACTION_STATUS_UNSET; + } + +end: + + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_set_output( + struct lttng_action *action, + struct lttng_snapshot_output *output) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !output) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + lttng_snapshot_output_destroy(action_snapshot_session->output); + action_snapshot_session->output = output; + + status = LTTNG_ACTION_STATUS_OK; + +end: + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_get_output_const( + const struct lttng_action *action, + const struct lttng_snapshot_output **output) +{ + const struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !output) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action_const(action); + + if (action_snapshot_session->output) { + *output = action_snapshot_session->output; + status = LTTNG_ACTION_STATUS_OK; + } else { + status = LTTNG_ACTION_STATUS_UNSET; + } + +end: + return status; +} diff --git a/src/common/actions/start-session.c b/src/common/actions/start-session.c new file mode 100644 index 000000000..7ff9e637f --- /dev/null +++ b/src/common/actions/start-session.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +struct lttng_action_start_session { + struct lttng_action parent; + + /* Owned by this. */ + char *session_name; +}; + +struct lttng_action_start_session_comm { + /* Includes the trailing \0. */ + uint32_t session_name_len; + + /* + * Variable data: + * + * - session name (null terminated) + */ + char data[]; +} LTTNG_PACKED; + +static struct lttng_action_start_session *action_start_session_from_action( + struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_start_session, parent); +} + +static const struct lttng_action_start_session *action_start_session_from_action_const( + const struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_start_session, parent); +} + +static bool lttng_action_start_session_validate(struct lttng_action *action) +{ + bool valid; + struct lttng_action_start_session *action_start_session; + + if (!action) { + valid = false; + goto end; + } + + action_start_session = action_start_session_from_action(action); + + /* A non-empty session name is mandatory. */ + if (!action_start_session->session_name || + strlen(action_start_session->session_name) == 0) { + valid = false; + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_start_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + struct lttng_action_start_session *a, *b; + + a = container_of(_a, struct lttng_action_start_session, parent); + b = container_of(_b, struct lttng_action_start_session, parent); + + /* Action is not valid if this is not true. */ + assert(a->session_name); + assert(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + +static int lttng_action_start_session_serialize( + struct lttng_action *action, struct lttng_dynamic_buffer *buf) +{ + struct lttng_action_start_session *action_start_session; + struct lttng_action_start_session_comm comm; + size_t session_name_len; + int ret; + + assert(action); + assert(buf); + + action_start_session = action_start_session_from_action(action); + + assert(action_start_session->session_name); + + DBG("Serializing start session action: session-name: %s", + action_start_session->session_name); + + session_name_len = strlen(action_start_session->session_name) + 1; + comm.session_name_len = session_name_len; + + ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(buf, + action_start_session->session_name, session_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +static void lttng_action_start_session_destroy(struct lttng_action *action) +{ + struct lttng_action_start_session *action_start_session; + + if (!action) { + goto end; + } + + action_start_session = action_start_session_from_action(action); + + free(action_start_session->session_name); + free(action_start_session); + +end: + return; +} + +ssize_t lttng_action_start_session_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len; + struct lttng_action_start_session_comm *comm; + const char *session_name; + struct lttng_action *action; + enum lttng_action_status status; + + action = lttng_action_start_session_create(); + if (!action) { + consumed_len = -1; + goto end; + } + + comm = (struct lttng_action_start_session_comm *) view->data; + session_name = (const char *) &comm->data; + + if (!lttng_buffer_view_validate_string( + view, session_name, comm->session_name_len)) { + consumed_len = -1; + goto end; + } + + status = lttng_action_start_session_set_session_name( + action, session_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + consumed_len = sizeof(struct lttng_action_start_session_comm) + + comm->session_name_len; + *p_action = action; + action = NULL; + +end: + lttng_action_start_session_destroy(action); + + return consumed_len; +} + +struct lttng_action *lttng_action_start_session_create(void) +{ + struct lttng_action *action; + + action = zmalloc(sizeof(struct lttng_action_start_session)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_START_SESSION, + lttng_action_start_session_validate, + lttng_action_start_session_serialize, + lttng_action_start_session_is_equal, + lttng_action_start_session_destroy); + +end: + return action; +} + +enum lttng_action_status lttng_action_start_session_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_start_session *action_start_session; + enum lttng_action_status status; + + if (!action || !session_name || strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_start_session = action_start_session_from_action(action); + + free(action_start_session->session_name); + + action_start_session->session_name = strdup(session_name); + if (!action_start_session->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_start_session_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_start_session *action_start_session; + enum lttng_action_status status; + + if (!action || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_start_session = action_start_session_from_action_const(action); + + *session_name = action_start_session->session_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} diff --git a/src/common/actions/stop-session.c b/src/common/actions/stop-session.c new file mode 100644 index 000000000..6b9489623 --- /dev/null +++ b/src/common/actions/stop-session.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2019 EfficiOS, Inc. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +struct lttng_action_stop_session { + struct lttng_action parent; + + /* Owned by this. */ + char *session_name; +}; + +struct lttng_action_stop_session_comm { + /* Includes the trailing \0. */ + uint32_t session_name_len; + + /* + * Variable data: + * + * - session name (null terminated) + */ + char data[]; +} LTTNG_PACKED; + +static struct lttng_action_stop_session *action_stop_session_from_action( + struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_stop_session, parent); +} + +static const struct lttng_action_stop_session *action_stop_session_from_action_const( + const struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_stop_session, parent); +} + +static bool lttng_action_stop_session_validate(struct lttng_action *action) +{ + bool valid; + struct lttng_action_stop_session *action_stop_session; + + if (!action) { + valid = false; + goto end; + } + + action_stop_session = action_stop_session_from_action(action); + + /* A non-empty session name is mandatory. */ + if (!action_stop_session->session_name || + strlen(action_stop_session->session_name) == 0) { + valid = false; + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_stop_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + const struct lttng_action_stop_session *a, *b; + + a = action_stop_session_from_action_const(_a); + b = action_stop_session_from_action_const(_b); + + /* Action is not valid if this is not true. */ + assert(a->session_name); + assert(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + +static int lttng_action_stop_session_serialize( + struct lttng_action *action, struct lttng_dynamic_buffer *buf) +{ + struct lttng_action_stop_session *action_stop_session; + struct lttng_action_stop_session_comm comm; + size_t session_name_len; + int ret; + + assert(action); + assert(buf); + + action_stop_session = action_stop_session_from_action(action); + + assert(action_stop_session->session_name); + + DBG("Serializing stop session action: session-name: %s", + action_stop_session->session_name); + + session_name_len = strlen(action_stop_session->session_name) + 1; + comm.session_name_len = session_name_len; + + ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(buf, + action_stop_session->session_name, session_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +static void lttng_action_stop_session_destroy(struct lttng_action *action) +{ + struct lttng_action_stop_session *action_stop_session; + + if (!action) { + goto end; + } + + action_stop_session = action_stop_session_from_action(action); + + free(action_stop_session->session_name); + free(action_stop_session); + +end: + return; +} + +ssize_t lttng_action_stop_session_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len; + struct lttng_action_stop_session_comm *comm; + const char *session_name; + struct lttng_action *action; + enum lttng_action_status status; + + action = lttng_action_stop_session_create(); + if (!action) { + consumed_len = -1; + goto end; + } + + comm = (struct lttng_action_stop_session_comm *) view->data; + session_name = (const char *) &comm->data; + + if (!lttng_buffer_view_validate_string( + view, session_name, comm->session_name_len)) { + consumed_len = -1; + goto end; + } + + status = lttng_action_stop_session_set_session_name( + action, session_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + consumed_len = sizeof(struct lttng_action_stop_session_comm) + + comm->session_name_len; + *p_action = action; + action = NULL; + +end: + lttng_action_stop_session_destroy(action); + + return consumed_len; +} + +struct lttng_action *lttng_action_stop_session_create(void) +{ + struct lttng_action *action; + + action = zmalloc(sizeof(struct lttng_action_stop_session)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_STOP_SESSION, + lttng_action_stop_session_validate, + lttng_action_stop_session_serialize, + lttng_action_stop_session_is_equal, + lttng_action_stop_session_destroy); + +end: + return action; +} + +enum lttng_action_status lttng_action_stop_session_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_stop_session *action_stop_session; + enum lttng_action_status status; + + if (!action || !session_name || strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_stop_session = action_stop_session_from_action(action); + + free(action_stop_session->session_name); + + action_stop_session->session_name = strdup(session_name); + if (!action_stop_session->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_stop_session_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_stop_session *action_stop_session; + enum lttng_action_status status; + + if (!action || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_stop_session = action_stop_session_from_action_const(action); + + *session_name = action_stop_session->session_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} diff --git a/src/common/argpar/Makefile.am b/src/common/argpar/Makefile.am new file mode 100644 index 000000000..175526abe --- /dev/null +++ b/src/common/argpar/Makefile.am @@ -0,0 +1,3 @@ +noinst_LTLIBRARIES = libargpar.la + +libargpar_la_SOURCES = argpar.c argpar.h diff --git a/src/common/argpar/argpar.c b/src/common/argpar/argpar.c new file mode 100644 index 000000000..a8977ea3b --- /dev/null +++ b/src/common/argpar/argpar.c @@ -0,0 +1,746 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "argpar.h" + +#define argpar_realloc(_ptr, _type, _nmemb) ((_type *) realloc(_ptr, (_nmemb) * sizeof(_type))) +#define argpar_calloc(_type, _nmemb) ((_type *) calloc((_nmemb), sizeof(_type))) +#define argpar_zalloc(_type) argpar_calloc(_type, 1) + +#define ARGPAR_ASSERT(_cond) assert(_cond) + +/* + * Structure holding the argpar state between successive argpar_state_parse_next + * calls. + * + * Created with `argpar_state_create` and destroyed with `argpar_state_destroy`. + */ +struct argpar_state { + /* + * Data provided by the user in argpar_state_create, does not change + * afterwards. + */ + unsigned int argc; + const char * const *argv; + const struct argpar_opt_descr *descrs; + + /* + * Index of the argument to process in the next argpar_state_parse_next + * call. + */ + unsigned int i; + + /* Counter of non-option arguments. */ + int non_opt_index; + + /* + * Short option state: if set, we are in the middle of a short option + * group, so we should resume there at the next argpar_state_parse_next + * call. + */ + const char *short_opt_ch; +}; + +static +char *argpar_vasprintf(const char *fmt, va_list args) +{ + int len1, len2; + char *str; + va_list args2; + + va_copy(args2, args); + + len1 = vsnprintf(NULL, 0, fmt, args); + if (len1 < 0) { + str = NULL; + goto end; + } + + str = malloc(len1 + 1); + if (!str) { + goto end; + } + + len2 = vsnprintf(str, len1 + 1, fmt, args2); + + ARGPAR_ASSERT(len1 == len2); + +end: + va_end(args2); + return str; +} + + +static +char *argpar_asprintf(const char *fmt, ...) +{ + va_list args; + char *str; + + va_start(args, fmt); + str = argpar_vasprintf(fmt, args); + va_end(args); + + return str; +} + +static +bool argpar_string_append_printf(char **str, const char *fmt, ...) +{ + char *new_str = NULL; + char *addendum; + bool success; + va_list args; + + ARGPAR_ASSERT(str); + + va_start(args, fmt); + addendum = argpar_vasprintf(fmt, args); + va_end(args); + + if (!addendum) { + success = false; + goto end; + } + + new_str = argpar_asprintf("%s%s", *str ? *str : "", addendum); + if (!new_str) { + success = false; + goto end; + } + + free(*str); + *str = new_str; + + success = true; + +end: + free(addendum); + + return success; +} + +ARGPAR_HIDDEN +void argpar_item_destroy(struct argpar_item *item) +{ + if (!item) { + goto end; + } + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt * const opt_item = (void *) item; + + free((void *) opt_item->arg); + } + + free(item); + +end: + return; +} + +static +bool push_item(struct argpar_item_array * const array, + struct argpar_item * const item) +{ + bool success; + + ARGPAR_ASSERT(array); + ARGPAR_ASSERT(item); + + if (array->n_items == array->n_alloc) { + unsigned int new_n_alloc = array->n_alloc * 2; + struct argpar_item **new_items; + + new_items = argpar_realloc(array->items, + struct argpar_item *, new_n_alloc); + if (!new_items) { + success = false; + goto end; + } + + array->n_alloc = new_n_alloc; + array->items = new_items; + } + + array->items[array->n_items] = item; + array->n_items++; + + success = true; + +end: + return success; +} + +static +void destroy_item_array(struct argpar_item_array * const array) +{ + if (array) { + unsigned int i; + + for (i = 0; i < array->n_items; i++) { + argpar_item_destroy(array->items[i]); + } + + free(array->items); + free(array); + } +} + +static +struct argpar_item_array *new_item_array(void) +{ + struct argpar_item_array *ret; + const int initial_size = 10; + + ret = argpar_zalloc(struct argpar_item_array); + if (!ret) { + goto end; + } + + ret->items = argpar_calloc(struct argpar_item *, initial_size); + if (!ret->items) { + goto error; + } + + ret->n_alloc = initial_size; + + goto end; + +error: + destroy_item_array(ret); + ret = NULL; + +end: + return ret; +} + +static +struct argpar_item_opt *create_opt_item( + const struct argpar_opt_descr * const descr, + const char * const arg) +{ + struct argpar_item_opt *opt_item = + argpar_zalloc(struct argpar_item_opt); + + if (!opt_item) { + goto end; + } + + opt_item->base.type = ARGPAR_ITEM_TYPE_OPT; + opt_item->descr = descr; + + if (arg) { + opt_item->arg = strdup(arg); + if (!opt_item->arg) { + goto error; + } + } + + goto end; + +error: + argpar_item_destroy(&opt_item->base); + opt_item = NULL; + +end: + return opt_item; +} + +static +struct argpar_item_non_opt *create_non_opt_item(const char * const arg, + const unsigned int orig_index, + const unsigned int non_opt_index) +{ + struct argpar_item_non_opt * const non_opt_item = + argpar_zalloc(struct argpar_item_non_opt); + + if (!non_opt_item) { + goto end; + } + + non_opt_item->base.type = ARGPAR_ITEM_TYPE_NON_OPT; + non_opt_item->arg = arg; + non_opt_item->orig_index = orig_index; + non_opt_item->non_opt_index = non_opt_index; + +end: + return non_opt_item; +} + +static +const struct argpar_opt_descr *find_descr( + const struct argpar_opt_descr * const descrs, + const char short_name, const char * const long_name) +{ + const struct argpar_opt_descr *descr; + + for (descr = descrs; descr->short_name || descr->long_name; descr++) { + if (short_name && descr->short_name && + short_name == descr->short_name) { + goto end; + } + + if (long_name && descr->long_name && + strcmp(long_name, descr->long_name) == 0) { + goto end; + } + } + +end: + return !descr->short_name && !descr->long_name ? NULL : descr; +} + +enum parse_orig_arg_opt_ret { + PARSE_ORIG_ARG_OPT_RET_OK, + PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT = -2, + PARSE_ORIG_ARG_OPT_RET_ERROR = -1, +}; + +static +enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts, + const char * const next_orig_arg, + const struct argpar_opt_descr * const descrs, + struct argpar_state *state, + char **error, + struct argpar_item **item) +{ + enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; + bool used_next_orig_arg = false; + + if (strlen(short_opts) == 0) { + argpar_string_append_printf(error, "Invalid argument"); + goto error; + } + + if (!state->short_opt_ch) { + state->short_opt_ch = short_opts; + } + + const char *opt_arg = NULL; + const struct argpar_opt_descr *descr; + struct argpar_item_opt *opt_item; + + /* Find corresponding option descriptor */ + descr = find_descr(descrs, *state->short_opt_ch, NULL); + if (!descr) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; + argpar_string_append_printf(error, + "Unknown option `-%c`", *state->short_opt_ch); + goto error; + } + + if (descr->with_arg) { + if (state->short_opt_ch[1]) { + /* `-oarg` form */ + opt_arg = &state->short_opt_ch[1]; + } else { + /* `-o arg` form */ + opt_arg = next_orig_arg; + used_next_orig_arg = true; + } + + /* + * We accept `-o ''` (empty option's argument), + * but not `-o` alone if an option's argument is + * expected. + */ + if (!opt_arg || (state->short_opt_ch[1] && strlen(opt_arg) == 0)) { + argpar_string_append_printf(error, + "Missing required argument for option `-%c`", + *state->short_opt_ch); + used_next_orig_arg = false; + goto error; + } + } + + /* Create and append option argument */ + opt_item = create_opt_item(descr, opt_arg); + if (!opt_item) { + goto error; + } + + *item = &opt_item->base; + + state->short_opt_ch++; + + if (descr->with_arg || !*state->short_opt_ch) { + /* Option has an argument: no more options */ + state->short_opt_ch = NULL; + + if (used_next_orig_arg) { + state->i += 2; + } else { + state->i += 1; + } + } + + goto end; + +error: + if (ret == PARSE_ORIG_ARG_OPT_RET_OK) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR; + } + +end: + return ret; +} + +static +enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg, + const char * const next_orig_arg, + const struct argpar_opt_descr * const descrs, + struct argpar_state *state, + char **error, + struct argpar_item **item) +{ + const size_t max_len = 127; + enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; + const struct argpar_opt_descr *descr; + struct argpar_item_opt *opt_item; + bool used_next_orig_arg = false; + + /* Option's argument, if any */ + const char *opt_arg = NULL; + + /* Position of first `=`, if any */ + const char *eq_pos; + + /* Buffer holding option name when `long_opt_arg` contains `=` */ + char buf[max_len + 1]; + + /* Option name */ + const char *long_opt_name = long_opt_arg; + + if (strlen(long_opt_arg) == 0) { + argpar_string_append_printf(error, + "Invalid argument"); + goto error; + } + + /* Find the first `=` in original argument */ + eq_pos = strchr(long_opt_arg, '='); + if (eq_pos) { + const size_t long_opt_name_size = eq_pos - long_opt_arg; + + /* Isolate the option name */ + if (long_opt_name_size > max_len) { + argpar_string_append_printf(error, + "Invalid argument `--%s`", long_opt_arg); + goto error; + } + + memcpy(buf, long_opt_arg, long_opt_name_size); + buf[long_opt_name_size] = '\0'; + long_opt_name = buf; + } + + /* Find corresponding option descriptor */ + descr = find_descr(descrs, '\0', long_opt_name); + if (!descr) { + argpar_string_append_printf(error, + "Unknown option `--%s`", long_opt_name); + ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT; + goto error; + } + + /* Find option's argument if any */ + if (descr->with_arg) { + if (eq_pos) { + /* `--long-opt=arg` style */ + opt_arg = eq_pos + 1; + } else { + /* `--long-opt arg` style */ + if (!next_orig_arg) { + argpar_string_append_printf(error, + "Missing required argument for option `--%s`", + long_opt_name); + goto error; + } + + opt_arg = next_orig_arg; + used_next_orig_arg = true; + } + } + + /* Create and append option argument */ + opt_item = create_opt_item(descr, opt_arg); + if (!opt_item) { + goto error; + } + + if (used_next_orig_arg) { + state->i += 2; + } else { + state->i += 1; + } + + *item = &opt_item->base; + goto end; + +error: + if (ret == PARSE_ORIG_ARG_OPT_RET_OK) { + ret = PARSE_ORIG_ARG_OPT_RET_ERROR; + } + +end: + return ret; +} + +static +enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg, + const char * const next_orig_arg, + const struct argpar_opt_descr * const descrs, + struct argpar_state *state, + char **error, + struct argpar_item **item) +{ + enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK; + + ARGPAR_ASSERT(orig_arg[0] == '-'); + + if (orig_arg[1] == '-') { + /* Long option */ + ret = parse_long_opt(&orig_arg[2], + next_orig_arg, descrs, state, error, item); + } else { + /* Short option */ + ret = parse_short_opts(&orig_arg[1], + next_orig_arg, descrs, state, error, item); + } + + return ret; +} + +static +bool prepend_while_parsing_arg_to_error(char **error, + const unsigned int i, const char * const arg) +{ + char *new_error; + bool success; + + ARGPAR_ASSERT(error); + ARGPAR_ASSERT(*error); + + new_error = argpar_asprintf("While parsing argument #%u (`%s`): %s", + i + 1, arg, *error); + if (!new_error) { + success = false; + goto end; + } + + free(*error); + *error = new_error; + success = true; + +end: + return success; +} + +ARGPAR_HIDDEN +struct argpar_state *argpar_state_create( + unsigned int argc, + const char * const *argv, + const struct argpar_opt_descr * const descrs) +{ + struct argpar_state *state; + + state = argpar_zalloc(struct argpar_state); + if (!state) { + goto end; + } + + state->argc = argc; + state->argv = argv; + state->descrs = descrs; + +end: + return state; +} + +ARGPAR_HIDDEN +void argpar_state_destroy(struct argpar_state *state) +{ + free(state); +} + +ARGPAR_HIDDEN +enum argpar_state_parse_next_status argpar_state_parse_next( + struct argpar_state *state, + struct argpar_item **item, + char **error) +{ + enum argpar_state_parse_next_status status; + + ARGPAR_ASSERT(state->i <= state->argc); + + *error = NULL; + + if (state->i == state->argc) { + status = ARGPAR_STATE_PARSE_NEXT_STATUS_END; + goto end; + } + + enum parse_orig_arg_opt_ret parse_orig_arg_opt_ret; + const char * const orig_arg = state->argv[state->i]; + const char * const next_orig_arg = + state->i < (state->argc - 1) ? state->argv[state->i + 1] : NULL; + + if (orig_arg[0] != '-') { + /* Non-option argument */ + struct argpar_item_non_opt *non_opt_item = + create_non_opt_item(orig_arg, state->i, state->non_opt_index); + + if (!non_opt_item) { + status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR; + goto end; + } + + state->non_opt_index++; + state->i++; + + *item = &non_opt_item->base; + status = ARGPAR_STATE_PARSE_NEXT_STATUS_OK; + goto end; + } + + /* Option argument */ + parse_orig_arg_opt_ret = parse_orig_arg_opt(orig_arg, + next_orig_arg, state->descrs, state, error, item); + switch (parse_orig_arg_opt_ret) { + case PARSE_ORIG_ARG_OPT_RET_OK: + status = ARGPAR_STATE_PARSE_NEXT_STATUS_OK; + break; + case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT: + status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT; + break;; + case PARSE_ORIG_ARG_OPT_RET_ERROR: + prepend_while_parsing_arg_to_error( + error, state->i, orig_arg); + status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR; + break; + default: + abort(); + } + +end: + return status; +} + +ARGPAR_HIDDEN +int argpar_state_get_ingested_orig_args(struct argpar_state *state) +{ + return state->i; +} + +ARGPAR_HIDDEN +struct argpar_parse_ret argpar_parse(unsigned int argc, + const char * const *argv, + const struct argpar_opt_descr * const descrs, + bool fail_on_unknown_opt) +{ + struct argpar_parse_ret parse_ret = { 0 }; + struct argpar_item *item = NULL; + struct argpar_state *state = NULL; + + parse_ret.items = new_item_array(); + if (!parse_ret.items) { + parse_ret.error = strdup("Failed to create items array."); + ARGPAR_ASSERT(parse_ret.error); + goto error; + } + + state = argpar_state_create(argc, argv, descrs); + if (!state) { + parse_ret.error = strdup("Failed to create argpar state."); + ARGPAR_ASSERT(parse_ret.error); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + status = argpar_state_parse_next(state, &item, &parse_ret.error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + if (fail_on_unknown_opt) { + parse_ret.ingested_orig_args = + argpar_state_get_ingested_orig_args(state); + prepend_while_parsing_arg_to_error( + &parse_ret.error, parse_ret.ingested_orig_args, + argv[parse_ret.ingested_orig_args]); + status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR; + goto error; + } + + free(parse_ret.error); + parse_ret.error = NULL; + break; + } + + ARGPAR_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (!push_item(parse_ret.items, item)) { + goto error; + } + item = NULL; + } + + ARGPAR_ASSERT(!parse_ret.error); + parse_ret.ingested_orig_args = + argpar_state_get_ingested_orig_args(state); + goto end; + +error: + ARGPAR_ASSERT(parse_ret.error); + + /* That's how we indicate that an error occured */ + destroy_item_array(parse_ret.items); + parse_ret.items = NULL; + +end: + argpar_state_destroy(state); + argpar_item_destroy(item); + return parse_ret; +} + +ARGPAR_HIDDEN +void argpar_parse_ret_fini(struct argpar_parse_ret *ret) +{ + ARGPAR_ASSERT(ret); + + destroy_item_array(ret->items); + ret->items = NULL; + + free(ret->error); + ret->error = NULL; +} diff --git a/src/common/argpar/argpar.h b/src/common/argpar/argpar.h new file mode 100644 index 000000000..1442f5183 --- /dev/null +++ b/src/common/argpar/argpar.h @@ -0,0 +1,330 @@ +#ifndef BABELTRACE_ARGPAR_H +#define BABELTRACE_ARGPAR_H + +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * argpar is a library that provides facilities for argument parsing. + * + * Two APIs are available: + * + * - The iterator-style API, where you initialize a state object with + * `argpar_state_create`, then repeatedly call `argpar_state_parse_next` to + * get the arguments, until (1) there are no more arguments, (2) the parser + * encounters an error (e.g. unknown option) or (3) you get bored. This + * API gives you more control on when to stop parsing the arguments. + * + * - The parse-everything-in-one-shot-API, where you call `argpar_parse`, + * which parses the arguments until (1) there are not more arguments or + * (2) it encounters a parser error. It returns you a list of all the + * arguments it was able to parse, which you can consult at your leisure. + * + * The following describes how arguments are parsed, and applies to both APIs. + * + * argpar parses the arguments `argv` of which the count is `argc` using the + * sentinel-terminated (use `ARGPAR_OPT_DESCR_SENTINEL`) option + * descriptor array `descrs`. + * + * argpar considers ALL the elements of `argv`, including the* first one, so + * that you would typically pass `argc - 1` and `&argv[1]` from what main() + * receives. + * + * This argument parser supports: + * + * * Short options without an argument, possibly tied together: + * + * -f -auf -n + * + * * Short options with argument: + * + * -b 45 -f/mein/file -xyzhello + * + * * Long options without an argument: + * + * --five-guys --burger-king --pizza-hut --subway + * + * * Long options with arguments: + * + * --security enable --time=18.56 + * + * * Non-option arguments (anything else). + * + * This parser does not accept `-` or `--` as arguments. The latter + * means "end of options" for many command-line tools, but this function + * is all about keeping the order of the arguments, so it does not mean + * much to put them at the end. This has the side effect that a + * non-option argument cannot have the form of an option, for example if + * you need to pass the exact relative path `--component`. In that case, + * you would need to pass `./--component`. There's no generic way to + * escape `-` for the moment. + * + * This parser accepts duplicate options (it will output one item for each + * instance). + * + * The returned items are of the type `struct argpar_item *`. Each item + * is to be casted to the appropriate type (`struct argpar_item_opt *` or + * `struct argpar_item_non_opt *`) depending on its type. + * + * The items are returned in the same order that the arguments were parsed, + * including non-option arguments. This means, for example, that for + * + * --hello --meow=23 /path/to/file -b + * + * found items are returned in this order: option item (--hello), option item + * (--meow=23), non-option item (/path/to/file) and option item (-b). + */ + +#include + +/* Sentinel for an option descriptor array */ +#define ARGPAR_OPT_DESCR_SENTINEL { -1, '\0', NULL, false } + +/* + * ARGPAR_HIDDEN: if argpar is used in some shared library, we don't want them + * to be exported by that library, so mark them as "hidden". + * + * On Windows, symbols are local unless explicitly exported, + * see https://gcc.gnu.org/wiki/Visibility + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define ARGPAR_HIDDEN +#else +#define ARGPAR_HIDDEN __attribute__((visibility("hidden"))) +#endif + +/* Forward-declaration for the opaque type. */ +struct argpar_state; + +/* Option descriptor */ +struct argpar_opt_descr { + /* Numeric ID for this option */ + const int id; + + /* Short option character, or `\0` */ + const char short_name; + + /* Long option name (without `--`), or `NULL` */ + const char * const long_name; + + /* True if this option has an argument */ + const bool with_arg; +}; + +/* Item type */ +enum argpar_item_type { + /* Option */ + ARGPAR_ITEM_TYPE_OPT, + + /* Non-option */ + ARGPAR_ITEM_TYPE_NON_OPT, +}; + +/* Base item */ +struct argpar_item { + enum argpar_item_type type; +}; + +/* Option item */ +struct argpar_item_opt { + struct argpar_item base; + + /* Corresponding descriptor */ + const struct argpar_opt_descr *descr; + + /* Argument, or `NULL` if none */ + const char *arg; +}; + +/* Non-option item */ +struct argpar_item_non_opt { + struct argpar_item base; + + /* + * Complete argument, pointing to one of the entries of the + * original arguments (`argv`). + */ + const char *arg; + + /* Index of this argument amongst all original arguments (`argv`) */ + unsigned int orig_index; + + /* Index of this argument amongst other non-option arguments */ + unsigned int non_opt_index; +}; + +struct argpar_item_array { + /* Array of `struct argpar_item *`, or `NULL` on error */ + struct argpar_item **items; + + /* Number of used slots in `items`. */ + unsigned int n_items; + + /* Number of allocated slots in `items`. */ + unsigned int n_alloc; +}; + +/* What is returned by argpar_parse() */ +struct argpar_parse_ret { + /* Array of `struct argpar_item *`, or `NULL` on error */ + struct argpar_item_array *items; + + /* Error string, or `NULL` if none */ + char *error; + + /* Number of original arguments (`argv`) ingested */ + unsigned int ingested_orig_args; +}; + +/* + * Parses arguments in `argv` until the end is reached or an error is + * encountered. + * + * On success, this function returns an array of items + * (field `items` of `struct argpar_parse_ret`) corresponding to each parsed + * argument. + * + * In the returned structure, `ingested_orig_args` is the number of + * ingested arguments within `argv` to produce the resulting array of + * items. + * + * If `fail_on_unknown_opt` is true, then on success `ingested_orig_args` is + * equal to `argc`. Otherwise, `ingested_orig_args` contains the number of + * original arguments until an unknown _option_ occurs. For example, with + * + * --great --white contact nuance --shark nuclear + * + * if `--shark` is not described within `descrs` and + * `fail_on_unknown_opt` is false, then `ingested_orig_args` is 4 (two + * options, two non-options), whereas `argc` is 6. + * + * This makes it possible to know where a command name is, for example. + * With those arguments: + * + * --verbose --stuff=23 do-something --specific-opt -f -b + * + * and the descriptors for `--verbose` and `--stuff` only, the function + * returns the `--verbose` and `--stuff` option items, the + * `do-something` non-option item, and that three original arguments + * were ingested. This means you can start the next argument parsing + * stage, with option descriptors depending on the command name, at + * `&argv[3]`. + * + * Note that `ingested_orig_args` is not always equal to the number of + * returned items, as + * + * --hello -fdw + * + * for example contains two ingested original arguments, but four + * resulting items. + * + * On failure, the returned structure's `items` member is `NULL`, and + * the `error` string member contains details about the error. + * + * You can finalize the returned structure with + * argpar_parse_ret_fini(). + */ +ARGPAR_HIDDEN +struct argpar_parse_ret argpar_parse(unsigned int argc, + const char * const *argv, + const struct argpar_opt_descr *descrs, + bool fail_on_unknown_opt); + +/* + * Finalizes what is returned by argpar_parse(). + * + * It is safe to call argpar_parse() multiple times with the same + * structure. + */ +ARGPAR_HIDDEN +void argpar_parse_ret_fini(struct argpar_parse_ret *ret); + +/* + * Creates an instance of `struct argpar_state`. + * + * This sets up the argpar_state structure, but does not actually + * start parsing the arguments. + * + * When you are done with it, the state must be freed with + * `argpar_state_destroy`. + */ +ARGPAR_HIDDEN +struct argpar_state *argpar_state_create( + unsigned int argc, + const char * const *argv, + const struct argpar_opt_descr * const descrs); + +/* + * Destroys an instance of `struct argpar_state`. + */ +ARGPAR_HIDDEN +void argpar_state_destroy(struct argpar_state *state); + + +enum argpar_state_parse_next_status { + ARGPAR_STATE_PARSE_NEXT_STATUS_OK, + ARGPAR_STATE_PARSE_NEXT_STATUS_END, + ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT, + ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR, +}; + +/* + * Parses and returns the next argument from `state`. + * + * On success, an item describing the argument is returned in `*item` and + * ARGPAR_STATE_PARSE_NEXT_STATUS_OK is returned. The item must be freed with + * `argpar_item_destroy`. + * + * If there are no more arguments to parse, ARGPAR_STATE_PARSE_NEXT_STATUS_END + * is returned. + * + * On failure (status codes ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT and + * ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR), an error string is returned in `*error`. + * This string must be freed with `free`. + */ +enum argpar_state_parse_next_status argpar_state_parse_next( + struct argpar_state *state, + struct argpar_item **item, + char **error); + +/* + * Return the number of ingested elements from argv that were required to + * produce the previously returned items. + */ +ARGPAR_HIDDEN +int argpar_state_get_ingested_orig_args(struct argpar_state *state); + +/* + * Destroy an instance of `struct argpar_item`, as returned by + * argpar_state_parse_next. + */ +ARGPAR_HIDDEN +void argpar_item_destroy(struct argpar_item *item); + +#define ARGPAR_ITEM_DESTROY_AND_RESET(_item) \ + { \ + argpar_item_destroy(_item); \ + _item = NULL; \ + } + + +#endif /* BABELTRACE_ARGPAR_H */ diff --git a/src/common/buffer-view.c b/src/common/buffer-view.c index 4bdb1eb7d..106542391 100644 --- a/src/common/buffer-view.c +++ b/src/common/buffer-view.c @@ -67,3 +67,45 @@ struct lttng_buffer_view lttng_buffer_view_from_dynamic_buffer( end: return view; } + +LTTNG_HIDDEN +bool lttng_buffer_view_validate_string(const struct lttng_buffer_view *buf, + const char *str, + size_t len_with_null_terminator) +{ + const char *past_buf_end; + size_t max_str_len_with_null_terminator; + size_t str_len; + bool ret; + + past_buf_end = buf->data + buf->size; + + /* Is the start of the string in the buffer view? */ + if (str < buf->data || str >= past_buf_end) { + ret = false; + goto end; + } + + /* + * Max length the string could have to fit in the buffer, including + * NULL terminator. + */ + max_str_len_with_null_terminator = past_buf_end - str; + + /* Could the string even fit in the buffer? */ + if (len_with_null_terminator > max_str_len_with_null_terminator) { + ret = false; + goto end; + } + + str_len = lttng_strnlen(str, max_str_len_with_null_terminator); + if (str_len != (len_with_null_terminator - 1)) { + ret = false; + goto end; + } + + ret = true; + +end: + return ret; +} diff --git a/src/common/buffer-view.h b/src/common/buffer-view.h index e8c351b3f..4486558aa 100644 --- a/src/common/buffer-view.h +++ b/src/common/buffer-view.h @@ -8,9 +8,10 @@ #ifndef LTTNG_BUFFER_VIEW_H #define LTTNG_BUFFER_VIEW_H +#include +#include #include #include -#include struct lttng_dynamic_buffer; @@ -70,4 +71,22 @@ struct lttng_buffer_view lttng_buffer_view_from_dynamic_buffer( const struct lttng_dynamic_buffer *src, size_t offset, ptrdiff_t len); +/** + * Validate that string starting at `str` of length `len_with_null_terminator` + * (including the NULL terminator) fits within the buffer view, and validate + * that it is indeed of that length. + * + * Return true if the validation passes, false otherwise. + * + * @buf The buffer view + * @str The start of the string + * @len_with_null_terminator Expected length of the string, including the + * NULL terminator. + * + */ +LTTNG_HIDDEN +bool lttng_buffer_view_validate_string(const struct lttng_buffer_view *buf, + const char *str, + size_t len_with_null_terminator); + #endif /* LTTNG_BUFFER_VIEW_H */ diff --git a/src/common/buffer-usage.c b/src/common/conditions/buffer-usage.c similarity index 99% rename from src/common/buffer-usage.c rename to src/common/conditions/buffer-usage.c index 92081c910..465a9263d 100644 --- a/src/common/buffer-usage.c +++ b/src/common/conditions/buffer-usage.c @@ -91,7 +91,8 @@ end: static int lttng_condition_buffer_usage_serialize( const struct lttng_condition *condition, - struct lttng_dynamic_buffer *buf) + struct lttng_dynamic_buffer *buf, + int *fd_to_send) { int ret; struct lttng_condition_buffer_usage *usage; @@ -149,6 +150,11 @@ int lttng_condition_buffer_usage_serialize( if (ret) { goto end; } + + if (fd_to_send) { + /* No fd to send */ + *fd_to_send = -1; + } end: return ret; } diff --git a/src/common/condition.c b/src/common/conditions/condition.c similarity index 79% rename from src/common/condition.c rename to src/common/conditions/condition.c index 076f0627c..b5e9aebbb 100644 --- a/src/common/condition.c +++ b/src/common/conditions/condition.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -55,7 +56,8 @@ end: LTTNG_HIDDEN int lttng_condition_serialize(const struct lttng_condition *condition, - struct lttng_dynamic_buffer *buf) + struct lttng_dynamic_buffer *buf, + int *fd_to_send) { int ret; struct lttng_condition_comm condition_comm = { 0 }; @@ -73,7 +75,7 @@ int lttng_condition_serialize(const struct lttng_condition *condition, goto end; } - ret = condition->serialize(condition, buf); + ret = condition->serialize(condition, buf, fd_to_send); if (ret) { goto end; } @@ -139,6 +141,9 @@ ssize_t lttng_condition_create_from_buffer( case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: create_from_buffer = lttng_condition_session_rotation_completed_create_from_buffer; break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + create_from_buffer = lttng_condition_event_rule_create_from_buffer; + break; default: ERR("Attempted to create condition of unknown type (%i)", (int) condition_comm->condition_type); @@ -172,3 +177,33 @@ void lttng_condition_init(struct lttng_condition *condition, { condition->type = type; } + +LTTNG_HIDDEN +const char *lttng_condition_type_str(enum lttng_condition_type type) +{ + switch (type) { + case LTTNG_CONDITION_TYPE_UNKNOWN: + return "unknown"; + + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + return "session consumed size"; + + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + return "buffer usage high"; + + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + return "buffer usage low"; + + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + return "session rotation ongoing"; + + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + return "session rotation completed"; + + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + return "event rule hit"; + + default: + return "???"; + } +} diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/event-rule.c new file mode 100644 index 000000000..aa1f1364a --- /dev/null +++ b/src/common/conditions/event-rule.c @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define IS_EVENT_RULE_CONDITION(condition) ( \ + lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT \ + ) + +static +bool is_event_rule_evaluation(const struct lttng_evaluation *evaluation) +{ + enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); + + return type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; +} + + +static +bool lttng_condition_event_rule_validate( + const struct lttng_condition *condition); +static +int lttng_condition_event_rule_serialize( + const struct lttng_condition *condition, + struct lttng_dynamic_buffer *buf, + int *fd_to_send); +static +bool lttng_condition_event_rule_is_equal(const struct lttng_condition *_a, + const struct lttng_condition *_b); +static +void lttng_condition_event_rule_destroy( + struct lttng_condition *condition); + + +static +bool lttng_condition_event_rule_validate( + const struct lttng_condition *condition) +{ + bool valid = false; + struct lttng_condition_event_rule *event_rule; + + if (!condition) { + goto end; + } + + event_rule = container_of(condition, + struct lttng_condition_event_rule, parent); + if (!event_rule->rule) { + ERR("Invalid session event_rule condition: a rule must be set."); + goto end; + } + + valid = lttng_event_rule_validate(event_rule->rule); +end: + return valid; +} + +static +int lttng_condition_event_rule_serialize( + const struct lttng_condition *condition, + struct lttng_dynamic_buffer *buf, + int *fd_to_send) +{ + int ret; + size_t header_offset, size_before_payload; + struct lttng_condition_event_rule *event_rule; + struct lttng_condition_event_rule_comm event_rule_comm = { 0 }; + struct lttng_condition_event_rule_comm *header; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition)) { + ret = -1; + goto end; + } + + DBG("Serializing event rule condition"); + event_rule = container_of(condition, struct lttng_condition_event_rule, + parent); + + header_offset = buf->size; + ret = lttng_dynamic_buffer_append(buf, &event_rule_comm, + sizeof(event_rule_comm)); + if (ret) { + goto end; + } + + size_before_payload = buf->size; + ret = lttng_event_rule_serialize(event_rule->rule, buf, fd_to_send); + if (ret) { + goto end; + } + + /* Update payload size */ + header = (struct lttng_condition_event_rule_comm *) ((char *) buf->data + header_offset); + header->length = buf->size - size_before_payload; + +end: + return ret; +} + +static +bool lttng_condition_event_rule_is_equal(const struct lttng_condition *_a, + const struct lttng_condition *_b) +{ + bool is_equal = false; + struct lttng_condition_event_rule *a, *b; + + a = container_of(_a, struct lttng_condition_event_rule, parent); + b = container_of(_b, struct lttng_condition_event_rule, parent); + + /* Both session names must be set or both must be unset. */ + if ((a->rule && !b->rule) || + (!a->rule && b->rule)) { + WARN("Comparing session event_rule conditions with uninitialized rule."); + goto end; + } + + is_equal = lttng_event_rule_is_equal(a->rule, b->rule); +end: + return is_equal; +} + +static +void lttng_condition_event_rule_destroy( + struct lttng_condition *condition) +{ + struct lttng_condition_event_rule *event_rule; + + event_rule = container_of(condition, + struct lttng_condition_event_rule, parent); + + + lttng_event_rule_destroy(event_rule->rule); + free(event_rule); +} + +struct lttng_condition *lttng_condition_event_rule_create( + struct lttng_event_rule *rule) +{ + struct lttng_condition *parent = NULL; + struct lttng_condition_event_rule *condition = NULL; + + if (!rule) { + goto end; + } + + condition = zmalloc(sizeof(struct lttng_condition_event_rule)); + if (!condition) { + return NULL; + } + + lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + condition->parent.validate = lttng_condition_event_rule_validate, + condition->parent.serialize = lttng_condition_event_rule_serialize, + condition->parent.equal = lttng_condition_event_rule_is_equal, + condition->parent.destroy = lttng_condition_event_rule_destroy, + + condition->rule = rule; + parent = &condition->parent; +end: + return parent; +} + +LTTNG_HIDDEN +ssize_t lttng_condition_event_rule_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_condition **_condition) +{ + ssize_t offset, event_rule_size; + const struct lttng_condition_event_rule_comm *comm; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_buffer_view event_rule_view; + + if (!view || !_condition) { + goto error; + } + + if (view->size < sizeof(*comm)) { + ERR("Failed to initialize from malformed event rule condition: buffer too short to contain header"); + goto error; + } + + comm = (const struct lttng_condition_event_rule_comm *) view->data; + offset = sizeof(*comm); + + /* Struct lttng_event_rule */ + event_rule_view = lttng_buffer_view_from_view(view, offset, -1); + event_rule_size = lttng_event_rule_create_from_buffer(&event_rule_view, &event_rule); + if (event_rule_size < 0 || !event_rule) { + goto error; + } + + if ((size_t) comm->length != event_rule_size) { + goto error; + } + + /* Move to the end */ + offset += comm->length; + + condition = lttng_condition_event_rule_create(event_rule); + if (!condition) { + goto error; + } + + /* Ownership passed on condition event rule create */ + event_rule = NULL; + + *_condition = condition; + condition = NULL; + goto end; + +error: + offset = -1; + +end: + lttng_event_rule_destroy(event_rule); + lttng_condition_destroy(condition); + return offset; +} + +LTTNG_HIDDEN +enum lttng_condition_status +lttng_condition_event_rule_get_rule_no_const( + const struct lttng_condition *condition, + struct lttng_event_rule **rule) +{ + struct lttng_condition_event_rule *event_rule; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !rule) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + event_rule = container_of(condition, struct lttng_condition_event_rule, + parent); + if (!event_rule->rule) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *rule = event_rule->rule; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_event_rule_get_rule( + const struct lttng_condition *condition, + const struct lttng_event_rule **rule) +{ + struct lttng_event_rule *no_const_rule = NULL; + enum lttng_condition_status status; + + status = lttng_condition_event_rule_get_rule_no_const(condition, &no_const_rule); + *rule = no_const_rule; + return status; +} + +LTTNG_HIDDEN +ssize_t lttng_evaluation_event_rule_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret, offset = 0; + const char *name; + struct lttng_evaluation *evaluation = NULL; + const struct lttng_evaluation_event_rule_comm *comm = + (const struct lttng_evaluation_event_rule_comm *) view->data; + struct lttng_buffer_view current_view; + + if (!_evaluation) { + ret = -1; + goto error; + } + + if (view->size < sizeof(*comm)) { + ret = -1; + goto error; + } + + /* Map the name, view of the payload */ + offset += sizeof(*comm); + current_view = lttng_buffer_view_from_view(view, offset, comm->trigger_name_length); + name = current_view.data; + if (!name) { + ret = -1; + goto error; + } + + if (comm->trigger_name_length == 1 || + name[comm->trigger_name_length - 1] != '\0' || + strlen(name) != comm->trigger_name_length - 1) { + /* + * Check that the name is not NULL, is NULL-terminated, and + * does not contain a NULL before the last byte. + */ + ret = -1; + goto error; + } + + offset += comm->trigger_name_length; + + evaluation = lttng_evaluation_event_rule_create(name); + if (!evaluation) { + ret = -1; + goto error; + } + + *_evaluation = evaluation; + evaluation = NULL; + ret = offset; + +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +static +int lttng_evaluation_event_rule_serialize( + const struct lttng_evaluation *evaluation, + struct lttng_dynamic_buffer *buf) +{ + int ret = 0; + struct lttng_evaluation_event_rule *hit; + struct lttng_evaluation_event_rule_comm comm; + + hit = container_of(evaluation, struct lttng_evaluation_event_rule, + parent); + comm.trigger_name_length = strlen(hit->name) + 1; + ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(buf, hit->name, comm.trigger_name_length); +end: + return ret; +} + +static +void lttng_evaluation_event_rule_destroy( + struct lttng_evaluation *evaluation) +{ + struct lttng_evaluation_event_rule *hit; + + hit = container_of(evaluation, struct lttng_evaluation_event_rule, + parent); + free(hit->name); + free(hit); +} + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_event_rule_create(const char *trigger_name) +{ + struct lttng_evaluation_event_rule *hit; + + hit = zmalloc(sizeof(struct lttng_evaluation_event_rule)); + if (!hit) { + goto end; + } + + /* TODO errir handling */ + hit->name = strdup(trigger_name); + + hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; + hit->parent.serialize = lttng_evaluation_event_rule_serialize; + hit->parent.destroy = lttng_evaluation_event_rule_destroy; +end: + return &hit->parent; +} + +enum lttng_evaluation_status +lttng_evaluation_event_rule_get_trigger_name( + const struct lttng_evaluation *evaluation, + const char **name) +{ + struct lttng_evaluation_event_rule *hit; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !is_event_rule_evaluation(evaluation) || !name) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + hit = container_of(evaluation, struct lttng_evaluation_event_rule, + parent); + *name = hit->name; +end: + return status; +} diff --git a/src/common/session-consumed-size.c b/src/common/conditions/session-consumed-size.c similarity index 99% rename from src/common/session-consumed-size.c rename to src/common/conditions/session-consumed-size.c index dfb72632e..6e0fcb49e 100644 --- a/src/common/session-consumed-size.c +++ b/src/common/conditions/session-consumed-size.c @@ -65,7 +65,8 @@ end: static int lttng_condition_session_consumed_size_serialize( const struct lttng_condition *condition, - struct lttng_dynamic_buffer *buf) + struct lttng_dynamic_buffer *buf, + int *fd_to_send) { int ret; size_t session_name_len; @@ -102,6 +103,11 @@ int lttng_condition_session_consumed_size_serialize( if (ret) { goto end; } + + if (fd_to_send) { + /* No fd to send */ + *fd_to_send = -1; + } end: return ret; } diff --git a/src/common/session-rotation.c b/src/common/conditions/session-rotation.c similarity index 98% rename from src/common/session-rotation.c rename to src/common/conditions/session-rotation.c index f8d4439de..1c4124f64 100644 --- a/src/common/session-rotation.c +++ b/src/common/conditions/session-rotation.c @@ -19,7 +19,8 @@ bool lttng_condition_session_rotation_validate( static int lttng_condition_session_rotation_serialize( const struct lttng_condition *condition, - struct lttng_dynamic_buffer *buf); + struct lttng_dynamic_buffer *buf, + int *fd_to_send); static bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a, const struct lttng_condition *_b); @@ -95,7 +96,8 @@ end: static int lttng_condition_session_rotation_serialize( const struct lttng_condition *condition, - struct lttng_dynamic_buffer *buf) + struct lttng_dynamic_buffer *buf, + int *fd_to_send) { int ret; size_t session_name_len; @@ -128,6 +130,11 @@ int lttng_condition_session_rotation_serialize( if (ret) { goto end; } + + if (fd_to_send) { + /* No fd to send */ + *fd_to_send = -1; + } end: return ret; } diff --git a/src/common/credentials.c b/src/common/credentials.c new file mode 100644 index 000000000..c7d621fbc --- /dev/null +++ b/src/common/credentials.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 - Jonathan Rajotte-Julien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include "credentials.h" + +bool lttng_credentials_is_equal(const struct lttng_credentials *a, + const struct lttng_credentials *b) +{ + assert(a); + assert(b); + if ((a->uid != b->uid) || (a->gid != b->gid)) { + return false; + } + return true; +}; diff --git a/src/common/credentials.h b/src/common/credentials.h index a0d93308c..16b65575b 100644 --- a/src/common/credentials.h +++ b/src/common/credentials.h @@ -9,10 +9,15 @@ #define LTTNG_CREDENTIALS_H #include +#include +#include struct lttng_credentials { uid_t uid; gid_t gid; }; +bool lttng_credentials_is_equal(const struct lttng_credentials *a, + const struct lttng_credentials *b); + #endif /* LTTNG_CREDENTIALS_H */ diff --git a/src/common/domain.c b/src/common/domain.c new file mode 100644 index 000000000..5d6e7a078 --- /dev/null +++ b/src/common/domain.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 - EfficiOS, inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "lttng/domain-internal.h" +#include "common/macros.h" + +LTTNG_HIDDEN +const char *lttng_domain_type_str(enum lttng_domain_type domain_type) +{ + switch (domain_type) { + case LTTNG_DOMAIN_NONE: + return "none"; + + case LTTNG_DOMAIN_KERNEL: + return "kernel"; + + case LTTNG_DOMAIN_UST: + return "ust"; + + case LTTNG_DOMAIN_JUL: + return "jul"; + + case LTTNG_DOMAIN_LOG4J: + return "log4j"; + + case LTTNG_DOMAIN_PYTHON: + return "python"; + + default: + return "???"; + } +} diff --git a/src/common/dynamic-array.h b/src/common/dynamic-array.h index 208dd1956..2b3ea6273 100644 --- a/src/common/dynamic-array.h +++ b/src/common/dynamic-array.h @@ -121,6 +121,23 @@ void *lttng_dynamic_pointer_array_get_pointer( return *element; } +/* + * Returns the pointer at index `index`, sets the array slot to NULL. Does not + * run the destructor. + */ + +static inline +void *lttng_dynamic_pointer_array_steal_pointer( + struct lttng_dynamic_pointer_array *array, size_t index) +{ + void **p_element = lttng_dynamic_array_get_element(&array->array, index); + void *element = *p_element; + + *p_element = NULL; + + return element; +} + /* * Add a pointer to the end of a dynamic pointer array. The array's element * count is increased by one and its underlying capacity is adjusted diff --git a/src/common/error.c b/src/common/error.c index ed81b8b45..8983652f9 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -7,14 +7,15 @@ #define _LGPL_SOURCE #include +#include #include +#include #include #include -#include -#include #include #include +#include #include "error.h" @@ -28,6 +29,7 @@ static int lttng_opt_abort_on_error = -1; /* TLS variable that contains the time of one single log entry. */ DEFINE_URCU_TLS(struct log_time, error_log_time); +DEFINE_URCU_TLS(const char *, logger_thread_name); LTTNG_HIDDEN const char *log_add_time(void) @@ -66,6 +68,30 @@ error: return ""; } +LTTNG_HIDDEN +void logger_set_thread_name(const char *name, bool set_pthread_name) +{ + int ret; + + assert(name); + URCU_TLS(logger_thread_name) = name; + + if (set_pthread_name) { + char pthread_name[16]; + + /* + * Truncations are expected since pthread limits thread names to + * a generous 16 characters. + */ + strncpy(pthread_name, name, sizeof(pthread_name)); + pthread_name[sizeof(pthread_name) - 1] = '\0'; + ret = pthread_setname_np(pthread_self(), pthread_name); + if (ret) { + DBG("Failed to set pthread name attribute"); + } + } +} + /* * Human readable error message. */ diff --git a/src/common/error.h b/src/common/error.h index 5218869da..d3a60c582 100644 --- a/src/common/error.h +++ b/src/common/error.h @@ -15,6 +15,8 @@ #include #include #include +#include +#include #ifndef _GNU_SOURCE #error "lttng-tools error.h needs _GNU_SOURCE" @@ -42,6 +44,7 @@ struct log_time { char str[19]; }; extern DECLARE_URCU_TLS(struct log_time, error_log_time); +extern DECLARE_URCU_TLS(const char *, logger_thread_name); extern int lttng_opt_quiet; extern int lttng_opt_verbose; @@ -139,13 +142,44 @@ static inline void __lttng_print_check_abort(enum lttng_error_level type) } while (0) /* Three level of debug. Use -v, -vv or -vvv for the levels */ -#define _ERRMSG(msg, type, fmt, args...) __lttng_print(type, msg \ - " - %s [%ld/%ld]: " fmt " (in %s() at " __FILE__ ":" XSTR(__LINE__) ")\n", \ - log_add_time(), (long) getpid(), (long) lttng_gettid(), ## args, __func__) +#define _ERRMSG(msg, type, fmt, args...) \ + do { \ + if (caa_unlikely(__lttng_print_check_opt(type))) { \ + char generic_name[MAX_INT_DEC_LEN(long) + \ + MAX_INT_DEC_LEN(long)]; \ + \ + snprintf(generic_name, sizeof(generic_name), \ + "%ld/%ld", (long) getpid(), \ + (long) lttng_gettid()); \ + \ + __lttng_print(type, \ + msg " - %s [%s]: " fmt \ + " (in %s() at " __FILE__ \ + ":" XSTR(__LINE__) ")\n", \ + log_add_time(), \ + URCU_TLS(logger_thread_name) ?: \ + generic_name, \ + ##args, __func__); \ + } \ + } while (0) -#define _ERRMSG_NO_LOC(msg, type, fmt, args...) __lttng_print(type, msg \ - " - %s [%ld/%ld]: " fmt "\n", \ - log_add_time(), (long) getpid(), (long) lttng_gettid(), ## args) +#define _ERRMSG_NO_LOC(msg, type, fmt, args...) \ + do { \ + if (caa_unlikely(__lttng_print_check_opt(type))) { \ + char generic_name[MAX_INT_DEC_LEN(long) + \ + MAX_INT_DEC_LEN(long)]; \ + \ + snprintf(generic_name, sizeof(generic_name), \ + "%ld/%ld", (long) getpid(), \ + (long) lttng_gettid()); \ + \ + __lttng_print(type, msg " - %s [%s]: " fmt "\n", \ + log_add_time(), \ + URCU_TLS(logger_thread_name) ?: \ + generic_name, \ + ##args); \ + } \ + } while (0) #define MSG(fmt, args...) \ __lttng_print(PRINT_MSG, fmt "\n", ## args) @@ -158,10 +192,10 @@ static inline void __lttng_print_check_abort(enum lttng_error_level type) #define BUG(fmt, args...) _ERRMSG("BUG", PRINT_BUG, fmt, ## args) -#define DBG(fmt, args...) _ERRMSG("DEBUG1", PRINT_DBG, fmt, ## args) -#define DBG_NO_LOC(fmt, args...) _ERRMSG_NO_LOC("DEBUG1", PRINT_DBG, fmt, ## args) -#define DBG2(fmt, args...) _ERRMSG("DEBUG2", PRINT_DBG2, fmt, ## args) -#define DBG3(fmt, args...) _ERRMSG("DEBUG3", PRINT_DBG3, fmt, ## args) +#define DBG(fmt, args...) _ERRMSG("DBG1", PRINT_DBG, fmt, ## args) +#define DBG_NO_LOC(fmt, args...) _ERRMSG_NO_LOC("DBG1", PRINT_DBG, fmt, ## args) +#define DBG2(fmt, args...) _ERRMSG("DBG2", PRINT_DBG2, fmt, ## args) +#define DBG3(fmt, args...) _ERRMSG("DBG3", PRINT_DBG3, fmt, ## args) #define LOG(type, fmt, args...) \ do { \ switch (type) { \ @@ -226,4 +260,8 @@ const char *error_get_str(int32_t code); */ const char *log_add_time(); +/* Name must be a statically-allocated string. */ +LTTNG_HIDDEN +void logger_set_thread_name(const char *name, bool set_pthread_name); + #endif /* _ERROR_H */ diff --git a/src/common/evaluation.c b/src/common/evaluation.c index efd212921..2e098900c 100644 --- a/src/common/evaluation.c +++ b/src/common/evaluation.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,13 @@ ssize_t lttng_evaluation_create_from_buffer( } evaluation_size += ret; break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + ret = lttng_evaluation_event_rule_create_from_buffer(&evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; default: ERR("Attempted to create evaluation of unknown type (%i)", (int) evaluation_comm->type); diff --git a/src/common/event-rule-kprobe.c b/src/common/event-rule-kprobe.c new file mode 100644 index 000000000..8d4b8c474 --- /dev/null +++ b/src/common/event-rule-kprobe.c @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_KPROBE_EVENT_RULE(rule) ( \ + lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KPROBE \ + ) + +#if (LTTNG_SYMBOL_NAME_LEN == 256) +#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" +#endif + +static +void lttng_event_rule_kprobe_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_kprobe *kprobe; + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, + parent); + + free(kprobe->name); + free(kprobe->probe.symbol_name); + free(kprobe); +} + +static +bool lttng_event_rule_kprobe_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_kprobe *kprobe; + + if (!rule) { + goto end; + } + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + + /* Required field */ + if (!kprobe->name) { + ERR("Invalid name event rule: a name must be set."); + goto end; + } + if (kprobe->probe.set == LTTNG_DOMAIN_NONE) { + ERR("Invalid kprobe event rule: a source must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static +int lttng_event_rule_kprobe_serialize( + const struct lttng_event_rule *rule, + struct lttng_dynamic_buffer *buf, + int *fd_to_send) +{ + int ret; + size_t name_len, probe_symbol_name_len; + struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kprobe_comm kprobe_comm; + + if (!rule || !IS_KPROBE_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + DBG("Serializing kprobe event rule"); + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + + name_len = strlen(kprobe->name) + 1; + + if (kprobe->probe.symbol_name != NULL) { + probe_symbol_name_len = strlen(kprobe->probe.symbol_name) + 1; + } else { + probe_symbol_name_len = 0; + } + + kprobe_comm.name_len = name_len; + kprobe_comm.probe_symbol_name_len = probe_symbol_name_len; + kprobe_comm.probe_address = kprobe->probe.address; + kprobe_comm.probe_offset = kprobe->probe.offset; + + ret = lttng_dynamic_buffer_append( + buf, &kprobe_comm, sizeof(kprobe_comm)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(buf, kprobe->name, name_len); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append( + buf, kprobe->probe.symbol_name, probe_symbol_name_len); + if (ret) { + goto end; + } + + if (fd_to_send) { + /* Nothing to send */ + *fd_to_send = -1; + } +end: + return ret; +} + +static +bool lttng_event_rule_kprobe_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + bool is_equal = false; + struct lttng_event_rule_kprobe *a, *b; + + a = container_of(_a, struct lttng_event_rule_kprobe, parent); + b = container_of(_b, struct lttng_event_rule_kprobe, parent); + + /* Quick checks */ + if (!!a->name != !!b->name) { + goto end; + } + + if (!!a->probe.symbol_name != !!b->probe.symbol_name) { + goto end; + } + + /* Long check */ + /* kprobe is invalid if this is not true */ + /* TODO: validate that a kprobe MUST have a name */ + assert(a->name); + assert(b->name); + if (strcmp(a->name, b->name)) { + goto end; + } + + if (a->probe.symbol_name) { + /* Both have symbol name due to previous checks */ + if (strcmp(a->probe.symbol_name, b->probe.symbol_name)) { + goto end; + } + } + + if (a->probe.offset != b->probe.offset) { + goto end; + } + + if (a->probe.address != b->probe.address) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + +static +enum lttng_error_code lttng_event_rule_kprobe_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + /* Nothing to do */ + return LTTNG_OK; +} + +static const char *lttng_event_rule_kprobe_get_filter( + const struct lttng_event_rule *rule) +{ + /* Not supported */ + return NULL; +} + +static const struct lttng_filter_bytecode * +lttng_event_rule_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule) +{ + /* Not supported */ + return NULL; +} + +static +struct lttng_event_exclusion *lttng_event_rule_kprobe_generate_exclusions(struct lttng_event_rule *rule) +{ + /* Not supported */ + return NULL; +} + +struct lttng_event_rule *lttng_event_rule_kprobe_create() +{ + struct lttng_event_rule_kprobe *rule; + + rule = zmalloc(sizeof(struct lttng_event_rule_kprobe)); + if (!rule) { + return NULL; + } + + lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_KPROBE); + rule->parent.validate = lttng_event_rule_kprobe_validate; + rule->parent.serialize = lttng_event_rule_kprobe_serialize; + rule->parent.equal = lttng_event_rule_kprobe_is_equal; + rule->parent.destroy = lttng_event_rule_kprobe_destroy; + rule->parent.populate = lttng_event_rule_kprobe_populate; + rule->parent.get_filter = lttng_event_rule_kprobe_get_filter; + rule->parent.get_filter_bytecode = lttng_event_rule_kprobe_get_filter_bytecode; + rule->parent.generate_exclusions = lttng_event_rule_kprobe_generate_exclusions; + return &rule->parent; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kprobe_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **_event_rule) +{ + ssize_t ret, offset = 0; + enum lttng_event_rule_status status; + const struct lttng_event_rule_kprobe_comm *kprobe_comm; + const char *name; + const char *probe_symbol_name = NULL; + struct lttng_buffer_view current_view; + struct lttng_event_rule *rule = NULL; + struct lttng_event_rule_kprobe *kprobe = NULL; + + if (!_event_rule) { + ret = -1; + goto end; + } + + if (view->size < sizeof(*kprobe_comm)) { + ERR("Failed to initialize from malformed event rule kprobe: buffer too short to contain header"); + ret = -1; + goto end; + } + + current_view = lttng_buffer_view_from_view( + view, offset, sizeof(*kprobe_comm)); + kprobe_comm = (typeof(kprobe_comm)) current_view.data; + if (!kprobe_comm) { + ret = -1; + goto end; + } + + rule = lttng_event_rule_kprobe_create(); + if (!rule) { + ERR("Failed to create event rule kprobe"); + ret = -1; + goto end; + } + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + + /* Skip to payload */ + offset += current_view.size; + /* Map the name */ + current_view = lttng_buffer_view_from_view( + view, offset, kprobe_comm->name_len); + name = current_view.data; + if (!name) { + ret = -1; + goto end; + } + + if (kprobe_comm->name_len == 1 || + name[kprobe_comm->name_len - 1] != '\0' || + strlen(name) != kprobe_comm->name_len - 1) { + /* + * Check that the name is not NULL, is NULL-terminated, and + * does not contain a NULL before the last byte. + */ + ret = -1; + goto end; + } + + /* Skip after the name */ + offset += kprobe_comm->name_len; + if (!kprobe_comm->probe_symbol_name_len) { + goto skip_probe_symbol_name; + } + + /* Map the probe_symbol_name */ + current_view = lttng_buffer_view_from_view( + view, offset, kprobe_comm->probe_symbol_name_len); + probe_symbol_name = current_view.data; + if (!probe_symbol_name) { + ret = -1; + goto end; + } + + if (kprobe_comm->probe_symbol_name_len == 1 || + probe_symbol_name[kprobe_comm->probe_symbol_name_len - + 1] != '\0' || + strlen(probe_symbol_name) != + kprobe_comm->probe_symbol_name_len - + 1) { + /* + * Check that the filter expression is not NULL, is + * NULL-terminated, and does not contain a NULL before the last + * byte. + */ + ret = -1; + goto end; + } + + /* Skip after the pattern */ + offset += kprobe_comm->probe_symbol_name_len; + +skip_probe_symbol_name: + + status = lttng_event_rule_kprobe_set_name(rule, name); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule kprobe name"); + ret = -1; + goto end; + } + kprobe->probe.offset = kprobe_comm->probe_offset; + kprobe->probe.address = kprobe_comm->probe_address; + if (probe_symbol_name) { + kprobe->probe.symbol_name = strdup(probe_symbol_name); + if (!kprobe->probe.symbol_name) { + ERR("Failed to set event rule kprobe probe symbol name"); + ret = -1; + goto end; + } + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_kprobe_set_source( + struct lttng_event_rule *rule, const char *source) +{ + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + int match; + char s_hex[19]; + char name[LTTNG_SYMBOL_NAME_LEN]; + struct lttng_event_rule_kprobe *kprobe; + + /* TODO: support multiple call for this, we must free the symbol name if + * that happens !!! + */ + + if (!source || !IS_KPROBE_EVENT_RULE(rule) || !rule) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + + /* Check for symbol+offset */ + match = sscanf(source, "%18s[^'+']+%18s", name, s_hex); + if (match == 2) { + /* TODO double validate termination handling of this */ + kprobe->probe.symbol_name = + strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!kprobe->probe.symbol_name) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + if (*s_hex == '\0') { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + kprobe->probe.offset = strtoul(s_hex, NULL, 0); + kprobe->probe.address = 0; + kprobe->probe.set = true; + goto end; + } + + /* Check for symbol */ + if (isalpha(name[0]) || name[0] == '_') { + match = sscanf(source, + "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "s", + name); + if (match == 1) { + /* TODO double validate termination handling of this */ + kprobe->probe.symbol_name = + strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!kprobe->probe.symbol_name) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + kprobe->probe.offset = 0; + kprobe->probe.address = 0; + kprobe->probe.set = true; + goto end; + } + } + + /* Check for address */ + match = sscanf(source, "%18s", s_hex); + if (match > 0) { + if (*s_hex == '\0') { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + kprobe->probe.address = strtoul(s_hex, NULL, 0); + kprobe->probe.offset = 0; + kprobe->probe.symbol_name = NULL; + kprobe->probe.set = true; + goto end; + } + + /* No match */ + status = LTTNG_EVENT_RULE_STATUS_INVALID; + +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( + struct lttng_event_rule *rule, const char *name) +{ + char *name_copy = NULL; + struct lttng_event_rule_kprobe *kprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name || + strlen(name) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (kprobe->name) { + free(kprobe->name); + } + + kprobe->name = name_copy; + name_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_kprobe_get_name( + const struct lttng_event_rule *rule, const char **name) +{ + struct lttng_event_rule_kprobe *kprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + if (!kprobe->name) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *name = kprobe->name; +end: + return status; +} + +LTTNG_HIDDEN +uint64_t lttng_event_rule_kprobe_get_address( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_kprobe *kprobe; + + assert(rule && IS_KPROBE_EVENT_RULE(rule)); + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + + return kprobe->probe.address; +} + +LTTNG_HIDDEN +uint64_t lttng_event_rule_kprobe_get_offset( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_kprobe *kprobe; + + assert(rule && IS_KPROBE_EVENT_RULE(rule)); + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + return kprobe->probe.offset; +} + +LTTNG_HIDDEN +const char *lttng_event_rule_kprobe_get_symbol_name( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_kprobe *kprobe; + + assert(rule && IS_KPROBE_EVENT_RULE(rule)); + + kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + return kprobe->probe.symbol_name; +} diff --git a/src/common/event-rule-kretprobe.c b/src/common/event-rule-kretprobe.c new file mode 100644 index 000000000..56916d9c9 --- /dev/null +++ b/src/common/event-rule-kretprobe.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +#define IS_KRETPROBE_EVENT_RULE(rule) ( \ + lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KRETPROBE \ + ) + +static +void lttng_event_rule_kretprobe_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_kretprobe *kretprobe; + + kretprobe = container_of(rule, struct lttng_event_rule_kretprobe, + parent); + + /* + * TODO + */ + free(kretprobe); +} + +static +bool lttng_event_rule_kretprobe_validate( + const struct lttng_event_rule *rule) +{ + /* TODO */ + return false; +} + +static +int lttng_event_rule_kretprobe_serialize( + const struct lttng_event_rule *rule, + struct lttng_dynamic_buffer *buf, int *fd_to_send) +{ + /* TODO */ + return -1; +} + +static +bool lttng_event_rule_kretprobe_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + /* TODO */ + return false; +} + +static +enum lttng_error_code lttng_event_rule_kretprobe_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + /* Nothing to do */ + return LTTNG_OK; +} + +static const char *lttng_event_rule_kretprobe_get_filter( + const struct lttng_event_rule *rule) +{ + /* Not supported */ + return NULL; +} + +static const struct lttng_filter_bytecode * +lttng_event_rule_kretprobe_get_filter_bytecode( + const struct lttng_event_rule *rule) +{ + /* Not supported */ + return NULL; +} + +static struct lttng_event_exclusion * +lttng_event_rule_kretprobe_generate_exclusions(struct lttng_event_rule *rule) +{ + /* Not supported */ + return NULL; +} + +struct lttng_event_rule *lttng_event_rule_kretprobe_create() +{ + struct lttng_event_rule_kretprobe *rule; + + rule = zmalloc(sizeof(struct lttng_event_rule_kretprobe)); + if (!rule) { + return NULL; + } + + lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_KRETPROBE); + rule->parent.validate = lttng_event_rule_kretprobe_validate; + rule->parent.serialize = lttng_event_rule_kretprobe_serialize; + rule->parent.equal = lttng_event_rule_kretprobe_is_equal; + rule->parent.destroy = lttng_event_rule_kretprobe_destroy; + rule->parent.populate = lttng_event_rule_kretprobe_populate; + rule->parent.get_filter = lttng_event_rule_kretprobe_get_filter; + rule->parent.get_filter_bytecode = lttng_event_rule_kretprobe_get_filter_bytecode; + rule->parent.generate_exclusions = lttng_event_rule_kretprobe_generate_exclusions; + return &rule->parent; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kretprobe_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **_event_rule) +{ + /* TODO */ + return -1; +} + +enum lttng_event_rule_status lttng_event_rule_kretprobe_set_source( + struct lttng_event_rule *rule, const char *source) +{ + return LTTNG_EVENT_RULE_STATUS_UNSUPPORTED; +} + +enum lttng_event_rule_status lttng_event_rule_kretprobe_set_name( + struct lttng_event_rule *rule, const char *name) +{ + return LTTNG_EVENT_RULE_STATUS_UNSUPPORTED; +} + +enum lttng_event_rule_status lttng_event_rule_kretprobe_get_name( + const struct lttng_event_rule *rule, const char **name) +{ + return LTTNG_EVENT_RULE_STATUS_UNSUPPORTED; +} + +LTTNG_HIDDEN +uint64_t lttng_event_rule_kretprobe_get_address( + const struct lttng_event_rule *rule) +{ + assert("Not implemented" && 0); +} + +LTTNG_HIDDEN +uint64_t lttng_event_rule_kretprobe_get_offset( + const struct lttng_event_rule *rule) +{ + assert("Not implemented" && 0); +} + +LTTNG_HIDDEN +const char *lttng_event_rule_kretprobe_get_symbol_name( + const struct lttng_event_rule *rule) +{ + assert("Not implemented" && 0); +} diff --git a/src/common/event-rule-syscall.c b/src/common/event-rule-syscall.c new file mode 100644 index 000000000..97f5f86b1 --- /dev/null +++ b/src/common/event-rule-syscall.c @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +#define IS_SYSCALL_EVENT_RULE(rule) ( \ + lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_SYSCALL \ + ) + +static +void lttng_event_rule_syscall_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_syscall *syscall; + + if (rule == NULL) { + return; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, + parent); + + free(syscall->pattern); + free(syscall->filter_expression); + free(syscall->internal_filter.filter); + free(syscall->internal_filter.bytecode); + free(syscall); +} + +static +bool lttng_event_rule_syscall_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_syscall *syscall; + + if (!rule) { + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + + /* Required field */ + if (!syscall->pattern) { + ERR("Invalid syscall event rule: a pattern must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static +int lttng_event_rule_syscall_serialize( + const struct lttng_event_rule *rule, + struct lttng_dynamic_buffer *buf, + int *fd_to_send) +{ + int ret; + size_t pattern_len, filter_expression_len; + struct lttng_event_rule_syscall *syscall; + struct lttng_event_rule_syscall_comm syscall_comm; + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + DBG("Serializing syscall event rule"); + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + + pattern_len = strlen(syscall->pattern) + 1; + + if (syscall->filter_expression != NULL) { + filter_expression_len = strlen(syscall->filter_expression) + 1; + } else { + filter_expression_len = 0; + } + + syscall_comm.pattern_len = pattern_len; + syscall_comm.filter_expression_len = filter_expression_len; + + ret = lttng_dynamic_buffer_append( + buf, &syscall_comm, sizeof(syscall_comm)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(buf, syscall->pattern, pattern_len); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append( + buf, syscall->filter_expression, filter_expression_len); + if (ret) { + goto end; + } + + if (fd_to_send) { + /* Nothing to send */ + *fd_to_send = -1; + } +end: + return ret; +} + +static +bool lttng_event_rule_syscall_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + bool is_equal = false; + struct lttng_event_rule_syscall *a, *b; + + a = container_of(_a, struct lttng_event_rule_syscall, parent); + b = container_of(_b, struct lttng_event_rule_syscall, parent); + + if (!!a->filter_expression != !!b->filter_expression) { + goto end; + } + + /* Long check */ + /* syscall is invalid if this is not true */ + assert(a->pattern); + assert(b->pattern); + if (strcmp(a->pattern, b->pattern)) { + goto end; + } + + if (a->filter_expression && b->filter_expression) { + if (strcmp(a->filter_expression, b->filter_expression)) { + goto end; + } + } + + is_equal = true; +end: + return is_equal; +} + +static +enum lttng_error_code lttng_event_rule_syscall_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + int ret; + enum lttng_error_code ret_code = LTTNG_OK; + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status; + const char *filter; + struct lttng_filter_bytecode *bytecode = NULL; + + assert(rule); + + syscall = container_of(rule, struct lttng_event_rule_syscall, + parent); + + /* Generate the filter bytecode */ + status = lttng_event_rule_syscall_get_filter(rule, &filter); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { + filter = NULL; + } else if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto end; + } + + if (filter && filter[0] == '\0') { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto error; + } + + if (filter == NULL) { + /* Nothing to do */ + ret = LTTNG_OK; + goto end; + } + + syscall->internal_filter.filter = strdup(filter); + if (syscall->internal_filter.filter == NULL) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret = run_as_generate_filter_bytecode(syscall->internal_filter.filter, uid, gid, &bytecode); + if (ret) { + ret_code = LTTNG_ERR_FILTER_INVAL; + } + + syscall->internal_filter.bytecode = bytecode; + bytecode = NULL; + +error: +end: + free(bytecode); + return ret_code; +} + +static const char *lttng_event_rule_syscall_get_internal_filter( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_syscall *syscall; + assert(rule); + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + return syscall->internal_filter.filter; +} + +static const struct lttng_filter_bytecode * +lttng_event_rule_syscall_get_internal_filter_bytecode( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_syscall *syscall; + assert(rule); + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + return syscall->internal_filter.bytecode; +} + +static struct lttng_event_exclusion * +lttng_event_rule_syscall_generate_exclusions(struct lttng_event_rule *rule) +{ + /* Not supported */ + return NULL; +} + +struct lttng_event_rule *lttng_event_rule_syscall_create() +{ + struct lttng_event_rule_syscall *rule; + + rule = zmalloc(sizeof(struct lttng_event_rule_syscall)); + if (!rule) { + return NULL; + } + + lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_SYSCALL); + rule->parent.validate = lttng_event_rule_syscall_validate; + rule->parent.serialize = lttng_event_rule_syscall_serialize; + rule->parent.equal = lttng_event_rule_syscall_is_equal; + rule->parent.destroy = lttng_event_rule_syscall_destroy; + rule->parent.populate = lttng_event_rule_syscall_populate; + rule->parent.get_filter = lttng_event_rule_syscall_get_internal_filter; + rule->parent.get_filter_bytecode = + lttng_event_rule_syscall_get_internal_filter_bytecode; + rule->parent.generate_exclusions = + lttng_event_rule_syscall_generate_exclusions; + return &rule->parent; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_syscall_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **_event_rule) +{ + ssize_t ret, offset = 0; + enum lttng_event_rule_status status; + const struct lttng_event_rule_syscall_comm *syscall_comm; + const char *pattern; + const char *filter_expression = NULL; + struct lttng_buffer_view current_view; + struct lttng_event_rule *rule = NULL; + + if (!_event_rule) { + ret = -1; + goto end; + } + + if (view->size < sizeof(*syscall_comm)) { + ERR("Failed to initialize from malformed event rule syscall: buffer too short to contain header"); + ret = -1; + goto end; + } + + current_view = lttng_buffer_view_from_view( + view, offset, sizeof(*syscall_comm)); + syscall_comm = (typeof(syscall_comm)) current_view.data; + + if (!syscall_comm) { + ret = -1; + goto end; + } + + rule = lttng_event_rule_syscall_create(); + if (!rule) { + ERR("Failed to create event rule syscall"); + ret = -1; + goto end; + } + + /* Skip to payload */ + offset += current_view.size; + + /* Map the pattern */ + current_view = lttng_buffer_view_from_view( + view, offset, syscall_comm->pattern_len); + pattern = current_view.data; + if (!pattern) { + ret = -1; + goto end; + } + + if (syscall_comm->pattern_len == 1 || + pattern[syscall_comm->pattern_len - 1] != '\0' || + strlen(pattern) != syscall_comm->pattern_len - 1) { + /* + * Check that the pattern is not NULL, is NULL-terminated, and + * does not contain a NULL before the last byte. + */ + ret = -1; + goto end; + } + + /* Skip after the pattern */ + offset += syscall_comm->pattern_len; + + if (!syscall_comm->filter_expression_len) { + goto skip_filter_expression; + } + + /* Map the filter_expression */ + current_view = lttng_buffer_view_from_view( + view, offset, syscall_comm->filter_expression_len); + filter_expression = current_view.data; + if (!filter_expression) { + ret = -1; + goto end; + } + + if (syscall_comm->filter_expression_len == 1 || + filter_expression[syscall_comm->filter_expression_len - + 1] != '\0' || + strlen(filter_expression) != + syscall_comm->filter_expression_len - + 1) { + /* + * Check that the filter expression is not NULL, is + * NULL-terminated, and does not contain a NULL before the last + * byte. + */ + ret = -1; + goto end; + } + + /* Skip after the pattern */ + offset += syscall_comm->filter_expression_len; + +skip_filter_expression: + + status = lttng_event_rule_syscall_set_pattern(rule, pattern); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule syscall pattern"); + ret = -1; + goto end; + } + + if (filter_expression) { + status = lttng_event_rule_syscall_set_filter( + rule, filter_expression); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule syscall pattern"); + ret = -1; + goto end; + } + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_syscall_set_pattern( + struct lttng_event_rule *rule, const char *pattern) +{ + char *pattern_copy = NULL; + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern || + strlen(pattern) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + pattern_copy = strdup(pattern); + if (!pattern_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (syscall->pattern) { + free(syscall->pattern); + } + + syscall->pattern = pattern_copy; + pattern_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_syscall_get_pattern( + const struct lttng_event_rule *rule, const char **pattern) +{ + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + if (!syscall->pattern) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *pattern = syscall->pattern; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_syscall_set_filter( + struct lttng_event_rule *rule, const char *expression) +{ + char *expression_copy = NULL; + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + /* TODO: validate that the passed expression is valid */ + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression || + strlen(expression) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + expression_copy = strdup(expression); + if (!expression_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (syscall->filter_expression) { + free(syscall->filter_expression); + } + + syscall->filter_expression = expression_copy; + expression_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_syscall_get_filter( + const struct lttng_event_rule *rule, const char **expression) +{ + struct lttng_event_rule_syscall *syscall; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + syscall = container_of(rule, struct lttng_event_rule_syscall, parent); + if (!syscall->filter_expression) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *expression = syscall->filter_expression; +end: + return status; +} diff --git a/src/common/event-rule-tracepoint.c b/src/common/event-rule-tracepoint.c new file mode 100644 index 000000000..f66e64167 --- /dev/null +++ b/src/common/event-rule-tracepoint.c @@ -0,0 +1,1125 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define IS_TRACEPOINT_EVENT_RULE(rule) ( \ + lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT \ + ) + +static +void lttng_event_rule_tracepoint_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_tracepoint *tracepoint; + + if (rule == NULL) { + return; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + + free(tracepoint->pattern); + free(tracepoint->filter_expression); + for(int i = 0; i < tracepoint->exclusions.count; i++) { + free(tracepoint->exclusions.values[i]); + } + free(tracepoint->exclusions.values); + free(tracepoint->internal_filter.filter); + free(tracepoint->internal_filter.bytecode); + free(tracepoint); +} + +static +bool lttng_event_rule_tracepoint_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_tracepoint *tracepoint; + + if (!rule) { + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + + /* Required field */ + if (!tracepoint->pattern) { + ERR("Invalid tracepoint event rule: a pattern must be set."); + goto end; + } + if (tracepoint->domain == LTTNG_DOMAIN_NONE) { + ERR("Invalid tracepoint event rule: a domain must be set."); + goto end; + } + + /* QUESTION: do we validate inside state on validate or during set of + * each component */ + + valid = true; +end: + return valid; +} + +static +int lttng_event_rule_tracepoint_serialize( + const struct lttng_event_rule *rule, + struct lttng_dynamic_buffer *buf, + int *fd_to_send) +{ + int ret; + size_t pattern_len, filter_expression_len, exclusions_len; + struct lttng_event_rule_tracepoint *tracepoint; + struct lttng_event_rule_tracepoint_comm tracepoint_comm; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + DBG("Serializing tracepoint event rule"); + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + + pattern_len = strlen(tracepoint->pattern) + 1; + + if (tracepoint->filter_expression != NULL) { + filter_expression_len = + strlen(tracepoint->filter_expression) + 1; + } else { + filter_expression_len = 0; + } + + exclusions_len = 0; + for (int i = 0; i < tracepoint->exclusions.count; i++) { + /* Payload */ + exclusions_len += strlen(tracepoint->exclusions.values[i]) + 1; + /* Bound check */ + exclusions_len += sizeof(uint32_t); + } + + tracepoint_comm.domain_type = (int8_t) tracepoint->domain; + tracepoint_comm.loglevel_type = (int8_t) tracepoint->loglevel.type; + tracepoint_comm.loglevel_value = tracepoint->loglevel.value; + tracepoint_comm.pattern_len = pattern_len; + tracepoint_comm.filter_expression_len = filter_expression_len; + tracepoint_comm.exclusions_count = tracepoint->exclusions.count; + tracepoint_comm.exclusions_len = exclusions_len; + + ret = lttng_dynamic_buffer_append( + buf, &tracepoint_comm, sizeof(tracepoint_comm)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(buf, tracepoint->pattern, + pattern_len); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(buf, tracepoint->filter_expression, + filter_expression_len); + if (ret) { + goto end; + } + + size_t exclusions_appended = 0; + for (int i = 0; i < tracepoint->exclusions.count; i++) { + size_t len; + len = strlen(tracepoint->exclusions.values[i]) + 1; + /* Append bound check, does not include the '\0' */ + ret = lttng_dynamic_buffer_append(buf, &len, sizeof(uint32_t)); + if (ret) { + goto end; + } + exclusions_appended += sizeof(uint32_t); + + /* Include the '\0' in the payload */ + ret = lttng_dynamic_buffer_append(buf, tracepoint->exclusions.values[i], + len); + if (ret) { + goto end; + } + exclusions_appended += len; + } + + assert(exclusions_len == exclusions_appended); + + if (fd_to_send) { + /* No fd to send */ + *fd_to_send = -1; + } + +end: + return ret; +} + +static +bool lttng_event_rule_tracepoint_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + bool is_equal = false; + struct lttng_event_rule_tracepoint *a, *b; + + a = container_of(_a, struct lttng_event_rule_tracepoint, parent); + b = container_of(_b, struct lttng_event_rule_tracepoint, parent); + + /* Quick checks */ + if (a->domain != b->domain) { + goto end; + } + + if (a->exclusions.count != b->exclusions.count) { + goto end; + } + + if (!!a->filter_expression != !!b->filter_expression) { + goto end; + } + + /* Long check */ + /* Tracepoint is invalid if this is not true */ + assert(a->pattern); + assert(b->pattern); + if (strcmp(a->pattern, b->pattern)) { + goto end; + } + + if (a->filter_expression && b->filter_expression) { + if (strcmp(a->filter_expression, b->filter_expression)) { + goto end; + } + } + + if (a->loglevel.type != b->loglevel.type) { + goto end; + } + + if (a->loglevel.value != b->loglevel.value) { + goto end; + } + + for (int i = 0; i < a->exclusions.count; i++) { + if (strcmp(a->exclusions.values[i], b->exclusions.values[i])) { + goto end; + } + } + + is_equal = true; +end: + return is_equal; +} + +/* + * On success ret is 0; + * + * On error ret is negative. + * + * An event with NO loglevel and the name is * will return NULL. + */ +static int generate_agent_filter( + const struct lttng_event_rule *rule, + char **_agent_filter) +{ + int err; + int ret = 0; + char *agent_filter = NULL; + const char *pattern; + const char *filter; + enum lttng_loglevel_type loglevel_type; + enum lttng_event_rule_status status; + + assert(rule); + assert(_agent_filter); + + status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_event_rule_tracepoint_get_filter(rule, &filter); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { + filter = NULL; + } else if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_event_rule_tracepoint_get_loglevel_type(rule, &loglevel_type); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = -1; + goto end; + } + + /* Don't add filter for the '*' event. */ + if (strcmp(pattern, "*") != 0) { + if (filter) { + err = asprintf(&agent_filter, "(%s) && (logger_name == \"%s\")", filter, + pattern); + } else { + err = asprintf(&agent_filter, "logger_name == \"%s\"", pattern); + } + if (err < 0) { + PERROR("asprintf"); + ret = -1; + goto end; + } + } + + if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + const char *op; + int loglevel_value; + + status = lttng_event_rule_tracepoint_get_loglevel(rule, &loglevel_value); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = -1; + goto end; + } + + if (loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE) { + op = ">="; + } else { + op = "=="; + } + + if (filter || agent_filter) { + char *new_filter; + + err = asprintf(&new_filter, "(%s) && (int_loglevel %s %d)", + agent_filter ? agent_filter : filter, op, + loglevel_value); + if (agent_filter) { + free(agent_filter); + } + agent_filter = new_filter; + } else { + err = asprintf(&agent_filter, "int_loglevel %s %d", op, + loglevel_value); + } + if (err < 0) { + PERROR("asprintf"); + ret = -1; + goto end; + } + } + + *_agent_filter = agent_filter; + agent_filter = NULL; + +end: + free(agent_filter); + return ret; +} + +static +enum lttng_error_code lttng_event_rule_tracepoint_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + int ret; + enum lttng_error_code ret_code; + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_domain_type domain_type; + enum lttng_event_rule_status status; + const char *filter; + struct lttng_filter_bytecode *bytecode = NULL; + + assert(rule); + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + + status = lttng_event_rule_tracepoint_get_filter(rule, &filter); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { + filter = NULL; + } else if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto end; + } + + if (filter && filter[0] == '\0') { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto error; + } + + status = lttng_event_rule_tracepoint_get_domain_type(rule, &domain_type); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret_code = LTTNG_ERR_UNK; + goto error; + } + + switch (domain_type) { + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_PYTHON: + { + char *agent_filter; + ret = generate_agent_filter(rule, &agent_filter); + if (ret) { + ret_code = LTTNG_ERR_FILTER_INVAL; + goto error; + } + tracepoint->internal_filter.filter = agent_filter; + break; + } + default: + { + if (filter) { + tracepoint->internal_filter.filter = strdup(filter); + if (tracepoint->internal_filter.filter == NULL) { + ret_code = LTTNG_ERR_NOMEM; + goto error; + } + } else { + tracepoint->internal_filter.filter = NULL; + } + break; + } + } + + if (tracepoint->internal_filter.filter == NULL) { + ret_code = LTTNG_OK; + goto end; + } + + ret = run_as_generate_filter_bytecode(tracepoint->internal_filter.filter, uid, gid, &bytecode); + if (ret) { + ret_code = LTTNG_ERR_FILTER_INVAL; + } + + tracepoint->internal_filter.bytecode = bytecode; + bytecode = NULL; + ret_code = LTTNG_OK; + +error: +end: + free(bytecode); + return ret_code; + +} + +static const char *lttng_event_rule_tracepoint_get_internal_filter( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_tracepoint *tracepoint; + assert(rule); + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + return tracepoint->internal_filter.filter; +} + +static const struct lttng_filter_bytecode * +lttng_event_rule_tracepoint_get_internal_filter_bytecode( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_tracepoint *tracepoint; + assert(rule); + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + return tracepoint->internal_filter.bytecode; +} + +/* TODO: review error handling, the function should be able to + * return error information. + */ +static struct lttng_event_exclusion * +lttng_event_rule_tracepoint_generate_exclusions(struct lttng_event_rule *rule) +{ + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + struct lttng_event_exclusion *local_exclusions = NULL; + struct lttng_event_exclusion *ret_exclusions = NULL; + unsigned int nb_exclusions = 0; + + (void) lttng_event_rule_tracepoint_get_domain_type(rule, &domain_type); + + switch (domain_type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + /* Not supported */ + ret_exclusions = NULL; + goto end; + case LTTNG_DOMAIN_UST: + /* Exclusions supported */ + break; + default: + assert(0); + } + + (void) lttng_event_rule_tracepoint_get_exclusions_count(rule, &nb_exclusions); + if (nb_exclusions == 0) { + /* Nothing to do */ + ret_exclusions = NULL; + goto end; + } + + local_exclusions = zmalloc(sizeof(struct lttng_event_exclusion) + (LTTNG_SYMBOL_NAME_LEN * nb_exclusions)); + if (!local_exclusions) { + ERR("local exclusion allocation"); + ret_exclusions = NULL; + goto end; + } + + local_exclusions->count = nb_exclusions; + for (unsigned int i = 0; i < nb_exclusions; i++) { + /* TODO: check for truncation. + * Part of this should be validated on set exclusion + */ + const char *tmp; + (void) lttng_event_rule_tracepoint_get_exclusion_at_index(rule, i, &tmp); + strncpy(local_exclusions->names[i], tmp, LTTNG_SYMBOL_NAME_LEN); + local_exclusions->names[i][LTTNG_SYMBOL_NAME_LEN-1] = '\0'; + } + + /* Pass ownership */ + ret_exclusions = local_exclusions; + local_exclusions = NULL; +end: + free(local_exclusions); + /* Not supported */ + return ret_exclusions; +} + +static struct lttng_event *lttng_event_rule_tracepoint_generate_lttng_event( + const struct lttng_event_rule *rule) +{ + const struct lttng_event_rule_tracepoint *tracepoint; + struct lttng_event *local_event = NULL; + struct lttng_event *event = NULL; + + tracepoint = container_of( + rule, const struct lttng_event_rule_tracepoint, parent); + + local_event = zmalloc(sizeof(*local_event)); + if (!local_event) { + goto error; + } + + local_event->type = LTTNG_EVENT_TRACEPOINT; + (void) strncpy(local_event->name, tracepoint->pattern, + sizeof(local_event->name) - 1); + local_event->name[sizeof(local_event->name) - 1] = '\0'; + local_event->loglevel_type = tracepoint->loglevel.type; + local_event->loglevel = tracepoint->loglevel.value; + + event = local_event; + local_event = NULL; +error: + free(local_event); + return event; +} + +struct lttng_event_rule *lttng_event_rule_tracepoint_create(enum lttng_domain_type domain_type) +{ + struct lttng_event_rule_tracepoint *rule; + + if (domain_type == LTTNG_DOMAIN_NONE) { + return NULL; + } + + rule = zmalloc(sizeof(struct lttng_event_rule_tracepoint)); + if (!rule) { + return NULL; + } + + lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + rule->parent.validate = lttng_event_rule_tracepoint_validate; + rule->parent.serialize = lttng_event_rule_tracepoint_serialize; + rule->parent.equal = lttng_event_rule_tracepoint_is_equal; + rule->parent.destroy = lttng_event_rule_tracepoint_destroy; + rule->parent.populate = lttng_event_rule_tracepoint_populate; + rule->parent.get_filter = lttng_event_rule_tracepoint_get_internal_filter; + rule->parent.get_filter_bytecode = lttng_event_rule_tracepoint_get_internal_filter_bytecode; + rule->parent.generate_exclusions = lttng_event_rule_tracepoint_generate_exclusions; + rule->parent.generate_lttng_event = + lttng_event_rule_tracepoint_generate_lttng_event; + + rule->domain = domain_type; + rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL; + + return &rule->parent; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_tracepoint_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **_event_rule) +{ + ssize_t ret, offset = 0; + enum lttng_event_rule_status status; + enum lttng_domain_type domain_type; + enum lttng_loglevel_type loglevel_type; + const struct lttng_event_rule_tracepoint_comm *tracepoint_comm; + const char *pattern; + const char *filter_expression = NULL; + const char **exclusions = NULL; + const uint32_t *exclusion_len; + const char *exclusion; + struct lttng_buffer_view current_view; + struct lttng_event_rule *rule = NULL; + + if (!_event_rule) { + ret = -1; + goto end; + } + + if (view->size < sizeof(*tracepoint_comm)) { + ERR("Failed to initialize from malformed event rule tracepoint: buffer too short to contain header"); + ret = -1; + goto end; + } + + current_view = lttng_buffer_view_from_view(view, offset, sizeof(*tracepoint_comm)); + tracepoint_comm = (typeof(tracepoint_comm)) current_view.data; + + if(!tracepoint_comm) { + ret = -1; + goto end; + } + + if (tracepoint_comm->domain_type <= LTTNG_DOMAIN_NONE || + tracepoint_comm->domain_type > LTTNG_DOMAIN_PYTHON) { + /* Invalid domain value. */ + ERR("Invalid domain type value (%i) found in tracepoint_comm buffer", + (int) tracepoint_comm->domain_type); + ret = -1; + goto end; + } + + domain_type = (enum lttng_domain_type) tracepoint_comm->domain_type; + rule = lttng_event_rule_tracepoint_create(domain_type); + if (!rule) { + ERR("Failed to create event rule tracepoint"); + ret = -1; + goto end; + } + + loglevel_type = (enum lttng_loglevel_type) + tracepoint_comm->loglevel_type; + switch (loglevel_type) { + case LTTNG_EVENT_LOGLEVEL_ALL: + status = lttng_event_rule_tracepoint_set_loglevel_all(rule); + break; + case LTTNG_EVENT_LOGLEVEL_RANGE: + status = lttng_event_rule_tracepoint_set_loglevel_range( + rule, (enum lttng_loglevel_type) tracepoint_comm + ->loglevel_value); + break; + case LTTNG_EVENT_LOGLEVEL_SINGLE: + status = lttng_event_rule_tracepoint_set_loglevel( + rule, (enum lttng_loglevel_type) tracepoint_comm + ->loglevel_value); + break; + default: + ERR("Failed to set event rule tracepoint loglevel: unknown loglevel type"); + ret = -1; + goto end; + } + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule tracepoint loglevel"); + } + + /* Skip to payload */ + offset += current_view.size; + + /* Map the pattern */ + current_view = lttng_buffer_view_from_view(view, offset, tracepoint_comm->pattern_len); + pattern = current_view.data; + if (!pattern) { + ret = -1; + goto end; + } + + if (tracepoint_comm->pattern_len == 1 || + pattern[tracepoint_comm->pattern_len - 1] != '\0' || + strlen(pattern) != tracepoint_comm->pattern_len - 1) { + /* + * Check that the pattern is not NULL, is NULL-terminated, and + * does not contain a NULL before the last byte. + */ + ret = -1; + goto end; + } + + /* Skip after the pattern */ + offset += tracepoint_comm->pattern_len; + + if (!tracepoint_comm->filter_expression_len) { + goto skip_filter_expression; + } + + /* Map the filter_expression */ + current_view = lttng_buffer_view_from_view(view, offset, tracepoint_comm->filter_expression_len); + filter_expression = current_view.data; + if (!filter_expression) { + ret = -1; + goto end; + } + + if (tracepoint_comm->filter_expression_len == 1 || + filter_expression[tracepoint_comm->filter_expression_len - + 1] != '\0' || + strlen(filter_expression) != + tracepoint_comm->filter_expression_len - + 1) { + /* + * Check that the filter expression is not NULL, is + * NULL-terminated, and does not contain a NULL before the last + * byte. + */ + ret = -1; + goto end; + } + + /* Skip after the pattern */ + offset += tracepoint_comm->filter_expression_len; + +skip_filter_expression: + + if (!tracepoint_comm->exclusions_count) { + goto skip_exclusions; + } + + exclusions = zmalloc(sizeof(*exclusions) * tracepoint_comm->exclusions_count); + if (!exclusions) { + ret = -1; + goto end; + } + + for (int i = 0; i < tracepoint_comm->exclusions_count; i++) { + current_view = lttng_buffer_view_from_view(view, offset, sizeof(*exclusion_len)); + exclusion_len = (typeof(exclusion_len)) current_view.data; + if (!exclusion_len) { + ret = -1; + goto end; + } + + offset += sizeof(*exclusion_len); + current_view = lttng_buffer_view_from_view(view, offset, *exclusion_len); + exclusion = current_view.data; + if (*exclusion_len == 1 || + exclusion[*exclusion_len - 1] != '\0' || + strlen(exclusion) != *exclusion_len - 1) { + /* + * Check that the exclusion is not NULL, is + * NULL-terminated, and does not contain a NULL before + * the last byte. + */ + ret = -1; + goto end; + } + exclusions[i] = exclusion; + /* Skip to next exclusion */ + offset += *exclusion_len; + } + +skip_exclusions: + status = lttng_event_rule_tracepoint_set_pattern(rule, pattern); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule tracepoint pattern"); + ret = -1; + goto end; + } + + if (filter_expression) { + status = lttng_event_rule_tracepoint_set_filter( + rule, filter_expression); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule tracepoint pattern"); + ret = -1; + goto end; + } + } + + if (exclusions) { + status = lttng_event_rule_tracepoint_set_exclusions(rule, + tracepoint_comm->exclusions_count, exclusions); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule tracepoint exclusions"); + ret = -1; + goto end; + } + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + free(exclusions); + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_pattern( + struct lttng_event_rule *rule, const char *pattern) +{ + char *pattern_copy = NULL; + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !pattern || + strlen(pattern) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + pattern_copy = strdup(pattern); + if (!pattern_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (tracepoint->pattern) { + free(tracepoint->pattern); + } + + tracepoint->pattern = pattern_copy; + pattern_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_pattern( + const struct lttng_event_rule *rule, const char **pattern) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !pattern) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + if (!tracepoint->pattern) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *pattern = tracepoint->pattern; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_domain_type( + const struct lttng_event_rule *rule, + enum lttng_domain_type *type) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !type) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + *type = tracepoint->domain; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter( + struct lttng_event_rule *rule, const char *expression) +{ + char *expression_copy = NULL; + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + /* TODO: validate that the passed expression is valid */ + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !expression || + strlen(expression) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + expression_copy = strdup(expression); + if (!expression_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (tracepoint->filter_expression) { + free(tracepoint->filter_expression); + } + + tracepoint->filter_expression = expression_copy; + expression_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter( + const struct lttng_event_rule *rule, const char **expression) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !expression) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + if (!tracepoint->filter_expression) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *expression = tracepoint->filter_expression; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel( + struct lttng_event_rule *rule, int level) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + /* + * TODO/QUESTION: do we validate the passed level based on the domain? + * What if no domain is set yet? Should we move the domain to the + * "create" api call to enforce the domain type? + */ + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + tracepoint->loglevel.value = level; + tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_SINGLE; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel_range( + struct lttng_event_rule *rule, int level) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + /* + * TODO/QUESTION: do we validate the passed level based on the domain? + * What if no domain is set yet? Should we move the domain to the + * "create" api call to enforce the domain type? + */ + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + tracepoint->loglevel.value = level; + tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_RANGE; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel_all( + struct lttng_event_rule *rule) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_loglevel_type( + const struct lttng_event_rule *rule, enum lttng_loglevel_type *type) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !type) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + *type = tracepoint->loglevel.type; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_loglevel( + const struct lttng_event_rule *rule, int *level) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !level) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + if (tracepoint->loglevel.type == LTTNG_EVENT_LOGLEVEL_ALL) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + *level = tracepoint->loglevel.value; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_exclusions( + struct lttng_event_rule *rule, + unsigned int count, + const char **exclusions) +{ + char **exclusions_copy = NULL; + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + enum lttng_domain_type domain_type; + + /* TODO: validate that the passed exclusions are valid? */ + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || count == 0 || + !exclusions) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of( + rule, struct lttng_event_rule_tracepoint, parent); + + status = lttng_event_rule_tracepoint_get_domain_type(rule, &domain_type); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + goto end; + } + + switch (domain_type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + status = LTTNG_EVENT_RULE_STATUS_UNSUPPORTED; + goto end; + case LTTNG_DOMAIN_UST: + /* Exclusions supported */ + break; + default: + assert(0); + } + + exclusions_copy = zmalloc(sizeof(char *) * count); + if (!exclusions_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + /* Perform the copy locally */ + for (int i = 0; i < count; i++) { + exclusions_copy[i] = strdup(exclusions[i]); + if (!exclusions_copy[i]) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + } + + if (tracepoint->exclusions.count != 0) { + for (int i = 0; i < tracepoint->exclusions.count; i++) { + free(tracepoint->exclusions.values[i]); + } + free(tracepoint->exclusions.values); + } + + tracepoint->exclusions.values = exclusions_copy; + tracepoint->exclusions.count = count; + exclusions_copy = NULL; +end: + if (exclusions_copy) { + for (int i = 0; i < count; i++) { + free(exclusions_copy[i]); + } + free(exclusions_copy); + } + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_exclusions_count( + const struct lttng_event_rule *rule, unsigned int *count) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !count) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + *count = tracepoint->exclusions.count; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_exclusion_at_index( + const struct lttng_event_rule *rule, + unsigned int index, + const char **exclusion) +{ + struct lttng_event_rule_tracepoint *tracepoint; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !exclusion) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + tracepoint = container_of(rule, struct lttng_event_rule_tracepoint, + parent); + if (index >= tracepoint->exclusions.count) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + *exclusion = tracepoint->exclusions.values[index]; +end: + return status; +} diff --git a/src/common/event-rule-uprobe.c b/src/common/event-rule-uprobe.c new file mode 100644 index 000000000..5f9a5e348 --- /dev/null +++ b/src/common/event-rule-uprobe.c @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define IS_UPROBE_EVENT_RULE(rule) ( \ + lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE \ + ) + +static +void lttng_event_rule_uprobe_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_uprobe *uprobe; + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, + parent); + + lttng_userspace_probe_location_destroy(uprobe->location); + free(uprobe->name); + free(uprobe); +} + +static +bool lttng_event_rule_uprobe_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_uprobe *uprobe; + + if (!rule) { + goto end; + } + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, + parent); + + /* Required field */ + if (!uprobe->name) { + ERR("Invalid uprobe event rule: a pattern must be set."); + goto end; + } + + if (!uprobe->location) { + ERR("Invalid uprobe event rule: a location must be set."); + goto end; + } + + /* TODO should we validate the probe location? */ + + valid = true; +end: + return valid; +} + +static +int lttng_event_rule_uprobe_serialize( + const struct lttng_event_rule *rule, + struct lttng_dynamic_buffer *buf, + int *fd_to_send) +{ + int ret; + size_t name_len, header_offset, size_before_probe; + struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_uprobe_comm uprobe_comm = { 0 }; + struct lttng_event_rule_uprobe_comm *header; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + header_offset = buf->size; + + DBG("Serializing uprobe event rule"); + uprobe = container_of( + rule, struct lttng_event_rule_uprobe, parent); + + name_len = strlen(uprobe->name) + 1; + + uprobe_comm.name_len = name_len; + + ret = lttng_dynamic_buffer_append( + buf, &uprobe_comm, sizeof(uprobe_comm)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(buf, uprobe->name, + name_len); + if (ret) { + goto end; + } + + size_before_probe = buf->size; + + /* This serialize return the size taken in the buffer */ + /* TODO: should all serialize standardise on this? */ + ret = lttng_userspace_probe_location_serialize( + uprobe->location, buf, fd_to_send); + if (ret < 0) { + goto end; + } + + /* Update the header regarding the probe size */ + header = (struct lttng_event_rule_uprobe_comm *) ((char *) buf->data + header_offset); + header->location_len = buf->size - size_before_probe; + + ret = 0; + +end: + return ret; +} + +static +bool lttng_event_rule_uprobe_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + bool is_equal = false; + struct lttng_event_rule_uprobe *a, *b; + + a = container_of(_a, struct lttng_event_rule_uprobe, parent); + b = container_of(_b, struct lttng_event_rule_uprobe, parent); + + /* uprobe is invalid if this is not true */ + assert(a->name); + assert(b->name); + if (strcmp(a->name, b->name)) { + goto end; + } + + assert(a->location); + assert(b->location); + is_equal = lttng_userspace_probe_location_is_equal(a->location, b->location); +end: + return is_equal; +} + +static +enum lttng_error_code lttng_event_rule_uprobe_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + /* Nothing to do */ + return LTTNG_OK; +} + +static const char *lttng_event_rule_uprobe_get_filter( + const struct lttng_event_rule *rule) +{ + /* Unsupported */ + return NULL; +} + +static const struct lttng_filter_bytecode * +lttng_event_rule_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule) +{ + /* Unsupported */ + return NULL; +} + +static +struct lttng_event_exclusion *lttng_event_rule_uprobe_generate_exclusions(struct lttng_event_rule *rule) +{ + /* Unsupported */ + return NULL; +} + +struct lttng_event_rule *lttng_event_rule_uprobe_create() +{ + struct lttng_event_rule_uprobe *rule; + + rule = zmalloc(sizeof(struct lttng_event_rule_uprobe)); + if (!rule) { + return NULL; + } + + lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_UPROBE); + rule->parent.validate = lttng_event_rule_uprobe_validate; + rule->parent.serialize = lttng_event_rule_uprobe_serialize; + rule->parent.equal = lttng_event_rule_uprobe_is_equal; + rule->parent.destroy = lttng_event_rule_uprobe_destroy; + rule->parent.populate = lttng_event_rule_uprobe_populate; + rule->parent.get_filter = lttng_event_rule_uprobe_get_filter; + rule->parent.get_filter_bytecode = lttng_event_rule_uprobe_get_filter_bytecode; + rule->parent.generate_exclusions = lttng_event_rule_uprobe_generate_exclusions; + return &rule->parent; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_uprobe_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_event_rule **_event_rule) +{ + ssize_t ret, offset = 0; + const struct lttng_event_rule_uprobe_comm *uprobe_comm; + const char *name; + struct lttng_buffer_view current_view; + struct lttng_event_rule *rule = NULL; + struct lttng_userspace_probe_location *location; + struct lttng_event_rule_uprobe *uprobe; + + if (!_event_rule) { + ret = -1; + goto end; + } + + if (view->size < sizeof(*uprobe_comm)) { + ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain header"); + ret = -1; + goto end; + } + + current_view = lttng_buffer_view_from_view(view, offset, sizeof(*uprobe_comm)); + uprobe_comm = (typeof(uprobe_comm)) current_view.data; + + if(!uprobe_comm) { + ret = -1; + goto end; + } + + rule = lttng_event_rule_uprobe_create(); + if (!rule) { + ERR("Failed to create event rule uprobe"); + ret = -1; + goto end; + } + + /* Skip to payload */ + offset += current_view.size; + + /* Map the name */ + current_view = lttng_buffer_view_from_view(view, offset, uprobe_comm->name_len); + name = current_view.data; + if (!name) { + ret = -1; + goto end; + } + + if (uprobe_comm->name_len == 1 || + name[uprobe_comm->name_len - 1] != '\0' || + strlen(name) != uprobe_comm->name_len - 1) { + /* + * Check that the name is not NULL, is NULL-terminated, and + * does not contain a NULL before the last byte. + */ + ret = -1; + goto end; + } + + /* Skip after the name */ + offset += uprobe_comm->name_len; + + /* Map the location */ + current_view = lttng_buffer_view_from_view(view, offset, uprobe_comm->location_len); + ret = lttng_userspace_probe_location_create_from_buffer(¤t_view, &location); + if (ret < 0) { + ret = -1; + goto end; + } + + assert(ret == uprobe_comm->location_len); + + /* Skip after the location */ + offset += uprobe_comm->location_len; + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe->location = location; + + (void) lttng_event_rule_uprobe_set_name(rule, name); + + if (!lttng_event_rule_uprobe_validate(rule)) { + ret = -1; + goto end; + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_uprobe_set_location( + struct lttng_event_rule *rule, + const struct lttng_userspace_probe_location *location) +{ + struct lttng_userspace_probe_location *location_copy = NULL; + struct lttng_event_rule_uprobe *uprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, + parent); + location_copy = lttng_userspace_probe_location_copy(location); + if (!location_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (uprobe->location) { + lttng_userspace_probe_location_destroy(uprobe->location); + } + + uprobe->location = location_copy; + location_copy = NULL; +end: + lttng_userspace_probe_location_destroy(location_copy); + return status; +} + +enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( + const struct lttng_event_rule *rule, + const struct lttng_userspace_probe_location **location) +{ + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + *location = lttng_event_rule_uprobe_get_location_no_const(rule); + if (!*location) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + +end: + return status; + +} + +LTTNG_HIDDEN +struct lttng_userspace_probe_location * +lttng_event_rule_uprobe_get_location_no_const( + const struct lttng_event_rule *rule) +{ + struct lttng_event_rule_uprobe *uprobe; + assert(rule); + uprobe = container_of(rule, struct lttng_event_rule_uprobe, + parent); + + return uprobe->location; +} + +enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( + struct lttng_event_rule *rule, const char *name) +{ + char *name_copy = NULL; + struct lttng_event_rule_uprobe *uprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name || + strlen(name) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, + parent); + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + if (uprobe->name) { + free(uprobe->name); + } + + uprobe->name = name_copy; + name_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_uprobe_get_name( + const struct lttng_event_rule *rule, const char **name) +{ + struct lttng_event_rule_uprobe *uprobe; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + uprobe = container_of(rule, struct lttng_event_rule_uprobe, + parent); + if (!uprobe->name) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *name = uprobe->name; +end: + return status; +} diff --git a/src/common/event-rule.c b/src/common/event-rule.c new file mode 100644 index 000000000..57bd68b16 --- /dev/null +++ b/src/common/event-rule.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2019 - Jonathan Rajotte-Julien + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum lttng_event_rule_type lttng_event_rule_get_type( + const struct lttng_event_rule *event_rule) +{ + return event_rule ? event_rule->type : LTTNG_EVENT_RULE_TYPE_UNKNOWN; +} + +LTTNG_HIDDEN +enum lttng_domain_type lttng_event_rule_get_domain_type( + const struct lttng_event_rule *event_rule) +{ + enum lttng_domain_type domain_type; + + switch (lttng_event_rule_get_type(event_rule)) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + (void) lttng_event_rule_tracepoint_get_domain_type(event_rule, &domain_type); + break; + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + case LTTNG_EVENT_RULE_TYPE_UPROBE: + domain_type = LTTNG_DOMAIN_KERNEL; + break; + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + domain_type = LTTNG_DOMAIN_NONE; + break; + } + + return domain_type; +} + +static +void lttng_event_rule_release(struct urcu_ref *ref) +{ + + struct lttng_event_rule *event_rule = container_of(ref, typeof(*event_rule), + ref); + + assert(event_rule->destroy); + event_rule->destroy(event_rule); +} + +void lttng_event_rule_destroy(struct lttng_event_rule *event_rule) +{ + lttng_event_rule_put(event_rule); +} + +LTTNG_HIDDEN +bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule) +{ + bool valid; + + if (!event_rule) { + valid = false; + goto end; + } + + if (!event_rule->validate) { + /* Sub-class guarantees that it can never be invalid. */ + valid = true; + goto end; + } + + valid = event_rule->validate(event_rule); +end: + return valid; +} + +LTTNG_HIDDEN +int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule, + struct lttng_dynamic_buffer *buf, + int *fd_to_send) +{ + int ret; + struct lttng_event_rule_comm event_rule_comm = { 0 }; + + if (!event_rule) { + ret = -1; + goto end; + } + + event_rule_comm.event_rule_type = (int8_t) event_rule->type; + + ret = lttng_dynamic_buffer_append(buf, &event_rule_comm, + sizeof(event_rule_comm)); + if (ret) { + goto end; + } + + ret = event_rule->serialize(event_rule, buf, fd_to_send); + if (ret) { + goto end; + } +end: + return ret; +} + +LTTNG_HIDDEN +bool lttng_event_rule_is_equal(const struct lttng_event_rule *a, + const struct lttng_event_rule *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_create_from_buffer( + const struct lttng_buffer_view *buffer, + struct lttng_event_rule **event_rule) +{ + ssize_t ret, event_rule_size = 0; + const struct lttng_event_rule_comm *event_rule_comm; + event_rule_create_from_buffer_cb create_from_buffer = NULL; + + if (!buffer || !event_rule) { + ret = -1; + goto end; + } + + DBG("Deserializing event_rule from buffer"); + event_rule_comm = (const struct lttng_event_rule_comm *) buffer->data; + event_rule_size += sizeof(*event_rule_comm); + + switch ((enum lttng_event_rule_type) event_rule_comm->event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + create_from_buffer = lttng_event_rule_tracepoint_create_from_buffer; + break; + case LTTNG_EVENT_RULE_TYPE_KPROBE: + create_from_buffer = lttng_event_rule_kprobe_create_from_buffer; + break; + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + create_from_buffer = lttng_event_rule_kretprobe_create_from_buffer; + break; + case LTTNG_EVENT_RULE_TYPE_UPROBE: + create_from_buffer = lttng_event_rule_uprobe_create_from_buffer; + break; + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + create_from_buffer = lttng_event_rule_syscall_create_from_buffer; + break; + default: + ERR("Attempted to create event rule of unknown type (%i)", + (int) event_rule_comm->event_rule_type); + ret = -1; + goto end; + } + + if (create_from_buffer) { + const struct lttng_buffer_view view = + lttng_buffer_view_from_view(buffer, + sizeof(*event_rule_comm), -1); + + ret = create_from_buffer(&view, event_rule); + if (ret < 0) { + goto end; + } + event_rule_size += ret; + + } else { + abort(); + } + + ret = event_rule_size; +end: + return ret; +} + +LTTNG_HIDDEN +void lttng_event_rule_init(struct lttng_event_rule *event_rule, + enum lttng_event_rule_type type) +{ + urcu_ref_init(&event_rule->ref); + event_rule->type = type; +} + +LTTNG_HIDDEN +bool lttng_event_rule_get(struct lttng_event_rule *event_rule) +{ + return urcu_ref_get_unless_zero(&event_rule->ref); +} + +LTTNG_HIDDEN +void lttng_event_rule_put(struct lttng_event_rule *event_rule) +{ + if (!event_rule) { + return; + } + assert(event_rule->ref.refcount); + urcu_ref_put(&event_rule->ref, lttng_event_rule_release); +} + +LTTNG_HIDDEN +enum lttng_error_code lttng_event_rule_populate(struct lttng_event_rule *rule, uid_t uid, gid_t gid) +{ + assert(rule->populate); + return rule->populate(rule, uid, gid); +} + +/* If not present return NULL + * Caller DO NOT own the returned object + */ +LTTNG_HIDDEN +const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule) +{ + assert(rule->get_filter); + return rule->get_filter(rule); +} + +/* If not present return NULL + * Caller DO NOT own the returned object + */ +LTTNG_HIDDEN +const struct lttng_filter_bytecode *lttng_event_rule_get_filter_bytecode( + const struct lttng_event_rule *rule) +{ + assert(rule->get_filter_bytecode); + return rule->get_filter_bytecode(rule); +} + +/* + * If not present return NULL + * Caller OWN the returned object + * TODO: should this be done another way? + */ +LTTNG_HIDDEN +struct lttng_event_exclusion *lttng_event_rule_generate_exclusions( + struct lttng_event_rule *rule) +{ + assert(rule->generate_exclusions); + return rule->generate_exclusions(rule); +} + +LTTNG_HIDDEN +struct lttng_event *lttng_event_rule_generate_lttng_event( + const struct lttng_event_rule *rule) +{ + assert(rule->generate_lttng_event); + return rule->generate_lttng_event(rule); +} + +LTTNG_HIDDEN +bool lttng_event_rule_is_agent(const struct lttng_event_rule *rule) +{ + bool ret = false; + enum lttng_domain_type type = lttng_event_rule_get_domain_type(rule); + + switch (type) { + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + ret = true; + break; + case LTTNG_DOMAIN_UST: + case LTTNG_DOMAIN_KERNEL: + ret = false; + break; + default: + assert(0); + }; + + return ret; +} + +LTTNG_HIDDEN +const char *lttng_event_rule_type_str(enum lttng_event_rule_type type) +{ + switch (type) { + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + return "unknown"; + + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + return "tracepoint"; + + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + return "syscall"; + + case LTTNG_EVENT_RULE_TYPE_KPROBE: + return "probe"; + + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + return "function"; + + case LTTNG_EVENT_RULE_TYPE_UPROBE: + return "userspace-probe"; + + default: + abort(); + } +} diff --git a/src/lib/lttng-ctl/filter/Makefile.am b/src/common/filter/Makefile.am similarity index 100% rename from src/lib/lttng-ctl/filter/Makefile.am rename to src/common/filter/Makefile.am diff --git a/src/lib/lttng-ctl/filter/filter-ast.h b/src/common/filter/filter-ast.h similarity index 95% rename from src/lib/lttng-ctl/filter/filter-ast.h rename to src/common/filter/filter-ast.h index fd322df3f..29fde10f8 100644 --- a/src/lib/lttng-ctl/filter/filter-ast.h +++ b/src/common/filter/filter-ast.h @@ -115,10 +115,10 @@ struct filter_node { enum ast_link_type post_op; /* reverse */ enum ast_link_type pre_op; /* forward */ union { - char *string; + const char *string; uint64_t constant; double float_constant; - char *identifier; + const char *identifier; /* * child can be nested. */ @@ -164,6 +164,8 @@ struct filter_parser_ctx { struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input); void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx); int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx); +int filter_parser_ctx_create_from_filter_expression( + const char *filter_expression, struct filter_parser_ctx **ctxp); static inline struct filter_ast *filter_parser_get_ast(struct filter_parser_ctx *parser_ctx) diff --git a/src/lib/lttng-ctl/filter/filter-bytecode.h b/src/common/filter/filter-bytecode.h similarity index 100% rename from src/lib/lttng-ctl/filter/filter-bytecode.h rename to src/common/filter/filter-bytecode.h diff --git a/src/lib/lttng-ctl/filter/filter-grammar-test.c b/src/common/filter/filter-grammar-test.c similarity index 100% rename from src/lib/lttng-ctl/filter/filter-grammar-test.c rename to src/common/filter/filter-grammar-test.c diff --git a/src/lib/lttng-ctl/filter/filter-ir.h b/src/common/filter/filter-ir.h similarity index 100% rename from src/lib/lttng-ctl/filter/filter-ir.h rename to src/common/filter/filter-ir.h diff --git a/src/lib/lttng-ctl/filter/filter-lexer.l b/src/common/filter/filter-lexer.l similarity index 100% rename from src/lib/lttng-ctl/filter/filter-lexer.l rename to src/common/filter/filter-lexer.l diff --git a/src/lib/lttng-ctl/filter/filter-parser.y b/src/common/filter/filter-parser.y similarity index 82% rename from src/lib/lttng-ctl/filter/filter-parser.y rename to src/common/filter/filter-parser.y index d1ea3a1a9..dfcdee362 100644 --- a/src/lib/lttng-ctl/filter/filter-parser.y +++ b/src/common/filter/filter-parser.y @@ -20,6 +20,8 @@ #include #include "filter-ast.h" #include "filter-parser.h" +#include "filter-bytecode.h" +#include "memstream.h" #include @@ -28,6 +30,20 @@ #define WIDTH_x64_SCANF_IS_A_BROKEN_API "17" #define WIDTH_lg_SCANF_IS_A_BROKEN_API "4096" /* Hugely optimistic approximation */ +#ifdef DEBUG +static const int print_xml = 1; +#define dbg_printf(fmt, args...) \ + printf("[debug filter_parser] " fmt, ## args) +#else +static const int print_xml = 0; +#define dbg_printf(fmt, args...) \ +do { \ + /* do nothing but check printf format */ \ + if (0) \ + printf("[debug filter_parser] " fmt, ## args); \ +} while (0) +#endif + LTTNG_HIDDEN int yydebug; LTTNG_HIDDEN @@ -93,7 +109,7 @@ end: * gsrc will be garbage collected immediately, and gstr might be. * Should only be used to append characters to a string literal or constant. */ -LTTNG_HIDDEN +static struct gc_string *gc_string_append(struct filter_parser_ctx *parser_ctx, struct gc_string *gstr, struct gc_string *gsrc) @@ -123,6 +139,8 @@ struct gc_string *gc_string_append(struct filter_parser_ctx *parser_ctx, return gstr; } +LTTNG_HIDDEN +void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src); LTTNG_HIDDEN void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src) { @@ -185,12 +203,16 @@ static struct filter_node *make_op_node(struct filter_parser_ctx *scanner, return node; } +LTTNG_HIDDEN +void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str); LTTNG_HIDDEN void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str) { fprintf(stderr, "error %s\n", str); } +LTTNG_HIDDEN +int yywrap(void); LTTNG_HIDDEN int yywrap(void) { @@ -294,6 +316,123 @@ void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx) free(parser_ctx); } +LTTNG_HIDDEN +int filter_parser_ctx_create_from_filter_expression( + const char *filter_expression, struct filter_parser_ctx **ctxp) +{ + int ret; + struct filter_parser_ctx *ctx = NULL; + FILE *fmem = NULL; + + assert(filter_expression); + assert(ctxp); + + /* + * Casting const to non-const, as the underlying function will use it in + * read-only mode. + */ + fmem = lttng_fmemopen((void *) filter_expression, + strlen(filter_expression), "r"); + if (!fmem) { + fprintf(stderr, "Error opening memory as stream\n"); + ret = -LTTNG_ERR_FILTER_NOMEM; + goto error; + } + ctx = filter_parser_ctx_alloc(fmem); + if (!ctx) { + fprintf(stderr, "Error allocating parser\n"); + ret = -LTTNG_ERR_FILTER_NOMEM; + goto filter_alloc_error; + } + ret = filter_parser_ctx_append_ast(ctx); + if (ret) { + fprintf(stderr, "Parse error\n"); + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + if (print_xml) { + ret = filter_visitor_print_xml(ctx, stdout, 0); + if (ret) { + fflush(stdout); + fprintf(stderr, "XML print error\n"); + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + } + + dbg_printf("Generating IR... "); + fflush(stdout); + ret = filter_visitor_ir_generate(ctx); + if (ret) { + fprintf(stderr, "Generate IR error\n"); + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + dbg_printf("done\n"); + + dbg_printf("Validating IR... "); + fflush(stdout); + ret = filter_visitor_ir_check_binary_op_nesting(ctx); + if (ret) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + + /* Normalize globbing patterns in the expression. */ + ret = filter_visitor_ir_normalize_glob_patterns(ctx); + if (ret) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + + /* Validate strings used as literals in the expression. */ + ret = filter_visitor_ir_validate_string(ctx); + if (ret) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + + /* Validate globbing patterns in the expression. */ + ret = filter_visitor_ir_validate_globbing(ctx); + if (ret) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + + dbg_printf("done\n"); + + dbg_printf("Generating bytecode... "); + fflush(stdout); + ret = filter_visitor_bytecode_generate(ctx); + if (ret) { + fprintf(stderr, "Generate bytecode error\n"); + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + dbg_printf("done\n"); + dbg_printf("Size of bytecode generated: %u bytes.\n", + bytecode_get_len(&ctx->bytecode->b)); + + /* No need to keep the memory stream. */ + if (fclose(fmem) != 0) { + fprintf(stderr, "fclose (%d) \n", errno); + ret = -LTTNG_ERR_FILTER_INVAL; + } + + *ctxp = ctx; + return 0; + +parse_error: + filter_ir_free(ctx); + filter_parser_ctx_free(ctx); +filter_alloc_error: + if (fclose(fmem) != 0) { + fprintf(stderr, "fclose (%d) \n", errno); + } +error: + return ret; +} + %} %define api.pure diff --git a/src/lib/lttng-ctl/filter/filter-symbols.h b/src/common/filter/filter-symbols.h similarity index 100% rename from src/lib/lttng-ctl/filter/filter-symbols.h rename to src/common/filter/filter-symbols.h diff --git a/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c b/src/common/filter/filter-visitor-generate-bytecode.c similarity index 100% rename from src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c rename to src/common/filter/filter-visitor-generate-bytecode.c diff --git a/src/lib/lttng-ctl/filter/filter-visitor-generate-ir.c b/src/common/filter/filter-visitor-generate-ir.c similarity index 99% rename from src/lib/lttng-ctl/filter/filter-visitor-generate-ir.c rename to src/common/filter/filter-visitor-generate-ir.c index 024aac639..11c6b610c 100644 --- a/src/lib/lttng-ctl/filter/filter-visitor-generate-ir.c +++ b/src/common/filter/filter-visitor-generate-ir.c @@ -77,7 +77,7 @@ enum ir_load_string_type get_literal_string_type(const char *string) } static -struct ir_op *make_op_load_string(char *string, enum ir_side side) +struct ir_op *make_op_load_string(const char *string, enum ir_side side) { struct ir_op *op; @@ -187,7 +187,7 @@ struct ir_load_expression *create_load_expression(struct filter_node *node) { struct ir_load_expression *load_exp; struct ir_load_expression_op *load_exp_op, *prev_op; - char *str; + const char *str; /* Get forward chain. */ node = load_expression_get_forward_chain(node); diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-comparator.c b/src/common/filter/filter-visitor-ir-check-binary-comparator.c similarity index 100% rename from src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-comparator.c rename to src/common/filter/filter-visitor-ir-check-binary-comparator.c diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-op-nesting.c b/src/common/filter/filter-visitor-ir-check-binary-op-nesting.c similarity index 100% rename from src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-op-nesting.c rename to src/common/filter/filter-visitor-ir-check-binary-op-nesting.c diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-normalize-glob-patterns.c b/src/common/filter/filter-visitor-ir-normalize-glob-patterns.c similarity index 100% rename from src/lib/lttng-ctl/filter/filter-visitor-ir-normalize-glob-patterns.c rename to src/common/filter/filter-visitor-ir-normalize-glob-patterns.c diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-globbing.c b/src/common/filter/filter-visitor-ir-validate-globbing.c similarity index 100% rename from src/lib/lttng-ctl/filter/filter-visitor-ir-validate-globbing.c rename to src/common/filter/filter-visitor-ir-validate-globbing.c diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-string.c b/src/common/filter/filter-visitor-ir-validate-string.c similarity index 100% rename from src/lib/lttng-ctl/filter/filter-visitor-ir-validate-string.c rename to src/common/filter/filter-visitor-ir-validate-string.c diff --git a/src/lib/lttng-ctl/filter/filter-visitor-xml.c b/src/common/filter/filter-visitor-xml.c similarity index 100% rename from src/lib/lttng-ctl/filter/filter-visitor-xml.c rename to src/common/filter/filter-visitor-xml.c diff --git a/src/lib/lttng-ctl/filter/memstream.h b/src/common/filter/memstream.h similarity index 100% rename from src/lib/lttng-ctl/filter/memstream.h rename to src/common/filter/memstream.h diff --git a/src/common/kernel-ctl/kernel-ctl.c b/src/common/kernel-ctl/kernel-ctl.c index 01af4a297..878929ff0 100644 --- a/src/common/kernel-ctl/kernel-ctl.c +++ b/src/common/kernel-ctl/kernel-ctl.c @@ -417,7 +417,22 @@ int kernctl_stop_session(int fd) LTTNG_KERNEL_SESSION_STOP); } -int kernctl_filter(int fd, struct lttng_filter_bytecode *filter) +int kernctl_create_trigger_group(int fd) +{ + return LTTNG_IOCTL_NO_CHECK(fd, LTTNG_KERNEL_TRIGGER_GROUP_CREATE); +} + +int kernctl_create_trigger_group_notification_fd(int group_fd) +{ + return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_TRIGGER_GROUP_NOTIFICATION_FD); +} + +int kernctl_create_trigger(int group_fd, struct lttng_kernel_trigger *trigger) +{ + return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_TRIGGER_CREATE, trigger); +} + +int kernctl_filter(int fd, const struct lttng_filter_bytecode *filter) { struct lttng_kernel_filter_bytecode *kb; uint32_t len; diff --git a/src/common/kernel-ctl/kernel-ctl.h b/src/common/kernel-ctl/kernel-ctl.h index f8eac8d56..b3d798ffd 100644 --- a/src/common/kernel-ctl/kernel-ctl.h +++ b/src/common/kernel-ctl/kernel-ctl.h @@ -26,8 +26,14 @@ int kernctl_disable(int fd); int kernctl_start_session(int fd); int kernctl_stop_session(int fd); +int kernctl_create_trigger_group(int fd); + +/* Apply on trigger_group FD*/ +int kernctl_create_trigger_group_notification_fd(int fd); +int kernctl_create_trigger(int fd, struct lttng_kernel_trigger *trigger); + /* Apply on event FD */ -int kernctl_filter(int fd, struct lttng_filter_bytecode *filter); +int kernctl_filter(int fd, const struct lttng_filter_bytecode *filter); int kernctl_add_callsite(int fd, struct lttng_kernel_event_callsite *callsite); int kernctl_tracepoint_list(int fd); diff --git a/src/common/kernel-ctl/kernel-ioctl.h b/src/common/kernel-ctl/kernel-ioctl.h index 59d71191f..65ed69ecd 100644 --- a/src/common/kernel-ctl/kernel-ioctl.h +++ b/src/common/kernel-ctl/kernel-ioctl.h @@ -117,6 +117,13 @@ #define LTTNG_KERNEL_TRACER_ABI_VERSION \ _IOR(0xF6, 0x4B, struct lttng_kernel_tracer_abi_version) +#define LTTNG_KERNEL_TRIGGER_GROUP_CREATE _IO(0xF6, 0x4C) + +/* Trigger group file descriptor ioctl */ +#define LTTNG_KERNEL_TRIGGER_GROUP_NOTIFICATION_FD _IO(0xF6, 0x30) +#define LTTNG_KERNEL_TRIGGER_CREATE \ + _IOW(0xF6, 0x31, struct lttng_kernel_trigger) + /* Session FD ioctl */ #define LTTNG_KERNEL_METADATA \ _IOW(0xF6, 0x54, struct lttng_kernel_channel) diff --git a/src/common/lttng-elf.c b/src/common/lttng-elf.c index 816dd0d76..bef20ffd4 100644 --- a/src/common/lttng-elf.c +++ b/src/common/lttng-elf.c @@ -750,7 +750,7 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset) char *curr_sym_str = NULL; char *symbol_table_data = NULL; char *string_table_data = NULL; - char *string_table_name = NULL; + const char *string_table_name = NULL; struct lttng_elf_shdr symtab_hdr; struct lttng_elf_shdr strtab_hdr; struct lttng_elf *elf = NULL; diff --git a/src/common/lttng-kernel.h b/src/common/lttng-kernel.h index d5904e79f..62e784eb6 100644 --- a/src/common/lttng-kernel.h +++ b/src/common/lttng-kernel.h @@ -150,6 +150,24 @@ struct lttng_kernel_event { } u; } LTTNG_PACKED; +#define LTTNG_KERNEL_TRIGGER_PADDING1 16 +#define LTTNG_KERNEL_TRIGGER_PADDING2 LTTNG_KERNEL_SYM_NAME_LEN + 32 +struct lttng_kernel_trigger { + uint64_t id; + char name[LTTNG_KERNEL_SYM_NAME_LEN]; /* event name */ + enum lttng_kernel_instrumentation instrumentation; + char padding[LTTNG_KERNEL_TRIGGER_PADDING1]; + + /* Per instrumentation type configuration */ + union { + struct lttng_kernel_kretprobe kretprobe; + struct lttng_kernel_kprobe kprobe; + struct lttng_kernel_uprobe uprobe; + struct lttng_kernel_function ftrace; + char padding[LTTNG_KERNEL_TRIGGER_PADDING2]; + } u; +} LTTNG_PACKED; + struct lttng_kernel_tracer_version { uint32_t major; uint32_t minor; diff --git a/src/common/notification.c b/src/common/notification.c index 6b32bd9ac..87f1a8a41 100644 --- a/src/common/notification.c +++ b/src/common/notification.c @@ -51,8 +51,7 @@ int lttng_notification_serialize(const struct lttng_notification *notification, } size_before_payload = buf->size; - ret = lttng_condition_serialize(notification->condition, - buf); + ret = lttng_condition_serialize(notification->condition, buf, NULL); if (ret) { goto end; } diff --git a/src/common/relayd/relayd.c b/src/common/relayd/relayd.c index 7b11c81f6..dfcb337a0 100644 --- a/src/common/relayd/relayd.c +++ b/src/common/relayd/relayd.c @@ -499,7 +499,7 @@ int relayd_add_stream(struct lttcomm_relayd_sock *rsock, const char *channel_nam int ret; struct lttcomm_relayd_status_stream reply; char pathname[RELAYD_COMM_LTTNG_PATH_MAX]; - char *separator; + const char *separator; /* Code flow error. Safety net. */ assert(rsock); diff --git a/src/common/runas.c b/src/common/runas.c index 5d63f640e..da2ae71a2 100644 --- a/src/common/runas.c +++ b/src/common/runas.c @@ -28,12 +28,17 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include + #include "runas.h" struct run_as_data; @@ -57,6 +62,7 @@ enum run_as_cmd { RUN_AS_RENAMEAT, RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, + RUN_AS_GENERATE_FILTER_BYTECODE, }; struct run_as_mkdir_data { @@ -94,6 +100,10 @@ struct run_as_extract_sdt_probe_offsets_data { char provider_name[LTTNG_SYMBOL_NAME_LEN]; } LTTNG_PACKED; +struct run_as_generate_filter_bytecode_data { + char filter_expression[LTTNG_FILTER_MAX_LEN]; +} LTTNG_PATCKED; + struct run_as_rename_data { /* * [0] = old_dirfd @@ -117,6 +127,11 @@ struct run_as_extract_sdt_probe_offsets_ret { uint64_t offsets[LTTNG_KERNEL_MAX_UPROBE_NUM]; } LTTNG_PACKED; +struct run_as_generate_filter_bytecode_ret { + /* A lttng_bytecode_filter strcut with "dynamic" payload */ + char bytecode[LTTNG_FILTER_MAX_LEN]; +} LTTNG_PACKED; + struct run_as_data { enum run_as_cmd cmd; union { @@ -127,6 +142,7 @@ struct run_as_data { struct run_as_rename_data rename; struct run_as_extract_elf_symbol_offset_data extract_elf_symbol_offset; struct run_as_extract_sdt_probe_offsets_data extract_sdt_probe_offsets; + struct run_as_generate_filter_bytecode_data generate_filter_bytecode; } u; uid_t uid; gid_t gid; @@ -153,6 +169,7 @@ struct run_as_ret { struct run_as_open_ret open; struct run_as_extract_elf_symbol_offset_ret extract_elf_symbol_offset; struct run_as_extract_sdt_probe_offsets_ret extract_sdt_probe_offsets; + struct run_as_generate_filter_bytecode_ret generate_filter_bytecode; } u; int _errno; bool _error; @@ -306,6 +323,13 @@ static const struct run_as_command_properties command_properties[] = { .out_fd_count = 0, .use_cwd_fd = false, }, + [RUN_AS_GENERATE_FILTER_BYTECODE] = { + .in_fds_offset = -1, + .in_fd_count = 0, + .out_fds_offset = -1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, }; struct run_as_worker { @@ -619,6 +643,47 @@ int _extract_sdt_probe_offsets(struct run_as_data *data, } #endif +static +int _generate_filter_bytecode(struct run_as_data *data, + struct run_as_ret *ret_value) { + int ret = 0; + const char *filter_expression = NULL; + struct filter_parser_ctx *ctx = NULL; + + ret_value->_error = false; + + filter_expression = data->u.generate_filter_bytecode.filter_expression; + + if (lttng_strnlen(filter_expression, LTTNG_FILTER_MAX_LEN - 1) == LTTNG_FILTER_MAX_LEN - 1) { + ret_value->_error = true; + ret = -1; + goto end; + } + + ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx); + if (ret < 0) { + ret_value->_error = true; + ret = -1; + goto end; + } + + DBG("Size of bytecode generated: %u bytes.", + bytecode_get_len(&ctx->bytecode->b)); + + /* Copy the lttng_bytecode_filter object to the return structure */ + memcpy(ret_value->u.generate_filter_bytecode.bytecode, + &ctx->bytecode->b, + sizeof(ctx->bytecode->b) + + bytecode_get_len(&ctx->bytecode->b)); + +end: + if (ctx) { + filter_bytecode_free(ctx); + filter_ir_free(ctx); + filter_parser_ctx_free(ctx); + } + return ret; +} static run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd) { @@ -648,6 +713,8 @@ run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd) return _extract_elf_symbol_offset; case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS: return _extract_sdt_probe_offsets; + case RUN_AS_GENERATE_FILTER_BYTECODE: + return _generate_filter_bytecode; default: ERR("Unknown command %d", (int) cmd); return NULL; @@ -660,6 +727,11 @@ int do_send_fds(int sock, const int *fds, unsigned int fd_count) ssize_t len; unsigned int i; + if (fd_count == 0) { + /* Nothing to send */ + return 0; + } + for (i = 0; i < fd_count; i++) { if (fds[i] < 0) { ERR("Attempt to send invalid file descriptor to master (fd = %i)", @@ -680,6 +752,11 @@ int do_recv_fds(int sock, int *fds, unsigned int fd_count) unsigned int i; ssize_t len; + if (fd_count == 0) { + /* Nothing to receive */ + goto end; + } + len = lttcomm_recv_fds_unix_sock(sock, fds, fd_count); if (len == 0) { ret = -1; @@ -1726,6 +1803,50 @@ error: return ret; } +LTTNG_HIDDEN +int run_as_generate_filter_bytecode(const char *filter_expression, + uid_t uid, + gid_t gid, + struct lttng_filter_bytecode **bytecode) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + const struct lttng_filter_bytecode *view_bytecode = NULL; + struct lttng_filter_bytecode *local_bytecode = NULL; + + DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d", + filter_expression, (int) uid, (int) gid); + + ret = lttng_strncpy(data.u.generate_filter_bytecode.filter_expression, filter_expression, + sizeof(data.u.generate_filter_bytecode.filter_expression)); + if (ret) { + goto error; + } + + run_as(RUN_AS_GENERATE_FILTER_BYTECODE, &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + if (run_as_ret._error) { + ret = -1; + goto error; + } + + view_bytecode = (const struct lttng_filter_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode; + + local_bytecode = zmalloc(sizeof(*local_bytecode) + view_bytecode->len); + if (!local_bytecode) { + ret = -ENOMEM; + goto error; + } + + memcpy(local_bytecode, run_as_ret.u.generate_filter_bytecode.bytecode, + sizeof(*local_bytecode) + view_bytecode->len); + *bytecode = local_bytecode; +error: + return ret; + +} + LTTNG_HIDDEN int run_as_create_worker(const char *procname, post_fork_cleanup_cb clean_up_func, diff --git a/src/common/runas.h b/src/common/runas.h index 619d3d0b3..398221708 100644 --- a/src/common/runas.h +++ b/src/common/runas.h @@ -14,6 +14,7 @@ #include #include +#include /* * The run-as process is launched by forking without an exec*() call. This means @@ -70,6 +71,11 @@ int run_as_extract_sdt_probe_offsets(int fd, const char *provider_name, const char* probe_name, uid_t uid, gid_t gid, uint64_t **offsets, uint32_t *num_offset); LTTNG_HIDDEN +int run_as_generate_filter_bytecode(const char *filter_expression, + uid_t uid, + gid_t gid, + struct lttng_filter_bytecode **bytecode); +LTTNG_HIDDEN int run_as_create_worker(const char *procname, post_fork_cleanup_cb clean_up_func, void *clean_up_user_data); LTTNG_HIDDEN diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index d89da4e32..735fa023c 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -99,6 +99,7 @@ enum lttcomm_sessiond_command { LTTNG_SESSION_LIST_ROTATION_SCHEDULES = 48, LTTNG_CREATE_SESSION_EXT = 49, LTTNG_CLEAR_SESSION = 50, + LTTNG_LIST_TRIGGERS = 51, }; enum lttcomm_relayd_command { diff --git a/src/common/snapshot.c b/src/common/snapshot.c new file mode 100644 index 000000000..2879f0f90 --- /dev/null +++ b/src/common/snapshot.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2020 - EfficiOS, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "buffer-view.h" +#include "dynamic-buffer.h" +#include "lttng/snapshot.h" +#include "lttng/snapshot-internal.h" +#include "snapshot.h" + +#include +#include + +LTTNG_HIDDEN +bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output) +{ + bool valid = false; + size_t len; + + /* + * It is mandatory to have a ctrl_url. If there is only one output + * URL (in the net://, net6:// or file:// form), it will be in this + * field. + */ + len = lttng_strnlen(output->ctrl_url, sizeof(output->ctrl_url)); + if (len == 0 || len >= sizeof(output->ctrl_url)) { + goto end; + } + + len = lttng_strnlen(output->data_url, sizeof(output->data_url)); + if (len >= sizeof(output->data_url)) { + goto end; + } + + len = lttng_strnlen(output->name, sizeof(output->name)); + if (len >= sizeof(output->name)) { + goto end; + } + + valid = true; + +end: + return valid; +} + +LTTNG_HIDDEN +bool lttng_snapshot_output_is_equal( + const struct lttng_snapshot_output *a, + const struct lttng_snapshot_output *b) +{ + bool equal = false; + + assert(a); + assert(b); + + if (a->max_size != b->max_size) { + goto end; + } + + if (strcmp(a->name, b->name) != 0) { + goto end; + } + + if (strcmp(a->ctrl_url, b->ctrl_url) != 0) { + goto end; + } + + if (strcmp(a->data_url, b->data_url) != 0) { + goto end; + } + + equal = true; + +end: + return equal; +} + +/* + * This is essentially the same as `struct lttng_snapshot_output`, but packed. + */ +struct LTTNG_PACKED lttng_snapshot_output_comm { + uint32_t id; + uint64_t max_size; + char name[LTTNG_NAME_MAX]; + char ctrl_url[PATH_MAX]; + char data_url[PATH_MAX]; +}; + +LTTNG_HIDDEN +int lttng_snapshot_output_serialize( + const struct lttng_snapshot_output *output, + struct lttng_dynamic_buffer *buf) +{ + struct lttng_snapshot_output_comm comm; + int ret; + + comm.id = output->id; + comm.max_size = output->max_size; + + ret = lttng_strncpy(comm.name, output->name, sizeof(comm.name)); + if (ret) { + goto end; + } + + ret = lttng_strncpy(comm.ctrl_url, output->ctrl_url, sizeof(comm.ctrl_url)); + if (ret) { + goto end; + } + + ret = lttng_strncpy(comm.data_url, output->data_url, sizeof(comm.data_url)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm)); + if (ret) { + goto end; + } + +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_snapshot_output_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_snapshot_output **output_p) +{ + const struct lttng_snapshot_output_comm *comm; + struct lttng_snapshot_output *output = NULL; + int ret; + + if (view->size != sizeof(*comm)) { + ret = -1; + goto end; + } + + output = lttng_snapshot_output_create(); + if (!output) { + ret = -1; + goto end; + } + + comm = (struct lttng_snapshot_output_comm *) view->data; + + output->id = comm->id; + output->max_size = comm->max_size; + + ret = lttng_strncpy(output->name, comm->name, sizeof(output->name)); + if (ret) { + goto end; + } + + ret = lttng_strncpy(output->ctrl_url, comm->ctrl_url, sizeof(output->ctrl_url)); + if (ret) { + goto end; + } + + ret = lttng_strncpy(output->data_url, comm->data_url, sizeof(output->data_url)); + if (ret) { + goto end; + } + + *output_p = output; + output = NULL; + ret = sizeof(*comm); + +end: + lttng_snapshot_output_destroy(output); + return ret; +} diff --git a/src/common/snapshot.h b/src/common/snapshot.h new file mode 100644 index 000000000..2f27d5c79 --- /dev/null +++ b/src/common/snapshot.h @@ -0,0 +1,47 @@ +#ifndef COMMON_SNAPSHOT_H +#define COMMON_SNAPSHOT_H + +/* + * Copyright (C) 2020 - EfficiOS, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "common/macros.h" + +#include + +struct lttng_buffer_view; +struct lttng_dynamic_buffer; +struct lttng_snapshot_output; + +LTTNG_HIDDEN +bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output); + +LTTNG_HIDDEN +bool lttng_snapshot_output_is_equal( + const struct lttng_snapshot_output *a, + const struct lttng_snapshot_output *b); + +LTTNG_HIDDEN +int lttng_snapshot_output_serialize( + const struct lttng_snapshot_output *output, + struct lttng_dynamic_buffer *buf); + +LTTNG_HIDDEN +ssize_t lttng_snapshot_output_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_snapshot_output **output_p); + +#endif /* COMMON_SNAPSHOT_H */ diff --git a/src/common/trigger.c b/src/common/trigger.c index dd161eb38..5c93c0ed4 100644 --- a/src/common/trigger.c +++ b/src/common/trigger.c @@ -7,12 +7,36 @@ #include #include +#include +#include +#include #include +#include #include +#include #include +#include + +static void lttng_trigger_set_internal_object_ownership( + struct lttng_trigger *trigger) +{ + /* + * This is necessary to faciliate the object destroy phase. A trigger + * created by a client does not OWN the internal objects (condition and + * action) but when a trigger object is created on the sessiond side or + * for listing triggers (mostly via create_from_buffer) the object is + * the owner of the internal objects. Hence, we set the ownership bool + * to true, in such case, to facilitate object lifetime management and + * internal ownership. + * + * TODO: I'm open to any other solution + */ + assert(trigger); + trigger->owns_internal_objects = true; +} LTTNG_HIDDEN -bool lttng_trigger_validate(struct lttng_trigger *trigger) +bool lttng_trigger_validate(const struct lttng_trigger *trigger) { bool valid; @@ -42,8 +66,15 @@ struct lttng_trigger *lttng_trigger_create( goto end; } + urcu_ref_init(&trigger->ref); + trigger->owns_internal_objects = false; + trigger->firing_policy.type = LTTNG_TRIGGER_FIRE_EVERY_N; + trigger->firing_policy.threshold = 1; trigger->condition = condition; trigger->action = action; + + trigger->creds.set = false; + end: return trigger; } @@ -54,11 +85,10 @@ struct lttng_condition *lttng_trigger_get_condition( return trigger ? trigger->condition : NULL; } -LTTNG_HIDDEN const struct lttng_condition *lttng_trigger_get_const_condition( const struct lttng_trigger *trigger) { - return trigger->condition; + return trigger ? trigger->condition : NULL; } struct lttng_action *lttng_trigger_get_action( @@ -67,33 +97,53 @@ struct lttng_action *lttng_trigger_get_action( return trigger ? trigger->action : NULL; } -LTTNG_HIDDEN const struct lttng_action *lttng_trigger_get_const_action( const struct lttng_trigger *trigger) { - return trigger->action; + return trigger ? trigger->action : NULL; } -void lttng_trigger_destroy(struct lttng_trigger *trigger) +static void trigger_destroy_ref(struct urcu_ref *ref) { - if (!trigger) { - return; + struct lttng_trigger *trigger = + container_of(ref, struct lttng_trigger, ref); + + if (trigger->owns_internal_objects) { + struct lttng_action *action = lttng_trigger_get_action(trigger); + struct lttng_condition *condition = + lttng_trigger_get_condition(trigger); + + assert(action); + assert(condition); + lttng_action_destroy(action); + lttng_condition_destroy(condition); } + free(trigger->name); free(trigger); } +void lttng_trigger_destroy(struct lttng_trigger *trigger) +{ + lttng_trigger_put(trigger); +} + LTTNG_HIDDEN ssize_t lttng_trigger_create_from_buffer( const struct lttng_buffer_view *src_view, struct lttng_trigger **trigger) { - ssize_t ret, offset = 0, condition_size, action_size; + ssize_t ret, offset = 0, condition_size, action_size, name_size = 0; + enum lttng_trigger_status status; struct lttng_condition *condition = NULL; struct lttng_action *action = NULL; const struct lttng_trigger_comm *trigger_comm; struct lttng_buffer_view condition_view; struct lttng_buffer_view action_view; + struct lttng_buffer_view name_view; + const char *name = NULL; + unsigned long long firing_threshold; + enum lttng_trigger_firing_policy_type firing_policy; if (!src_view || !trigger) { ret = -1; @@ -104,6 +154,22 @@ ssize_t lttng_trigger_create_from_buffer( trigger_comm = (const struct lttng_trigger_comm *) src_view->data; offset += sizeof(*trigger_comm); + firing_policy = trigger_comm->policy_type; + firing_threshold = trigger_comm->policy_threshold; + if (trigger_comm->name_length != 0) { + name_view = lttng_buffer_view_from_view( + src_view, offset, trigger_comm->name_length); + name = name_view.data; + if (trigger_comm->name_length == 1 || + name[trigger_comm->name_length - 1] != '\0' || + strlen(name) != trigger_comm->name_length - 1) { + ret = -1; + goto end; + } + offset += trigger_comm->name_length; + name_size = trigger_comm->name_length; + } + condition_view = lttng_buffer_view_from_view(src_view, offset, -1); /* struct lttng_condition */ @@ -125,7 +191,7 @@ ssize_t lttng_trigger_create_from_buffer( offset += action_size; /* Unexpected size of inner-elements; the buffer is corrupted. */ - if ((ssize_t) trigger_comm->length != condition_size + action_size) { + if ((ssize_t) trigger_comm->length != condition_size + action_size + name_size) { ret = -1; goto error; } @@ -135,6 +201,26 @@ ssize_t lttng_trigger_create_from_buffer( ret = -1; goto error; } + + /* Take ownership of the internal object from there */ + lttng_trigger_set_internal_object_ownership(*trigger); + condition = NULL; + action = NULL; + + if (name) { + status = lttng_trigger_set_name(*trigger, name); + if (status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + goto end; + } + } + + status = lttng_trigger_set_firing_policy(*trigger, firing_policy, firing_threshold); + if (status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + goto end; + } + ret = offset; end: return ret; @@ -149,15 +235,27 @@ error: * for the detailed format. */ LTTNG_HIDDEN -int lttng_trigger_serialize(struct lttng_trigger *trigger, - struct lttng_dynamic_buffer *buf) +int lttng_trigger_serialize(const struct lttng_trigger *trigger, + struct lttng_dynamic_buffer *buf, + int *fd_to_send) { int ret; - size_t header_offset, size_before_payload; + size_t header_offset, size_before_payload, size_name; struct lttng_trigger_comm trigger_comm = { 0 }; struct lttng_trigger_comm *header; header_offset = buf->size; + + if (trigger->name != NULL) { + size_name = strlen(trigger->name) + 1; + } else { + size_name = 0; + } + + trigger_comm.name_length = size_name; + trigger_comm.policy_type = (uint8_t) trigger->firing_policy.type; + trigger_comm.policy_threshold = (uint64_t) trigger->firing_policy.threshold; + ret = lttng_dynamic_buffer_append(buf, &trigger_comm, sizeof(trigger_comm)); if (ret) { @@ -165,7 +263,14 @@ int lttng_trigger_serialize(struct lttng_trigger *trigger, } size_before_payload = buf->size; - ret = lttng_condition_serialize(trigger->condition, buf); + + /* Trigger name */ + ret = lttng_dynamic_buffer_append(buf, trigger->name, size_name); + if (ret) { + goto end; + } + + ret = lttng_condition_serialize(trigger->condition, buf, fd_to_send); if (ret) { goto end; } @@ -181,3 +286,478 @@ int lttng_trigger_serialize(struct lttng_trigger *trigger, end: return ret; } + +enum lttng_trigger_status lttng_trigger_set_name(struct lttng_trigger *trigger, const char* name) +{ + char *name_copy = NULL; + enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK; + + if (!trigger || !name || + strlen(name) == 0) { + status = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_TRIGGER_STATUS_ERROR; + goto end; + } + + if (trigger->name) { + free(trigger->name); + } + + trigger->name = name_copy; + name_copy = NULL; +end: + return status; +} + +enum lttng_trigger_status lttng_trigger_get_name(const struct lttng_trigger *trigger, const char **name) +{ + enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK; + + if (!trigger || !name) { + status = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + if (!trigger->name) { + status = LTTNG_TRIGGER_STATUS_UNSET; + } + + *name = trigger->name; +end: + return status; +} + +LTTNG_HIDDEN +int lttng_trigger_assign(struct lttng_trigger *dst, + const struct lttng_trigger *src) +{ + int ret = 0; + enum lttng_trigger_status status; + /* todo some validation */ + + status = lttng_trigger_set_name(dst, src->name); + if (status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + ERR("Failed to set name for trigger"); + goto end; + } +end: + return ret; +} + +LTTNG_HIDDEN +void lttng_trigger_set_key(struct lttng_trigger *trigger, uint64_t key) +{ + assert(trigger); + trigger->key.value = key; + trigger->key.set = true; +} + +LTTNG_HIDDEN +uint64_t lttng_trigger_get_key(const struct lttng_trigger *trigger) +{ + assert(trigger); + + assert(trigger->key.set == true); + return trigger->key.value; +} + +LTTNG_HIDDEN +int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset) +{ + int ret = 0; + char *generated_name = NULL; + assert(trigger->key.set); + + ret = asprintf(&generated_name, "T%" PRIu64 "", trigger->key.value + offset); + if (ret < 0) { + ERR("Failed to generate trigger name"); + ret = -1; + goto end; + } + + if (trigger->name) { + free(trigger->name); + } + trigger->name = generated_name; +end: + return ret; +} + +LTTNG_HIDDEN +bool lttng_trigger_is_equal( + const struct lttng_trigger *a, const struct lttng_trigger *b) +{ + /* TODO: Optimization: for now a trigger with a firing policy that is + * not the same even if the conditions and actions is the same is + * treated as a "completely" different trigger. In a perfect world we + * would simply add a supplemental counter internally (sessiond side) to + * remove overhead on the tracer side. + */ + if (a->firing_policy.type != b->firing_policy.type) { + return false; + } + + if (a->firing_policy.threshold != b->firing_policy.threshold) { + return false; + } + + /* + * Name is not taken into account since it is cosmetic only + */ + if (!lttng_condition_is_equal(a->condition, b->condition)) { + return false; + } + if (!lttng_action_is_equal(a->action, b->action)) { + return false; + } + + return true; +} + +static void delete_trigger_array_element(void *ptr) +{ + struct lttng_trigger *trigger = ptr; + lttng_trigger_destroy(trigger); +} + +LTTNG_HIDDEN +struct lttng_triggers *lttng_triggers_create(void) +{ + struct lttng_triggers *triggers = NULL; + + triggers = zmalloc(sizeof(*triggers)); + if (!triggers) { + goto error; + } + + lttng_dynamic_pointer_array_init(&triggers->array, delete_trigger_array_element); + + return triggers; +error: + free(triggers); + return NULL; +} + +LTTNG_HIDDEN +struct lttng_trigger *lttng_triggers_get_pointer_of_index( + const struct lttng_triggers *triggers, unsigned int index) +{ + assert(triggers); + if (index >= lttng_dynamic_pointer_array_get_count(&triggers->array)) { + return NULL; + } + return lttng_dynamic_pointer_array_get_pointer(&triggers->array, index); +} + +LTTNG_HIDDEN +int lttng_triggers_add( + struct lttng_triggers *triggers, struct lttng_trigger *trigger) +{ + assert(triggers); + assert(trigger); + + return lttng_dynamic_pointer_array_add_pointer(&triggers->array, trigger); +} + +const struct lttng_trigger *lttng_triggers_get_at_index( + const struct lttng_triggers *triggers, unsigned int index) +{ + assert(triggers); + return lttng_triggers_get_pointer_of_index(triggers, index); +} + +enum lttng_trigger_status lttng_triggers_get_count(const struct lttng_triggers *triggers, unsigned int *count) +{ + enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK; + + if (!triggers || !count) { + status = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&triggers->array); +end: + return status; +} + +void lttng_triggers_destroy(struct lttng_triggers *triggers) +{ + if (!triggers) { + return; + } + + lttng_dynamic_pointer_array_reset(&triggers->array); + free(triggers); +} + +int lttng_triggers_serialize(const struct lttng_triggers *triggers, + struct lttng_dynamic_buffer *buffer) +{ + int ret; + unsigned int count; + size_t header_offset, size_before_payload; + struct lttng_triggers_comm triggers_comm = { 0 }; + struct lttng_triggers_comm *header; + struct lttng_trigger *trigger; + enum lttng_trigger_status status; + + header_offset = buffer->size; + + status = lttng_triggers_get_count(triggers, &count); + if (status != LTTNG_TRIGGER_STATUS_OK) { + ret = LTTNG_ERR_INVALID; + goto end; + } + + triggers_comm.count = count; + + ret = lttng_dynamic_buffer_append(buffer, &triggers_comm, + sizeof(triggers_comm)); + if (ret) { + goto end; + } + + size_before_payload = buffer->size; + + for (int i = 0; i < count; i++) { + trigger = lttng_triggers_get_pointer_of_index(triggers, i); + if (!trigger) { + assert(0); + } + + ret = lttng_trigger_serialize(trigger, buffer, NULL); + if (ret) { + goto end; + } + } + + /* Update payload size. */ + header = (struct lttng_triggers_comm *) ((char *) buffer->data + header_offset); + header->length = buffer->size - size_before_payload; +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_triggers_create_from_buffer( + const struct lttng_buffer_view *src_view, + struct lttng_triggers **triggers) +{ + ssize_t ret, offset = 0, trigger_size, triggers_size = 0; + const struct lttng_triggers_comm *triggers_comm; + struct lttng_buffer_view trigger_view; + struct lttng_triggers *local_triggers = NULL; + + if (!src_view || !triggers) { + ret = -1; + goto error; + } + + /* lttng_trigger_comms header */ + triggers_comm = (const struct lttng_triggers_comm *) src_view->data; + offset += sizeof(*triggers_comm); + + local_triggers = lttng_triggers_create(); + if (!local_triggers) { + ret = -1; + goto error; + } + + for (int i = 0; i < triggers_comm->count; i++) { + struct lttng_trigger *trigger = NULL; + trigger_view = lttng_buffer_view_from_view(src_view, offset, -1); + trigger_size = lttng_trigger_create_from_buffer(&trigger_view, + &trigger); + if (trigger_size < 0) { + ret = trigger_size; + goto error; + } + + /* Pass ownership of the trigger to the collection */ + ret = lttng_triggers_add(local_triggers, trigger); + if (ret < 0) { + assert(0); + } + trigger = NULL; + + offset += trigger_size; + triggers_size += trigger_size; + } + + /* Unexpected size of inner-elements; the buffer is corrupted. */ + if ((ssize_t) triggers_comm->length != triggers_size) { + ret = -1; + goto error; + } + + /* Pass ownership to caller */ + *triggers = local_triggers; + local_triggers = NULL; + + ret = offset; +error: + + lttng_triggers_destroy(local_triggers); + return ret; +} + +LTTNG_HIDDEN +const struct lttng_credentials *lttng_trigger_get_credentials( + const struct lttng_trigger *trigger) +{ + assert(trigger->creds.set); + return &(trigger->creds.credentials); +} + +LTTNG_HIDDEN +void lttng_trigger_set_credentials( + struct lttng_trigger *trigger, uid_t uid, gid_t gid) +{ + trigger->creds.credentials.uid = uid; + trigger->creds.credentials.gid = gid; + trigger->creds.set = true; +} + +enum lttng_trigger_status lttng_trigger_set_firing_policy( + struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy_type policy_type, + unsigned long long threshold) +{ + enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK; + assert(trigger); + + if (threshold < 1) { + ret = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + trigger->firing_policy.type = policy_type; + trigger->firing_policy.threshold = threshold; + +end: + return ret; +} + +enum lttng_trigger_status lttng_trigger_get_firing_policy( + const struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy_type *policy_type, + unsigned long long *threshold) +{ + enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK; + + if (!trigger || !policy_type || !threshold) { + status = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + *policy_type = trigger->firing_policy.type; + *threshold = trigger->firing_policy.threshold; + +end: + return status; +} + +LTTNG_HIDDEN +bool lttng_trigger_is_ready_to_fire(struct lttng_trigger *trigger) +{ + assert(trigger); + bool ready_to_fire = false; + + trigger->firing_policy.current_count++; + + switch (trigger->firing_policy.type) { + case LTTNG_TRIGGER_FIRE_EVERY_N: + if (trigger->firing_policy.current_count == trigger->firing_policy.threshold) { + trigger->firing_policy.current_count = 0; + ready_to_fire = true; + } + break; + case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N: + if (trigger->firing_policy.current_count == trigger->firing_policy.threshold) { + /* TODO: remove the trigger of at least deactivate it on + * the tracers side to remove any work overhead on the + * traced application or kernel since the trigger will + * never fire again. + * Still this branch should be left here since event + * could still be in the pipe. These will be discarded. + */ + ready_to_fire = true; + } + break; + default: + assert(0); + }; + + return ready_to_fire; +} + +LTTNG_HIDDEN +void lttng_trigger_get(struct lttng_trigger *trigger) +{ + urcu_ref_get(&trigger->ref); +} + +LTTNG_HIDDEN +void lttng_trigger_put(struct lttng_trigger *trigger) +{ + if (!trigger) { + return; + } + + urcu_ref_put(&trigger->ref , trigger_destroy_ref); +} + +LTTNG_HIDDEN +enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction( + const struct lttng_trigger *trigger) +{ + enum lttng_domain_type type = LTTNG_DOMAIN_NONE; + const struct lttng_event_rule *event_rule; + enum lttng_condition_status c_status; + enum lttng_condition_type c_type; + + assert(trigger); + assert(trigger->condition); + c_type = lttng_condition_get_type(trigger->condition); + if (c_type == LTTNG_CONDITION_TYPE_UNKNOWN) { + assert(0); + } + + switch (c_type) { + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + type = LTTNG_DOMAIN_NONE; + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + c_status = lttng_condition_event_rule_get_rule( + trigger->condition, &event_rule); + if (c_status != LTTNG_CONDITION_STATUS_OK) { + /* The condition object is invalid */ + assert(0); + } + + type = lttng_event_rule_get_domain_type(event_rule); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + c_status = lttng_condition_buffer_usage_get_domain_type( + trigger->condition, &type); + if (c_status != LTTNG_CONDITION_STATUS_OK) { + /* The condition object is invalid */ + assert(0); + } + break; + default: + type = LTTNG_DOMAIN_NONE; + break; + } + + return type; +} diff --git a/src/common/unix.c b/src/common/unix.c index 222b4a3d9..26eda52d9 100644 --- a/src/common/unix.c +++ b/src/common/unix.c @@ -232,12 +232,9 @@ retry: if (errno == EINTR) { goto retry; } else { - /* - * Only warn about EPIPE when quiet mode is - * deactivated. - * We consider EPIPE as expected. - */ - if (errno != EPIPE || !lttng_opt_quiet) { + /* We consider EPIPE and EAGAIN as expected. */ + if (!lttng_opt_quiet && + (errno != EPIPE && errno != EAGAIN)) { PERROR("recvmsg"); } goto end; @@ -320,12 +317,9 @@ retry: if (errno == EINTR) { goto retry; } else { - /* - * Only warn about EPIPE when quiet mode is - * deactivated. - * We consider EPIPE as expected. - */ - if (errno != EPIPE || !lttng_opt_quiet) { + /* We consider EPIPE and EAGAIN as expected. */ + if (!lttng_opt_quiet && + (errno != EPIPE && errno != EAGAIN)) { PERROR("sendmsg"); } goto end; diff --git a/src/common/userspace-probe.c b/src/common/userspace-probe.c index 508c9e8d4..4cc881025 100644 --- a/src/common/userspace-probe.c +++ b/src/common/userspace-probe.c @@ -144,6 +144,41 @@ void lttng_userspace_probe_location_destroy( } } +static +bool lttng_userspace_probe_location_function_is_equal(const struct lttng_userspace_probe_location *_a, + const struct lttng_userspace_probe_location *_b) +{ + bool is_equal = false; + struct lttng_userspace_probe_location_function *a, *b; + + a = container_of(_a, struct lttng_userspace_probe_location_function, parent); + b = container_of(_b, struct lttng_userspace_probe_location_function, parent); + + /* The binary_fd is not checked since it does not hold "immutable" + * information. + */ + + if (a->instrumentation_type != b->instrumentation_type) { + goto end; + } + + assert(a->function_name); + assert(b->function_name); + if (strcmp(a->function_name, b->function_name)) { + goto end; + } + + assert(a->binary_path); + assert(b->binary_path); + if (strcmp(a->binary_path, b->binary_path)) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + static struct lttng_userspace_probe_location * lttng_userspace_probe_location_function_create_no_check(const char *binary_path, const char *function_name, @@ -192,6 +227,7 @@ lttng_userspace_probe_location_function_create_no_check(const char *binary_path, ret = &location->parent; ret->lookup_method = lookup_method; ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION; + ret->equal = lttng_userspace_probe_location_function_is_equal; goto end; error: @@ -206,6 +242,43 @@ end: return ret; } +static +bool lttng_userspace_probe_location_tracepoint_is_equal(const struct lttng_userspace_probe_location *_a, + const struct lttng_userspace_probe_location *_b) +{ + bool is_equal = false; + struct lttng_userspace_probe_location_tracepoint *a, *b; + + a = container_of(_a, struct lttng_userspace_probe_location_tracepoint, parent); + b = container_of(_b, struct lttng_userspace_probe_location_tracepoint, parent); + + /* The binary_fd is not checked since it does not hold "immutable" + * information. + */ + + assert(a->probe_name); + assert(b->probe_name); + if (strcmp(a->probe_name, b->probe_name)) { + goto end; + } + + assert(a->provider_name); + assert(b->provider_name); + if (strcmp(a->provider_name, b->provider_name)) { + goto end; + } + + assert(a->binary_path); + assert(b->binary_path); + if (strcmp(a->binary_path, b->binary_path)) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + static struct lttng_userspace_probe_location * lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_path, const char *provider_name, const char *probe_name, @@ -261,6 +334,7 @@ lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_pat ret = &location->parent; ret->lookup_method = lookup_method; ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT; + ret->equal = lttng_userspace_probe_location_tracepoint_is_equal; goto end; error: @@ -1711,3 +1785,70 @@ struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy( err: return new_location; } + +LTTNG_HIDDEN +bool lttng_userspace_probe_location_is_equal( + const struct lttng_userspace_probe_location *a, + const struct lttng_userspace_probe_location *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +LTTNG_HIDDEN +int lttng_userspace_probe_location_set_binary_fd( + struct lttng_userspace_probe_location *location, int fd) +{ + int ret = 0; + const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + /* + * Set the file descriptor received from the client through the unix + * socket in the probe location. + */ + lookup = lttng_userspace_probe_location_get_lookup_method(location); + if (!lookup) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } + + /* + * From the kernel tracer's perspective, all userspace probe event types + * are all the same: a file and an offset. + */ + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + ret = lttng_userspace_probe_location_function_set_binary_fd( + location, fd); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + ret = lttng_userspace_probe_location_tracepoint_set_binary_fd( + location, fd); + break; + default: + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } + + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } +end: + return ret; +} diff --git a/src/common/utils.c b/src/common/utils.c index d9bacad0e..83d4dbe45 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -1527,3 +1527,40 @@ int utils_change_working_directory(const char *path) end: return ret; } + +LTTNG_HIDDEN +int utils_parse_unsigned_long_long(const char *str, + unsigned long long *value) +{ + int ret; + char *endptr; + + assert(str); + assert(value); + + errno = 0; + *value = strtoull(str, &endptr, 10); + + /* Conversion failed. Out of range? */ + if (errno != 0) { + ret = -1; + goto end; + } + + /* Not the end of the string? */ + if (*endptr) { + ret = -1; + goto end; + } + + /* Empty string? */ + if (endptr == str) { + ret = -1; + goto end; + } + + ret = 0; + +end: + return ret; +} diff --git a/src/common/utils.h b/src/common/utils.h index b6e5c97db..d5dc9d01a 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -55,4 +55,16 @@ int utils_get_memory_available(size_t *value); int utils_get_memory_total(size_t *value); int utils_change_working_directory(const char *path); +/* + * Parse `str` as an unsigned long long value. + * + * Return 0 on success. Return -1 on failure which can be because: + * + * - `str` is zero length + * - `str` contains invalid + */ +LTTNG_HIDDEN +int utils_parse_unsigned_long_long(const char *str, + unsigned long long *value); + #endif /* _COMMON_UTILS_H */ diff --git a/src/lib/lttng-ctl/Makefile.am b/src/lib/lttng-ctl/Makefile.am index de390175b..dcc12aa26 100644 --- a/src/lib/lttng-ctl/Makefile.am +++ b/src/lib/lttng-ctl/Makefile.am @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -SUBDIRS = filter +SUBDIRS = AM_CPPFLAGS += -I$(srcdir) -I$(builddir) @@ -15,8 +15,7 @@ liblttng_ctl_la_LDFLAGS = \ liblttng_ctl_la_LIBADD = \ $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \ - $(top_builddir)/src/common/libcommon.la \ - $(top_builddir)/src/lib/lttng-ctl/filter/libfilter.la + $(top_builddir)/src/common/libcommon.la pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = lttng-ctl.pc diff --git a/src/lib/lttng-ctl/channel.c b/src/lib/lttng-ctl/channel.c index f2a65885d..5fc77d0e4 100644 --- a/src/lib/lttng-ctl/channel.c +++ b/src/lib/lttng-ctl/channel.c @@ -632,7 +632,7 @@ enum lttng_notification_channel_status send_condition_command( goto end_unlock; } - ret = lttng_condition_serialize(condition, &buffer); + ret = lttng_condition_serialize(condition, &buffer, NULL); if (ret) { status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID; goto end_unlock; diff --git a/src/lib/lttng-ctl/lttng-ctl-health.c b/src/lib/lttng-ctl/lttng-ctl-health.c index d6a3e4f13..91108c166 100644 --- a/src/lib/lttng-ctl/lttng-ctl-health.c +++ b/src/lib/lttng-ctl/lttng-ctl-health.c @@ -62,6 +62,7 @@ const char *sessiond_thread_name[NR_HEALTH_SESSIOND_TYPES] = { [ HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH ] = "Session daemon application registration dispatcher", [ HEALTH_SESSIOND_TYPE_ROTATION ] = "Session daemon rotation manager", [ HEALTH_SESSIOND_TYPE_TIMER ] = "Session daemon timer manager", + [ HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR ] = "Session daemon trigger action executor", }; static diff --git a/src/lib/lttng-ctl/lttng-ctl-helper.h b/src/lib/lttng-ctl/lttng-ctl-helper.h index 8c14e1fdc..ed91148f6 100644 --- a/src/lib/lttng-ctl/lttng-ctl-helper.h +++ b/src/lib/lttng-ctl/lttng-ctl-helper.h @@ -58,6 +58,23 @@ int lttng_ctl_ask_sessiond_fds_no_cmd_header(struct lttcomm_session_msg *lsm, return lttng_ctl_ask_sessiond_fds_varlen(lsm, fds, nb_fd, NULL, 0, NULL, NULL, NULL); } + +/* + * Calls lttng_ctl_ask_sessiond_fds_varlen() with fds with no expected command + * header and with varlen data. + */ +static inline int lttng_ctl_ask_sessiond_fds_varlen_no_cmd_header( + struct lttcomm_session_msg *lsm, + const int *fds, + size_t nb_fd, + void *vardata, + size_t vardata_len, + void **user_payload_buf) +{ + return lttng_ctl_ask_sessiond_fds_varlen(lsm, fds, nb_fd, vardata, + vardata_len, user_payload_buf, NULL, NULL); +} + /* * Use this if no variable length data needs to be sent. */ diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 4e4912073..fff602305 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -38,26 +38,12 @@ #include #include -#include "filter/filter-ast.h" -#include "filter/filter-parser.h" -#include "filter/filter-bytecode.h" -#include "filter/memstream.h" +#include +#include +#include +#include #include "lttng-ctl-helper.h" -#ifdef DEBUG -static const int print_xml = 1; -#define dbg_printf(fmt, args...) \ - printf("[debug liblttng-ctl] " fmt, ## args) -#else -static const int print_xml = 0; -#define dbg_printf(fmt, args...) \ -do { \ - /* do nothing but check printf format */ \ - if (0) \ - printf("[debug liblttnctl] " fmt, ## args); \ -} while (0) -#endif - #define COPY_DOMAIN_PACKED(dst, src) \ do { \ struct lttng_domain _tmp_domain; \ @@ -896,7 +882,7 @@ static char *set_agent_filter(const char *filter, struct lttng_event *ev) /* Add loglevel filtering if any for the JUL domain. */ if (ev->loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { - char *op; + const char *op; if (ev->loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE) { op = ">="; @@ -930,133 +916,6 @@ error: return NULL; } -/* - * Generate the filter bytecode from a given filter expression string. Put the - * newly allocated parser context in ctxp and populate the lsm object with the - * expression len. - * - * Return 0 on success else a LTTNG_ERR_* code and ctxp is untouched. - */ -static int generate_filter(char *filter_expression, - struct lttcomm_session_msg *lsm, struct filter_parser_ctx **ctxp) -{ - int ret; - struct filter_parser_ctx *ctx = NULL; - FILE *fmem = NULL; - - assert(filter_expression); - assert(lsm); - assert(ctxp); - - /* - * Casting const to non-const, as the underlying function will use it in - * read-only mode. - */ - fmem = lttng_fmemopen((void *) filter_expression, - strlen(filter_expression), "r"); - if (!fmem) { - fprintf(stderr, "Error opening memory as stream\n"); - ret = -LTTNG_ERR_FILTER_NOMEM; - goto error; - } - ctx = filter_parser_ctx_alloc(fmem); - if (!ctx) { - fprintf(stderr, "Error allocating parser\n"); - ret = -LTTNG_ERR_FILTER_NOMEM; - goto filter_alloc_error; - } - ret = filter_parser_ctx_append_ast(ctx); - if (ret) { - fprintf(stderr, "Parse error\n"); - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - if (print_xml) { - ret = filter_visitor_print_xml(ctx, stdout, 0); - if (ret) { - fflush(stdout); - fprintf(stderr, "XML print error\n"); - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - } - - dbg_printf("Generating IR... "); - fflush(stdout); - ret = filter_visitor_ir_generate(ctx); - if (ret) { - fprintf(stderr, "Generate IR error\n"); - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - dbg_printf("done\n"); - - dbg_printf("Validating IR... "); - fflush(stdout); - ret = filter_visitor_ir_check_binary_op_nesting(ctx); - if (ret) { - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - - /* Normalize globbing patterns in the expression. */ - ret = filter_visitor_ir_normalize_glob_patterns(ctx); - if (ret) { - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - - /* Validate strings used as literals in the expression. */ - ret = filter_visitor_ir_validate_string(ctx); - if (ret) { - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - - /* Validate globbing patterns in the expression. */ - ret = filter_visitor_ir_validate_globbing(ctx); - if (ret) { - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - - dbg_printf("done\n"); - - dbg_printf("Generating bytecode... "); - fflush(stdout); - ret = filter_visitor_bytecode_generate(ctx); - if (ret) { - fprintf(stderr, "Generate bytecode error\n"); - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - dbg_printf("done\n"); - dbg_printf("Size of bytecode generated: %u bytes.\n", - bytecode_get_len(&ctx->bytecode->b)); - - lsm->u.enable.bytecode_len = sizeof(ctx->bytecode->b) - + bytecode_get_len(&ctx->bytecode->b); - lsm->u.enable.expression_len = strlen(filter_expression) + 1; - - /* No need to keep the memory stream. */ - if (fclose(fmem) != 0) { - PERROR("fclose"); - } - - *ctxp = ctx; - return 0; - -parse_error: - filter_ir_free(ctx); - filter_parser_ctx_free(ctx); -filter_alloc_error: - if (fclose(fmem) != 0) { - PERROR("fclose"); - } -error: - return ret; -} - /* * Enable event(s) for a channel, possibly with exclusions and a filter. * If no event name is specified, all events are enabled. @@ -1160,10 +1019,14 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, } } - ret = generate_filter(filter_expression, &lsm, &ctx); + ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx); if (ret) { goto filter_error; } + + lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b) + + bytecode_get_len(&ctx->bytecode->b); + lsm.u.enable.expression_len = strlen(filter_expression) + 1; } ret = lttng_dynamic_buffer_set_capacity(&send_buffer, @@ -1369,10 +1232,14 @@ int lttng_disable_event_ext(struct lttng_handle *handle, } } - ret = generate_filter(filter_expression, &lsm, &ctx); + ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx); if (ret) { goto filter_error; } + + lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b) + + bytecode_get_len(&ctx->bytecode->b); + lsm.u.enable.expression_len = strlen(filter_expression) + 1; } varlen_data = zmalloc(lsm.u.disable.bytecode_len @@ -3054,8 +2921,15 @@ end: int lttng_register_trigger(struct lttng_trigger *trigger) { int ret; + int reply_ret; struct lttcomm_session_msg lsm; struct lttng_dynamic_buffer buffer; + void *reply = NULL; + struct lttng_buffer_view reply_view; + struct lttng_trigger *reply_trigger = NULL; + bool send_fd = false; + int fd_to_send; + enum lttng_domain_type domain_type; lttng_dynamic_buffer_init(&buffer); if (!trigger) { @@ -3068,23 +2942,91 @@ int lttng_register_trigger(struct lttng_trigger *trigger) goto end; } - ret = lttng_trigger_serialize(trigger, &buffer); + domain_type = lttng_trigger_get_underlying_domain_type_restriction( + trigger); + + ret = lttng_trigger_serialize(trigger, &buffer, &fd_to_send); if (ret < 0) { ret = -LTTNG_ERR_UNK; goto end; } + if (getenv("LTTNG_REGISTER_TRIGGER_DRY_RUN")) { + /* + * Don't really send the request, just deserialize, validate + * that it is equal to the original trigger (to test + * serialization and deserialization), and return. + */ + struct lttng_buffer_view bv; + ssize_t sz; + + bv = lttng_buffer_view_from_dynamic_buffer(&buffer, 0, -1); + sz = lttng_trigger_create_from_buffer(&bv, &reply_trigger); + if (sz != bv.size) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + if (!reply_trigger) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + if (!lttng_trigger_is_equal(trigger, reply_trigger)) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + /* Give it a dummy name. */ + lttng_trigger_set_name(trigger, "yop"); + + ret = 0; + goto end; + } + + send_fd = fd_to_send >= 0; + memset(&lsm, 0, sizeof(lsm)); lsm.cmd_type = LTTNG_REGISTER_TRIGGER; + lsm.domain.type = domain_type; lsm.u.trigger.length = (uint32_t) buffer.size; - ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, buffer.data, - buffer.size, NULL); + reply_ret = lttng_ctl_ask_sessiond_fds_varlen_no_cmd_header(&lsm, + send_fd ? &fd_to_send : NULL, + send_fd ? 1 : 0, + buffer.data, + buffer.size, + &reply); + if (reply_ret < 0) { + ret = reply_ret; + goto end; + } else if (reply_ret == 0) { + /* Socket unexpectedly closed by the session daemon. */ + ret = -LTTNG_ERR_FATAL; + goto end; + } + + reply_view = lttng_buffer_view_init(reply, 0, reply_ret); + ret = lttng_trigger_create_from_buffer(&reply_view, &reply_trigger); + if (ret < 0) { + ret = -LTTNG_ERR_FATAL; + goto end; + } + + ret = lttng_trigger_assign(trigger, reply_trigger); + if (ret < 0) { + ret = -LTTNG_ERR_FATAL; + goto end; + } + + ret = 0; end: + free(reply); lttng_dynamic_buffer_reset(&buffer); + lttng_trigger_destroy(reply_trigger); return ret; } -int lttng_unregister_trigger(struct lttng_trigger *trigger) +int lttng_unregister_trigger(const struct lttng_trigger *trigger) { int ret; struct lttcomm_session_msg lsm; @@ -3101,7 +3043,7 @@ int lttng_unregister_trigger(struct lttng_trigger *trigger) goto end; } - ret = lttng_trigger_serialize(trigger, &buffer); + ret = lttng_trigger_serialize(trigger, &buffer, NULL); if (ret < 0) { ret = -LTTNG_ERR_UNK; goto end; @@ -3117,6 +3059,49 @@ end: return ret; } +/* + * Ask the session daemon for all registered triggers. + * Allocate a lttng_triggers collection. + * On error, returns a negative value. + */ +int lttng_list_triggers(struct lttng_triggers **triggers) +{ + int ret; + int reply_ret; + struct lttcomm_session_msg lsm; + struct lttng_buffer_view reply_view; + struct lttng_triggers *local_triggers = NULL; + void *reply = NULL; + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_LIST_TRIGGERS; + + reply_ret = lttng_ctl_ask_sessiond(&lsm, &reply); + if (reply_ret < 0) { + ret = reply_ret; + goto end; + } else if (reply_ret == 0) { + /* Socket unexpectedly closed by the session daemon. */ + ret = -LTTNG_ERR_FATAL; + goto end; + } + + reply_view = lttng_buffer_view_init(reply, 0, reply_ret); + ret = lttng_triggers_create_from_buffer(&reply_view, &local_triggers); + if (ret < 0) { + ret = -LTTNG_ERR_FATAL; + goto end; + } + + *triggers = local_triggers; + local_triggers = NULL; + ret = 0; +end: + free(reply); + free(local_triggers); + return ret; +} + static int lttng_track_untrack_id(struct lttng_handle *handle, enum lttng_tracker_type tracker_type, const struct lttng_tracker_id *id, diff --git a/src/lib/lttng-ctl/snapshot.c b/src/lib/lttng-ctl/snapshot.c index 0aebf1575..8d3ec2013 100644 --- a/src/lib/lttng-ctl/snapshot.c +++ b/src/lib/lttng-ctl/snapshot.c @@ -238,29 +238,29 @@ void lttng_snapshot_output_destroy(struct lttng_snapshot_output *obj) * Getter family functions of snapshot output. */ -uint32_t lttng_snapshot_output_get_id(struct lttng_snapshot_output *output) +uint32_t lttng_snapshot_output_get_id(const struct lttng_snapshot_output *output) { return output->id; } const char *lttng_snapshot_output_get_name( - struct lttng_snapshot_output *output) + const struct lttng_snapshot_output *output) { return output->name; } -const char *lttng_snapshot_output_get_data_url(struct lttng_snapshot_output *output) +const char *lttng_snapshot_output_get_data_url(const struct lttng_snapshot_output *output) { return output->data_url; } -const char *lttng_snapshot_output_get_ctrl_url(struct lttng_snapshot_output *output) +const char *lttng_snapshot_output_get_ctrl_url(const struct lttng_snapshot_output *output) { return output->ctrl_url; } uint64_t lttng_snapshot_output_get_maxsize( - struct lttng_snapshot_output *output) + const struct lttng_snapshot_output *output) { return output->max_size; } @@ -323,3 +323,126 @@ int lttng_snapshot_output_set_data_url(const char *url, lttng_ctl_copy_string(output->data_url, url, sizeof(output->data_url)); return 0; } + +int lttng_snapshot_output_set_local_path(const char *path, + struct lttng_snapshot_output *output) +{ + int ret; + struct lttng_uri *uris = NULL; + ssize_t num_uris; + + if (!path || !output) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + num_uris = uri_parse_str_urls(path, NULL, &uris); + if (num_uris != 1) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (uris[0].dtype != LTTNG_DST_PATH) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(output->ctrl_url, path, sizeof(output->ctrl_url)); + if (ret != 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + +end: + free(uris); + return ret; +} + +int lttng_snapshot_output_set_network_url(const char *url, + struct lttng_snapshot_output *output) +{ + int ret; + struct lttng_uri *uris = NULL; + ssize_t num_uris; + + if (!url || !output) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + num_uris = uri_parse_str_urls(url, NULL, &uris); + if (num_uris != 2) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (uris[0].dtype != LTTNG_DST_IPV4 && + uris[0].dtype != LTTNG_DST_IPV6) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (uris[1].dtype != LTTNG_DST_IPV4 && + uris[1].dtype != LTTNG_DST_IPV6) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(output->ctrl_url, url, sizeof(output->ctrl_url)); + if (ret != 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + +end: + free(uris); + return ret; +} + +int lttng_snapshot_output_set_network_urls( + const char *ctrl_url, const char *data_url, + struct lttng_snapshot_output *output) +{ + int ret; + struct lttng_uri *uris = NULL; + ssize_t num_uris; + + if (!ctrl_url || !data_url || !output) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + num_uris = uri_parse_str_urls(ctrl_url, data_url, &uris); + if (num_uris != 2) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (uris[0].dtype != LTTNG_DST_IPV4 && + uris[0].dtype != LTTNG_DST_IPV6) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (uris[1].dtype != LTTNG_DST_IPV4 && + uris[1].dtype != LTTNG_DST_IPV6) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(output->ctrl_url, ctrl_url, sizeof(output->ctrl_url)); + if (ret != 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(output->data_url, data_url, sizeof(output->data_url)); + if (ret != 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + +end: + free(uris); + return ret; +} diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am index cbac90da7..6020964f1 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -27,8 +27,14 @@ TESTS = tools/filtering/test_invalid_filter \ tools/crash/test_crash \ tools/regen-metadata/test_ust \ tools/regen-statedump/test_ust \ - tools/notification/test_notification_ust \ - tools/notification/test_notification_kernel \ + tools/notification/test_notification_ust_error \ + tools/notification/test_notification_ust_buffer_usage \ + tools/notification/test_notification_ust_event_rule_condition_exclusion \ + tools/notification/test_notification_kernel_error \ + tools/notification/test_notification_kernel_buffer_usage \ + tools/notification/test_notification_kernel_instrumentation \ + tools/notification/test_notification_kernel_syscall \ + tools/notification/test_notification_kernel_userspace_probe \ tools/notification/test_notification_multi_app \ tools/rotation/test_ust \ tools/rotation/test_kernel \ @@ -39,7 +45,10 @@ TESTS = tools/filtering/test_invalid_filter \ tools/notification/test_notification_multi_app \ tools/clear/test_ust \ tools/clear/test_kernel \ - tools/tracker/test_event_tracker + tools/tracker/test_event_tracker \ + tools/trigger/test_add_trigger_cli \ + tools/trigger/test_list_triggers_cli \ + tools/trigger/test_remove_trigger_cli if HAVE_LIBLTTNG_UST_CTL SUBDIRS += ust diff --git a/tests/regression/kernel/select_poll_epoll.c b/tests/regression/kernel/select_poll_epoll.c index 33b8cb5d5..42d554cb2 100644 --- a/tests/regression/kernel/select_poll_epoll.c +++ b/tests/regression/kernel/select_poll_epoll.c @@ -46,7 +46,7 @@ struct ppoll_thread_data { int value; }; -void test_select_big(void) +static void test_select_big(void) { fd_set rfds, wfds, exfds; struct timeval tv; @@ -95,7 +95,7 @@ end: return; } -void test_pselect(void) +static void test_pselect(void) { fd_set rfds; struct timespec tv; @@ -128,7 +128,7 @@ void test_pselect(void) } -void test_select(void) +static void test_select(void) { fd_set rfds; struct timeval tv; @@ -161,7 +161,7 @@ void test_select(void) } -void test_poll(void) +static void test_poll(void) { struct pollfd ufds[NB_FD]; char buf[BUF_SIZE]; @@ -185,7 +185,7 @@ void test_poll(void) } } -void test_ppoll(void) +static void test_ppoll(void) { struct pollfd ufds[NB_FD]; char buf[BUF_SIZE]; @@ -217,7 +217,7 @@ void test_ppoll(void) } } -void test_ppoll_big(void) +static void test_ppoll_big(void) { struct pollfd ufds[MAX_FDS]; char buf[BUF_SIZE]; @@ -256,7 +256,7 @@ void test_ppoll_big(void) return; } -void test_epoll(void) +static void test_epoll(void) { int ret, epollfd; char buf[BUF_SIZE]; @@ -298,7 +298,7 @@ end: return; } -void test_pepoll(void) +static void test_pepoll(void) { int ret, epollfd; char buf[BUF_SIZE]; @@ -340,7 +340,7 @@ end: return; } -void run_working_cases(void) +static void run_working_cases(void) { int ret; int pipe_fds[2]; @@ -386,7 +386,7 @@ end: * segfault (eventually with a "*** stack smashing detected ***" message). * The event should contain an array of 100 FDs filled with garbage. */ -void ppoll_fds_buffer_overflow(void) +static void ppoll_fds_buffer_overflow(void) { struct pollfd ufds[NB_FD]; char buf[BUF_SIZE]; @@ -417,7 +417,7 @@ void ppoll_fds_buffer_overflow(void) * cleanly fail with a "Invalid argument". * The event should contain an empty array of FDs and overflow = 1. */ -void ppoll_fds_ulong_max(void) +static void ppoll_fds_ulong_max(void) { struct pollfd ufds[NB_FD]; char buf[BUF_SIZE]; @@ -447,7 +447,7 @@ void ppoll_fds_ulong_max(void) * Pass an invalid file descriptor to pselect6(). The syscall should return * -EBADF. The recorded event should contain a "ret = -EBADF (-9)". */ -void pselect_invalid_fd(void) +static void pselect_invalid_fd(void) { fd_set rfds; int ret; @@ -493,7 +493,7 @@ error: * Invalid pointer as writefds, should output a ppoll event * with 0 FDs. */ -void pselect_invalid_pointer(void) +static void pselect_invalid_pointer(void) { fd_set rfds; int ret; @@ -524,7 +524,7 @@ void pselect_invalid_pointer(void) * Pass an invalid pointer to epoll_pwait, should fail with * "Bad address", the event returns 0 FDs. */ -void epoll_pwait_invalid_pointer(void) +static void epoll_pwait_invalid_pointer(void) { int ret, epollfd; char buf[BUF_SIZE]; @@ -568,7 +568,7 @@ end: * Set maxevents to INT_MAX, should output "Invalid argument" * The event should return an empty array. */ -void epoll_pwait_int_max(void) +static void epoll_pwait_int_max(void) { int ret, epollfd; char buf[BUF_SIZE]; @@ -607,7 +607,7 @@ end: return; } -void *ppoll_writer(void *arg) +static void *ppoll_writer(void *arg) { struct ppoll_thread_data *data = (struct ppoll_thread_data *) arg; @@ -620,7 +620,7 @@ void *ppoll_writer(void *arg) return NULL; } -void do_ppoll(int *fds, struct pollfd *ufds) +static void do_ppoll(int *fds, struct pollfd *ufds) { int i, ret; struct timespec ts; @@ -649,7 +649,7 @@ void do_ppoll(int *fds, struct pollfd *ufds) } } -void stress_ppoll(int *fds, int value) +static void stress_ppoll(int *fds, int value) { pthread_t writer; int iter, ret; @@ -690,7 +690,7 @@ end: * * ppoll should work as expected and the trace should be readable at the end. */ -void ppoll_concurrent_write(void) +static void ppoll_concurrent_write(void) { int i, ret, fds[MAX_FDS]; @@ -715,7 +715,7 @@ void ppoll_concurrent_write(void) return; } -void *epoll_pwait_writer(void *addr) +static void *epoll_pwait_writer(void *addr) { srand(time(NULL)); @@ -732,7 +732,7 @@ void *epoll_pwait_writer(void *addr) * buffer allocated for the returned data. This should randomly segfault. * The trace should be readable and no kernel OOPS should occur. */ -void epoll_pwait_concurrent_munmap(void) +static void epoll_pwait_concurrent_munmap(void) { int ret, epollfd, i, fds[MAX_FDS]; char buf[BUF_SIZE]; @@ -814,16 +814,7 @@ end: return; } -void usage(poptContext optCon, int exitcode, char *error, char *addl) -{ - poptPrintUsage(optCon, stderr, 0); - if (error) { - fprintf(stderr, "%s: %s\n", error, addl); - } - exit(exitcode); -} - -void print_list(void) +static void print_list(void) { fprintf(stderr, "Test list (-t X):\n"); fprintf(stderr, "\t1: Working cases for select, pselect6, poll, ppoll " diff --git a/tests/regression/tools/Makefile.am b/tests/regression/tools/Makefile.am index 22691e5e2..d561a6479 100644 --- a/tests/regression/tools/Makefile.am +++ b/tests/regression/tools/Makefile.am @@ -2,4 +2,4 @@ SUBDIRS = streaming filtering health tracefile-limits snapshots live exclusion save-load mi \ wildcard crash regen-metadata regen-statedump notification rotation \ - base-path metadata working-directory relayd-grouping clear tracker + base-path metadata working-directory relayd-grouping clear tracker trigger diff --git a/tests/regression/tools/exclusion/test_exclusion b/tests/regression/tools/exclusion/test_exclusion index 6cd808c6d..ed653b72f 100755 --- a/tests/regression/tools/exclusion/test_exclusion +++ b/tests/regression/tools/exclusion/test_exclusion @@ -16,7 +16,7 @@ TESTAPP_NAME="gen-ust-nevents" TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" NR_ITER=100 NR_USEC_WAIT=1 -NUM_TESTS=149 +NUM_TESTS=178 source $TESTDIR/utils/utils.sh @@ -25,12 +25,45 @@ function enable_ust_lttng_all_event_exclusion() sess_name="$1" exclusion="$2" - $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event -a -s $sess_name -u -x "$exclusion" + $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event -u "tp:*" -s $sess_name -x "$exclusion" > /dev/null } function run_apps { - $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT >/dev/null 2>&1 + $TESTAPP_BIN --iter $NR_ITER --wait $NR_USEC_WAIT >/dev/null 2>&1 + ok $? "Running test application" +} + +# Testing for the absence of an event when testing exclusion is tricky. An +# event could be absent because our exclusion mechanism works but also because +# the event was not generate in the first place. This function test the ability +# of our test suite to generate events. +function dry_run +{ + trace_path=$(mktemp -d) + + # Create session + create_lttng_session_ok $SESSION_NAME $trace_path + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event -u "tp:*" -s $SESSION_NAME > /dev/null + ok $? "Enabling events without exclusion" + + # Trace apps + start_lttng_tracing_ok $SESSION_NAME + run_apps + stop_lttng_tracing_ok $SESSION_NAME + + nb_events=$(babeltrace $trace_path | wc -l) + if [ "$nb_events" -ne "0" ]; then + ok 0 "Events were found during the dry run without exclusion" + else + fail "No events were found during the dry run without exclusion" + fi + + rm -rf $trace_path + + # Destroy session + destroy_lttng_session_ok $SESSION_NAME } function test_exclusion @@ -53,7 +86,7 @@ function test_exclusion # Destroy session destroy_lttng_session_ok $SESSION_NAME - stats=`babeltrace $trace_path | $STATS_BIN --tracepoint "$event_name_expected_to_be_missing" | grep -v index` + stats=`babeltrace $trace_path | $STATS_BIN --tracepoint "$event_name_expected_to_be_missing" | grep -v index 2> /dev/null` if [ ! -z "$stats" ]; then fail "Excluded event \"$event_name_expected_to_be_missing\" was found in trace!" else @@ -83,10 +116,14 @@ function test_exclusion_fail plan_tests $NUM_TESTS -print_test_banner $TEST_DESC +print_test_banner "$TEST_DESC" start_lttng_sessiond +diag "Enable event without exclusion" +dry_run + +diag "Enable event with exclusion" test_exclusion 'tp:tptest2' 'tp:tptest2' test_exclusion 'tp:tptest3' 'tp:tptest3' test_exclusion 'tp:tptest*' 'tp:tptest1' @@ -110,12 +147,12 @@ test_exclusion '*3' 'tp:tptest3' test_exclusion 'tp*test3,*2' 'tp:tptest2' test_exclusion '**tp*test3,*2' 'tp:tptest3' -# Cannot use exclusions with non-globbing event name +diag "Cannot use exclusions with non-globbing event name" test_exclusion_fail "allo" "lol" test_exclusion_fail "allo" "meow,lol" test_exclusion_fail "allo" "z*em" -# Exclusion name excludes all possible event names +diag "Exclusion name excludes all possible event names" test_exclusion_fail "allo*" "all*" test_exclusion_fail "allo*" "ze,all*,yes" diff --git a/tests/regression/tools/health/health_exit.c b/tests/regression/tools/health/health_exit.c index e9324fa2e..6d04c9da7 100644 --- a/tests/regression/tools/health/health_exit.c +++ b/tests/regression/tools/health/health_exit.c @@ -30,6 +30,7 @@ int check_env_var(const char *env) /* Session daemon */ +int __testpoint_sessiond_thread_manage_clients(void); int __testpoint_sessiond_thread_manage_clients(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CLIENTS_EXIT"; @@ -42,6 +43,7 @@ int __testpoint_sessiond_thread_manage_clients(void) return 0; } +int __testpoint_sessiond_thread_registration_apps(void); int __testpoint_sessiond_thread_registration_apps(void) { const char *var = "LTTNG_SESSIOND_THREAD_REG_APPS_EXIT"; @@ -53,6 +55,7 @@ int __testpoint_sessiond_thread_registration_apps(void) return 0; } +int __testpoint_sessiond_thread_manage_apps(void); int __testpoint_sessiond_thread_manage_apps(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_APPS_EXIT"; @@ -65,6 +68,7 @@ int __testpoint_sessiond_thread_manage_apps(void) return 0; } +int __testpoint_sessiond_thread_manage_kernel(void); int __testpoint_sessiond_thread_manage_kernel(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_KERNEL_EXIT"; @@ -76,6 +80,7 @@ int __testpoint_sessiond_thread_manage_kernel(void) return 0; } +int __testpoint_sessiond_thread_manage_consumer(void); int __testpoint_sessiond_thread_manage_consumer(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CONSUMER_EXIT"; @@ -87,6 +92,7 @@ int __testpoint_sessiond_thread_manage_consumer(void) return 0; } +int __testpoint_sessiond_thread_ht_cleanup(void); int __testpoint_sessiond_thread_ht_cleanup(void) { const char *var = "LTTNG_SESSIOND_THREAD_HT_CLEANUP_EXIT"; @@ -99,6 +105,7 @@ int __testpoint_sessiond_thread_ht_cleanup(void) return 0; } +int __testpoint_sessiond_thread_app_manage_notify(void); int __testpoint_sessiond_thread_app_manage_notify(void) { const char *var = "LTTNG_SESSIOND_THREAD_APP_MANAGE_NOTIFY_EXIT"; @@ -111,6 +118,7 @@ int __testpoint_sessiond_thread_app_manage_notify(void) return 0; } +int __testpoint_sessiond_thread_app_reg_dispatch(void); int __testpoint_sessiond_thread_app_reg_dispatch(void) { const char *var = "LTTNG_SESSIOND_THREAD_APP_REG_DISPATCH_EXIT"; @@ -124,6 +132,7 @@ int __testpoint_sessiond_thread_app_reg_dispatch(void) /* Consumer daemon */ +int __testpoint_consumerd_thread_channel(void); int __testpoint_consumerd_thread_channel(void) { const char *var = "LTTNG_CONSUMERD_THREAD_CHANNEL_EXIT"; @@ -136,6 +145,7 @@ int __testpoint_consumerd_thread_channel(void) return 0; } +int __testpoint_consumerd_thread_metadata(void); int __testpoint_consumerd_thread_metadata(void) { const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_EXIT"; @@ -148,6 +158,7 @@ int __testpoint_consumerd_thread_metadata(void) return 0; } +int __testpoint_consumerd_thread_data(void); int __testpoint_consumerd_thread_data(void) { const char *var = "LTTNG_CONSUMERD_THREAD_DATA_EXIT"; @@ -160,6 +171,7 @@ int __testpoint_consumerd_thread_data(void) return 0; } +int __testpoint_consumerd_thread_sessiond(void); int __testpoint_consumerd_thread_sessiond(void) { const char *var = "LTTNG_CONSUMERD_THREAD_SESSIOND_EXIT"; @@ -172,6 +184,7 @@ int __testpoint_consumerd_thread_sessiond(void) return 0; } +int __testpoint_consumerd_thread_metadata_timer(void); int __testpoint_consumerd_thread_metadata_timer(void) { const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_TIMER_EXIT"; @@ -185,6 +198,7 @@ int __testpoint_consumerd_thread_metadata_timer(void) /* Relay daemon */ +int __testpoint_relayd_thread_dispatcher(void); int __testpoint_relayd_thread_dispatcher(void) { const char *var = "LTTNG_RELAYD_THREAD_DISPATCHER_EXIT"; @@ -196,6 +210,7 @@ int __testpoint_relayd_thread_dispatcher(void) return 0; } +int __testpoint_relayd_thread_worker(void); int __testpoint_relayd_thread_worker(void) { const char *var = "LTTNG_RELAYD_THREAD_WORKER_EXIT"; @@ -208,6 +223,7 @@ int __testpoint_relayd_thread_worker(void) return 0; } +int __testpoint_relayd_thread_listener(void); int __testpoint_relayd_thread_listener(void) { const char *var = "LTTNG_RELAYD_THREAD_LISTENER_EXIT"; @@ -219,6 +235,7 @@ int __testpoint_relayd_thread_listener(void) return 0; } +int __testpoint_relayd_thread_live_dispatcher(void); int __testpoint_relayd_thread_live_dispatcher(void) { const char *var = "LTTNG_RELAYD_THREAD_LIVE_DISPATCHER_EXIT"; @@ -230,6 +247,7 @@ int __testpoint_relayd_thread_live_dispatcher(void) return 0; } +int __testpoint_relayd_thread_live_worker(void); int __testpoint_relayd_thread_live_worker(void) { const char *var = "LTTNG_RELAYD_THREAD_LIVE_WORKER_EXIT"; @@ -242,6 +260,7 @@ int __testpoint_relayd_thread_live_worker(void) return 0; } +int __testpoint_relayd_thread_live_listener(void); int __testpoint_relayd_thread_live_listener(void) { const char *var = "LTTNG_RELAYD_THREAD_LIVE_LISTENER_EXIT"; diff --git a/tests/regression/tools/health/health_fail.c b/tests/regression/tools/health/health_fail.c index b3ebcedea..ca2bc3485 100644 --- a/tests/regression/tools/health/health_fail.c +++ b/tests/regression/tools/health/health_fail.c @@ -30,6 +30,7 @@ int check_env_var(const char *env) /* Session daemon */ +int __testpoint_sessiond_thread_manage_clients(void); int __testpoint_sessiond_thread_manage_clients(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CLIENTS_TP_FAIL"; @@ -41,6 +42,7 @@ int __testpoint_sessiond_thread_manage_clients(void) return 0; } +int __testpoint_sessiond_thread_registration_apps(void); int __testpoint_sessiond_thread_registration_apps(void) { const char *var = "LTTNG_SESSIOND_THREAD_REG_APPS_TP_FAIL"; @@ -52,6 +54,7 @@ int __testpoint_sessiond_thread_registration_apps(void) return 0; } +int __testpoint_sessiond_thread_manage_apps(void); int __testpoint_sessiond_thread_manage_apps(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_APPS_TP_FAIL"; @@ -63,6 +66,7 @@ int __testpoint_sessiond_thread_manage_apps(void) return 0; } +int __testpoint_sessiond_thread_manage_kernel(void); int __testpoint_sessiond_thread_manage_kernel(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_KERNEL_TP_FAIL"; @@ -74,6 +78,7 @@ int __testpoint_sessiond_thread_manage_kernel(void) return 0; } +int __testpoint_sessiond_thread_manage_consumer(void); int __testpoint_sessiond_thread_manage_consumer(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CONSUMER_TP_FAIL"; @@ -85,6 +90,7 @@ int __testpoint_sessiond_thread_manage_consumer(void) return 0; } +int __testpoint_sessiond_thread_ht_cleanup(void); int __testpoint_sessiond_thread_ht_cleanup(void) { const char *var = "LTTNG_SESSIOND_THREAD_HT_CLEANUP_TP_FAIL"; @@ -96,6 +102,7 @@ int __testpoint_sessiond_thread_ht_cleanup(void) return 0; } +int __testpoint_sessiond_thread_app_manage_notify(void); int __testpoint_sessiond_thread_app_manage_notify(void) { const char *var = "LTTNG_SESSIOND_THREAD_APP_MANAGE_NOTIFY_TP_FAIL"; @@ -107,6 +114,7 @@ int __testpoint_sessiond_thread_app_manage_notify(void) return 0; } +int __testpoint_sessiond_thread_app_reg_dispatch(void); int __testpoint_sessiond_thread_app_reg_dispatch(void) { const char *var = "LTTNG_SESSIOND_THREAD_APP_REG_DISPATCH_TP_FAIL"; @@ -120,6 +128,7 @@ int __testpoint_sessiond_thread_app_reg_dispatch(void) /* Consumer daemon */ +int __testpoint_consumerd_thread_channel(void); int __testpoint_consumerd_thread_channel(void) { const char *var = "LTTNG_CONSUMERD_THREAD_CHANNEL_TP_FAIL"; @@ -131,6 +140,7 @@ int __testpoint_consumerd_thread_channel(void) return 0; } +int __testpoint_consumerd_thread_metadata(void); int __testpoint_consumerd_thread_metadata(void) { const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_TP_FAIL"; @@ -142,6 +152,7 @@ int __testpoint_consumerd_thread_metadata(void) return 0; } +int __testpoint_consumerd_thread_data(void); int __testpoint_consumerd_thread_data(void) { const char *var = "LTTNG_CONSUMERD_THREAD_DATA_TP_FAIL"; @@ -153,6 +164,7 @@ int __testpoint_consumerd_thread_data(void) return 0; } +int __testpoint_consumerd_thread_sessiond(void); int __testpoint_consumerd_thread_sessiond(void) { const char *var = "LTTNG_CONSUMERD_THREAD_SESSIOND_TP_FAIL"; @@ -164,6 +176,7 @@ int __testpoint_consumerd_thread_sessiond(void) return 0; } +int __testpoint_consumerd_thread_metadata_timer(void); int __testpoint_consumerd_thread_metadata_timer(void) { const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_TIMER_TP_FAIL"; @@ -177,6 +190,7 @@ int __testpoint_consumerd_thread_metadata_timer(void) /* Relay daemon */ +int __testpoint_relayd_thread_dispatcher(void); int __testpoint_relayd_thread_dispatcher(void) { const char *var = "LTTNG_RELAYD_THREAD_DISPATCHER_TP_FAIL"; @@ -188,6 +202,7 @@ int __testpoint_relayd_thread_dispatcher(void) return 0; } +int __testpoint_relayd_thread_worker(void); int __testpoint_relayd_thread_worker(void) { const char *var = "LTTNG_RELAYD_THREAD_WORKER_TP_FAIL"; @@ -199,6 +214,7 @@ int __testpoint_relayd_thread_worker(void) return 0; } +int __testpoint_relayd_thread_listener(void); int __testpoint_relayd_thread_listener(void) { const char *var = "LTTNG_RELAYD_THREAD_LISTENER_TP_FAIL"; @@ -210,6 +226,7 @@ int __testpoint_relayd_thread_listener(void) return 0; } +int __testpoint_relayd_thread_live_dispatcher(void); int __testpoint_relayd_thread_live_dispatcher(void) { const char *var = "LTTNG_RELAYD_THREAD_LIVE_DISPATCHER_TP_FAIL"; @@ -221,6 +238,7 @@ int __testpoint_relayd_thread_live_dispatcher(void) return 0; } +int __testpoint_relayd_thread_live_worker(void); int __testpoint_relayd_thread_live_worker(void) { const char *var = "LTTNG_RELAYD_THREAD_LIVE_WORKER_TP_FAIL"; @@ -232,6 +250,7 @@ int __testpoint_relayd_thread_live_worker(void) return 0; } +int __testpoint_relayd_thread_live_listener(void); int __testpoint_relayd_thread_live_listener(void) { const char *var = "LTTNG_RELAYD_THREAD_LIVE_LISTENER_TP_FAIL"; diff --git a/tests/regression/tools/health/health_stall.c b/tests/regression/tools/health/health_stall.c index 4439b7369..1c0d6dc77 100644 --- a/tests/regression/tools/health/health_stall.c +++ b/tests/regression/tools/health/health_stall.c @@ -43,6 +43,7 @@ void do_stall(void) /* Session daemon */ +int __testpoint_sessiond_thread_manage_clients(void); int __testpoint_sessiond_thread_manage_clients(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CLIENTS_STALL"; @@ -54,6 +55,7 @@ int __testpoint_sessiond_thread_manage_clients(void) return 0; } +int __testpoint_sessiond_thread_registration_apps(void); int __testpoint_sessiond_thread_registration_apps(void) { const char *var = "LTTNG_SESSIOND_THREAD_REG_APPS_STALL"; @@ -65,6 +67,7 @@ int __testpoint_sessiond_thread_registration_apps(void) return 0; } +int __testpoint_sessiond_thread_manage_apps(void); int __testpoint_sessiond_thread_manage_apps(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_APPS_STALL"; @@ -76,6 +79,7 @@ int __testpoint_sessiond_thread_manage_apps(void) return 0; } +int __testpoint_sessiond_thread_manage_kernel(void); int __testpoint_sessiond_thread_manage_kernel(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_KERNEL_STALL"; @@ -87,6 +91,7 @@ int __testpoint_sessiond_thread_manage_kernel(void) return 0; } +int __testpoint_sessiond_thread_manage_consumer(void); int __testpoint_sessiond_thread_manage_consumer(void) { const char *var = "LTTNG_SESSIOND_THREAD_MANAGE_CONSUMER_STALL"; @@ -98,6 +103,7 @@ int __testpoint_sessiond_thread_manage_consumer(void) return 0; } +int __testpoint_sessiond_thread_ht_cleanup(void); int __testpoint_sessiond_thread_ht_cleanup(void) { const char *var = "LTTNG_SESSIOND_THREAD_HT_CLEANUP_STALL"; @@ -109,6 +115,7 @@ int __testpoint_sessiond_thread_ht_cleanup(void) return 0; } +int __testpoint_sessiond_thread_app_manage_notify(void); int __testpoint_sessiond_thread_app_manage_notify(void) { const char *var = "LTTNG_SESSIOND_THREAD_APP_MANAGE_NOTIFY_STALL"; @@ -120,6 +127,7 @@ int __testpoint_sessiond_thread_app_manage_notify(void) return 0; } +int __testpoint_sessiond_thread_app_reg_dispatch(void); int __testpoint_sessiond_thread_app_reg_dispatch(void) { const char *var = "LTTNG_SESSIOND_THREAD_APP_REG_DISPATCH_STALL"; @@ -133,6 +141,7 @@ int __testpoint_sessiond_thread_app_reg_dispatch(void) /* Consumer daemon */ +int __testpoint_consumerd_thread_channel(void); int __testpoint_consumerd_thread_channel(void) { const char *var = "LTTNG_CONSUMERD_THREAD_CHANNEL_STALL"; @@ -144,6 +153,7 @@ int __testpoint_consumerd_thread_channel(void) return 0; } +int __testpoint_consumerd_thread_metadata(void); int __testpoint_consumerd_thread_metadata(void) { const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_STALL"; @@ -155,6 +165,7 @@ int __testpoint_consumerd_thread_metadata(void) return 0; } +int __testpoint_consumerd_thread_data(void); int __testpoint_consumerd_thread_data(void) { const char *var = "LTTNG_CONSUMERD_THREAD_DATA_STALL"; @@ -166,6 +177,7 @@ int __testpoint_consumerd_thread_data(void) return 0; } +int __testpoint_consumerd_thread_sessiond(void); int __testpoint_consumerd_thread_sessiond(void) { const char *var = "LTTNG_CONSUMERD_THREAD_SESSIOND_STALL"; @@ -177,6 +189,7 @@ int __testpoint_consumerd_thread_sessiond(void) return 0; } +int __testpoint_consumerd_thread_metadata_timer(void); int __testpoint_consumerd_thread_metadata_timer(void) { const char *var = "LTTNG_CONSUMERD_THREAD_METADATA_TIMER_STALL"; @@ -190,6 +203,7 @@ int __testpoint_consumerd_thread_metadata_timer(void) /* Relay daemon */ +int __testpoint_relayd_thread_dispatcher(void); int __testpoint_relayd_thread_dispatcher(void) { const char *var = "LTTNG_RELAYD_THREAD_DISPATCHER_STALL"; @@ -201,6 +215,7 @@ int __testpoint_relayd_thread_dispatcher(void) return 0; } +int __testpoint_relayd_thread_worker(void); int __testpoint_relayd_thread_worker(void) { const char *var = "LTTNG_RELAYD_THREAD_WORKER_STALL"; @@ -212,6 +227,7 @@ int __testpoint_relayd_thread_worker(void) return 0; } +int __testpoint_relayd_thread_listener(void); int __testpoint_relayd_thread_listener(void) { const char *var = "LTTNG_RELAYD_THREAD_LISTENER_STALL"; @@ -223,6 +239,7 @@ int __testpoint_relayd_thread_listener(void) return 0; } +int __testpoint_relayd_thread_live_dispatcher(void); int __testpoint_relayd_thread_live_dispatcher(void) { const char *var = "LTTNG_RELAYD_THREAD_LIVE_DISPATCHER_STALL"; @@ -234,6 +251,7 @@ int __testpoint_relayd_thread_live_dispatcher(void) return 0; } +int __testpoint_relayd_thread_live_worker(void); int __testpoint_relayd_thread_live_worker(void) { const char *var = "LTTNG_RELAYD_THREAD_LIVE_WORKER_STALL"; @@ -245,6 +263,7 @@ int __testpoint_relayd_thread_live_worker(void) return 0; } +int __testpoint_relayd_thread_live_listener(void); int __testpoint_relayd_thread_live_listener(void) { const char *var = "LTTNG_RELAYD_THREAD_LIVE_LISTENER_STALL"; diff --git a/tests/regression/tools/live/live_test.c b/tests/regression/tools/live/live_test.c index ee75c04f7..5d10d228b 100644 --- a/tests/regression/tools/live/live_test.c +++ b/tests/regression/tools/live/live_test.c @@ -97,7 +97,7 @@ ssize_t lttng_live_send(int fd, const void *buf, size_t len) } static -int connect_viewer(char *hostname) +int connect_viewer(const char *hostname) { struct hostent *host; struct sockaddr_in server_addr; @@ -138,7 +138,7 @@ end: return ret; } -int establish_connection(void) +static int establish_connection(void) { struct lttng_viewer_cmd cmd; struct lttng_viewer_connect connect; @@ -182,7 +182,7 @@ error: /* * Returns the number of sessions, should be 1 during the unit test. */ -int list_sessions(uint64_t *session_id) +static int list_sessions(uint64_t *session_id) { struct lttng_viewer_cmd cmd; struct lttng_viewer_list_sessions list; @@ -229,7 +229,7 @@ error: return -1; } -int create_viewer_session(void) +static int create_viewer_session(void) { struct lttng_viewer_cmd cmd; struct lttng_viewer_create_session_response resp; @@ -267,7 +267,7 @@ error: return -1; } -int attach_session(uint64_t id) +static int attach_session(uint64_t id) { struct lttng_viewer_cmd cmd; struct lttng_viewer_attach_session_request rq; @@ -356,7 +356,7 @@ error: return -1; } -int get_metadata(void) +static int get_metadata(void) { struct lttng_viewer_cmd cmd; struct lttng_viewer_get_metadata rq; @@ -451,7 +451,7 @@ error: return -1; } -int get_next_index(void) +static int get_next_index(void) { struct lttng_viewer_cmd cmd; struct lttng_viewer_get_next_index rq; @@ -616,7 +616,7 @@ error: return -1; } -int detach_viewer_session(uint64_t id) +static int detach_viewer_session(uint64_t id) { struct lttng_viewer_cmd cmd; struct lttng_viewer_detach_session_response resp; diff --git a/tests/regression/tools/notification/Makefile.am b/tests/regression/tools/notification/Makefile.am index 3430f450a..beb6cee6a 100644 --- a/tests/regression/tools/notification/Makefile.am +++ b/tests/regression/tools/notification/Makefile.am @@ -1,8 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_srcdir)/tests -I$(top_srcdir)/tests/utils/ -I$(srcdir) -AM_LDFLAGS = - +AM_CFLAGS += -I$(top_srcdir)/tests/utils LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la LIB_LTTNG_CTL = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la @@ -12,7 +10,7 @@ noinst_PROGRAMS = base_client notification rotation if NO_SHARED CLEANFILES = libpause_consumer.so libpause_consumer.so.debug -EXTRA_DIST = test_notification_ust test_notification_kernel test_notification_multi_app base_client.c notification.c consumer_testpoints.c +EXTRA_DIST = test_notification_ust_error test_notification_ust_buffer_usage test_notification_ust_event_rule_condition_exclusion test_notification_kernel_error test_notification_kernel_buffer_usage test_notification_kernel_instrumentation test_notification_kernel_syscall test_notification_kernel_userspace_probe test_notification_multi_app base_client.c notification.c consumer_testpoints.c else @@ -38,8 +36,8 @@ notification_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm rotation_SOURCES = rotation.c rotation_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm -noinst_SCRIPTS = test_notification_ust test_notification_kernel test_notification_multi_app test_rotation -EXTRA_DIST = test_notification_ust test_notification_kernel test_notification_multi_app test_rotation +noinst_SCRIPTS = test_notification_ust_error test_notification_ust_buffer_usage test_notification_ust_event_rule_condition_exclusion test_notification_kernel_error test_notification_kernel_buffer_usage test_notification_kernel_instrumentation test_notification_kernel_syscall test_notification_kernel_userspace_probe test_notification_multi_app test_rotation +EXTRA_DIST = test_notification_ust_error test_notification_ust_buffer_usage test_notification_ust_event_rule_condition_exclusion test_notification_kernel_error test_notification_kernel_buffer_usage test_notification_kernel_instrumentation test_notification_kernel_syscall test_notification_kernel_userspace_probe test_notification_multi_app test_rotation all-local: diff --git a/tests/regression/tools/notification/base_client.c b/tests/regression/tools/notification/base_client.c index fcef66c21..18304b729 100644 --- a/tests/regression/tools/notification/base_client.c +++ b/tests/regression/tools/notification/base_client.c @@ -42,7 +42,8 @@ int handle_condition( const struct lttng_condition *condition, const struct lttng_evaluation *condition_evaluation); -int parse_arguments(char **argv) { +static int parse_arguments(char **argv) +{ const char *domain_type_string = NULL; const char *buffer_usage_type_string = NULL; const char *buffer_usage_threshold_type = NULL; diff --git a/tests/regression/tools/notification/consumer_testpoints.c b/tests/regression/tools/notification/consumer_testpoints.c index c43721606..0d1edc5f9 100644 --- a/tests/regression/tools/notification/consumer_testpoints.c +++ b/tests/regression/tools/notification/consumer_testpoints.c @@ -47,6 +47,7 @@ void __attribute__((destructor)) pause_pipe_fini(void) * thread to create a named pipe/FIFO which a test application can use to either * pause or resume the consumption of data. */ +int __testpoint_consumerd_thread_data(void); int __testpoint_consumerd_thread_data(void) { int ret = 0; @@ -106,6 +107,7 @@ end: return ret; } +int __testpoint_consumerd_thread_data_poll(void); int __testpoint_consumerd_thread_data_poll(void) { int ret = 0; diff --git a/tests/regression/tools/notification/notification.c b/tests/regression/tools/notification/notification.c index f076bbd0e..436519c69 100644 --- a/tests/regression/tools/notification/notification.c +++ b/tests/regression/tools/notification/notification.c @@ -29,21 +29,25 @@ #include #include #include +#include #include #include +#include +#include +#include +#include #include +#include #include #include #include -#include +#include #include -#define NUM_TESTS 104 - int nb_args = 0; int named_pipe_args_start = 0; -pid_t app_pid = -1; +pid_t app_pid = 0; const char *app_state_file = NULL; static @@ -87,7 +91,7 @@ void wait_on_file(const char *path, bool file_exist) } } -int write_pipe(const char *path, uint8_t data) +static int write_pipe(const char *path, uint8_t data) { int ret = 0; int fd = 0; @@ -118,7 +122,7 @@ end: return ret; } -int stop_consumer(const char **argv) +static int stop_consumer(const char **argv) { int ret = 0, i; @@ -128,7 +132,7 @@ int stop_consumer(const char **argv) return ret; } -int resume_consumer(const char **argv) +static int resume_consumer(const char **argv) { int ret = 0, i; @@ -138,7 +142,7 @@ int resume_consumer(const char **argv) return ret; } -int suspend_application() +static int suspend_application(void) { int ret; struct stat buf; @@ -152,6 +156,8 @@ int suspend_application() /* * Send SIGUSR1 to application instructing it to bypass tracepoint. */ + assert(app_pid > 1); + ret = kill(app_pid, SIGUSR1); if (ret) { fail("SIGUSR1 failed. errno %d", errno); @@ -166,7 +172,7 @@ error: } -int resume_application() +static int resume_application() { int ret; struct stat buf; @@ -181,6 +187,8 @@ int resume_application() goto error; } + assert(app_pid > 1); + ret = kill(app_pid, SIGUSR1); if (ret) { fail("SIGUSR1 failed. errno %d", errno); @@ -196,7 +204,7 @@ error: } -void test_triggers_buffer_usage_condition(const char *session_name, +static void test_triggers_buffer_usage_condition(const char *session_name, const char *channel_name, enum lttng_domain_type domain_type, enum lttng_condition_type condition_type) @@ -315,7 +323,7 @@ void test_triggers_buffer_usage_condition(const char *session_name, assert("Logic error for test vector generation"); } - loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s", + loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s", session_name_set ? "set" : "unset", channel_name_set ? "set" : "unset", threshold_ratio_set ? "set" : "unset", @@ -389,46 +397,90 @@ void wait_data_pending(const char *session_name) } while (ret != 0); } -void test_notification_channel(const char *session_name, const char *channel_name, const enum lttng_domain_type domain_type, const char **argv) +static int setup_buffer_usage_condition(struct lttng_condition *condition, + const char *condition_name, + const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type) { - int ret = 0; enum lttng_condition_status condition_status; - enum lttng_notification_channel_status nc_status; + int ret = 0; - struct lttng_action *action = NULL; - struct lttng_notification *notification = NULL; - struct lttng_notification_channel *notification_channel = NULL; - struct lttng_trigger *trigger = NULL; + condition_status = lttng_condition_buffer_usage_set_session_name( + condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Error setting session name on %s creation", condition_name); + ret = -1; + goto end; + } - struct lttng_condition *low_condition = NULL; - struct lttng_condition *high_condition = NULL; - struct lttng_condition *dummy_invalid_condition = NULL; - struct lttng_condition *dummy_condition = NULL; + condition_status = lttng_condition_buffer_usage_set_channel_name( + condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Error setting channel name on %s creation", condition_name); + ret = -1; + goto end; + } - double low_ratio = 0.0; - double high_ratio = 0.99; + condition_status = lttng_condition_buffer_usage_set_domain_type( + condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Error setting domain type on %s creation", condition_name); + ret = -1; + goto end; + } - /* Set-up */ - action = lttng_action_notify_create(); - if (!action) { - fail("Setup error on action creation"); +end: + return ret; +} + +static void test_invalid_channel_subscription( + const enum lttng_domain_type domain_type) +{ + enum lttng_condition_status condition_status; + enum lttng_notification_channel_status nc_status; + struct lttng_condition *dummy_condition = NULL; + struct lttng_condition *dummy_invalid_condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + int ret = 0; + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { goto end; } - /* Create a dummy, empty condition for later test */ + /* + * Create a dummy, empty (thus invalid) condition to test error paths. + */ dummy_invalid_condition = lttng_condition_buffer_usage_low_create(); if (!dummy_invalid_condition) { fail("Setup error on condition creation"); goto end; } + /* + * Test subscription and unsubscription of an invalid condition to/from + * a channel. + */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Subscribing to an invalid condition"); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Unsubscribing from an invalid condition"); + /* Create a valid dummy condition with a ratio of 0.5 */ dummy_condition = lttng_condition_buffer_usage_low_create(); if (!dummy_condition) { fail("Setup error on dummy_condition creation"); goto end; - } + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( dummy_condition, 0.5); if (condition_status != LTTNG_CONDITION_STATUS_OK) { @@ -436,168 +488,247 @@ void test_notification_channel(const char *session_name, const char *channel_nam goto end; } - condition_status = lttng_condition_buffer_usage_set_session_name( - dummy_condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on dummy_condition creation"); - goto end; - } - condition_status = lttng_condition_buffer_usage_set_channel_name( - dummy_condition, channel_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on dummy_condition creation"); - goto end; - } - condition_status = lttng_condition_buffer_usage_set_domain_type( - dummy_condition, domain_type); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on dummy_condition creation"); + ret = setup_buffer_usage_condition(dummy_condition, "dummy_condition", + "dummy_session", "dummy_channel", domain_type); + if (ret) { + fail("Setup error on dummy condition creation"); goto end; } - /* Register a low condition with a ratio */ - low_condition = lttng_condition_buffer_usage_low_create(); - if (!low_condition) { - fail("Setup error on low_condition creation"); - goto end; - } - condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - low_condition, low_ratio); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on low_condition creation"); - goto end; - } + /* + * Test subscription and unsubscription to/from a channel with invalid + * parameters. + */ + nc_status = lttng_notification_channel_subscribe(NULL, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Notification channel subscription is invalid: NULL, NULL"); - condition_status = lttng_condition_buffer_usage_set_session_name( - low_condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on low_condition creation"); - goto end; - } - condition_status = lttng_condition_buffer_usage_set_channel_name( - low_condition, channel_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on low_condition creation"); - goto end; + nc_status = lttng_notification_channel_subscribe( + notification_channel, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Notification channel subscription is invalid: NON-NULL, NULL"); + + nc_status = lttng_notification_channel_subscribe(NULL, dummy_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, + "Notification channel subscription is invalid: NULL, NON-NULL"); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, dummy_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, + "Unsubscribing from a valid unknown condition"); + +end: + lttng_notification_channel_destroy(notification_channel); + lttng_condition_destroy(dummy_invalid_condition); + lttng_condition_destroy(dummy_condition); + return; +} + +enum buffer_usage_type { + BUFFER_USAGE_TYPE_LOW, + BUFFER_USAGE_TYPE_HIGH, +}; + +static int register_buffer_usage_notify_trigger(const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type, + enum buffer_usage_type buffer_usage_type, + double ratio, + struct lttng_condition **condition, + struct lttng_action **action, + struct lttng_trigger **trigger) +{ + enum lttng_condition_status condition_status; + struct lttng_action *tmp_action = NULL; + struct lttng_condition *tmp_condition = NULL; + struct lttng_trigger *tmp_trigger = NULL; + int ret = 0; + + /* Set-up */ + tmp_action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + ret = -1; + goto error; } - condition_status = lttng_condition_buffer_usage_set_domain_type( - low_condition, domain_type); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on low_condition creation"); - goto end; + if (buffer_usage_type == BUFFER_USAGE_TYPE_LOW) { + tmp_condition = lttng_condition_buffer_usage_low_create(); + } else { + tmp_condition = lttng_condition_buffer_usage_high_create(); } - /* Register a high condition with a ratio */ - high_condition = lttng_condition_buffer_usage_high_create(); - if (!high_condition) { - fail("Setup error on high_condition creation"); - goto end; + if (!tmp_condition) { + fail("Setup error on condition creation"); + ret = -1; + goto error; } + /* Set the buffer usage threashold */ condition_status = lttng_condition_buffer_usage_set_threshold_ratio( - high_condition, high_ratio); + tmp_condition, ratio); if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on high_condition creation"); - goto end; + fail("Setup error on condition creation"); + ret = -1; + goto error; } - condition_status = lttng_condition_buffer_usage_set_session_name( - high_condition, session_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on high_condition creation"); - goto end; + ret = setup_buffer_usage_condition(tmp_condition, "condition_name", + session_name, channel_name, domain_type); + if (ret) { + fail("Setup error on condition creation"); + ret = -1; + goto error; } - condition_status = lttng_condition_buffer_usage_set_channel_name( - high_condition, channel_name); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on high_condition creation"); - goto end; + + /* Register the triggers for condition */ + tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action); + if (!tmp_trigger) { + fail("Setup error on trigger creation"); + ret = -1; + goto error; } - condition_status = lttng_condition_buffer_usage_set_domain_type( - high_condition, domain_type); - if (condition_status != LTTNG_CONDITION_STATUS_OK) { - fail("Setup error on high_condition creation"); - goto end; + + ret = lttng_register_trigger(tmp_trigger); + if (ret) { + fail("Setup error on trigger registration"); + ret = -1; + goto error; } - /* Register the triggers for low and high condition */ - trigger = lttng_trigger_create(low_condition, action); - if (!trigger) { - fail("Setup error on low trigger creation"); + *condition = tmp_condition; + *trigger = tmp_trigger; + *action = tmp_action; + goto end; + +error: + lttng_action_destroy(tmp_action); + lttng_condition_destroy(tmp_condition); + lttng_trigger_destroy(tmp_trigger); + +end: + return ret; +} + +static void test_subscription_twice(const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type) +{ + int ret = 0; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *action = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + + struct lttng_condition *condition = NULL; + + ret = register_buffer_usage_notify_trigger(session_name, channel_name, + domain_type, BUFFER_USAGE_TYPE_LOW, 0.99, &condition, + &action, &trigger); + if (ret) { + fail("Setup error on trigger registration"); goto end; } - ret = lttng_register_trigger(trigger); - if (ret) { - fail("Setup error on low trigger registration"); + /* Begin testing. */ + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { goto end; } + /* Subscribe a valid condition. */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to condition"); + + /* Subscribing again should fail. */ + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, + "Subscribe to a condition for which subscription was already done"); + +end: + lttng_unregister_trigger(trigger); lttng_trigger_destroy(trigger); - trigger = NULL; + lttng_notification_channel_destroy(notification_channel); + lttng_action_destroy(action); + lttng_condition_destroy(condition); +} - trigger = lttng_trigger_create(high_condition, action); - if (!trigger) { - fail("Setup error on high trigger creation"); +static void test_buffer_usage_notification_channel(const char *session_name, + const char *channel_name, + const enum lttng_domain_type domain_type, + const char **argv) +{ + int ret = 0; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *low_action = NULL; + struct lttng_action *high_action = NULL; + struct lttng_notification *notification = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *low_trigger = NULL; + struct lttng_trigger *high_trigger = NULL; + + struct lttng_condition *low_condition = NULL; + struct lttng_condition *high_condition = NULL; + + double low_ratio = 0.0; + double high_ratio = 0.99; + + ret = register_buffer_usage_notify_trigger(session_name, channel_name, + domain_type, BUFFER_USAGE_TYPE_LOW, low_ratio, + &low_condition, &low_action, &low_trigger); + if (ret) { + fail("Setup error on low trigger registration"); goto end; } - ret = lttng_register_trigger(trigger); + ret = register_buffer_usage_notify_trigger(session_name, channel_name, + domain_type, BUFFER_USAGE_TYPE_HIGH, high_ratio, + &high_condition, &high_action, &high_trigger); if (ret) { fail("Setup error on high trigger registration"); goto end; } /* Begin testing */ - notification_channel = lttng_notification_channel_create(lttng_session_daemon_notification_endpoint); + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); ok(notification_channel, "Notification channel object creation"); if (!notification_channel) { goto end; } - /* Basic error path check */ - nc_status = lttng_notification_channel_subscribe(NULL, NULL); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NULL"); - - nc_status = lttng_notification_channel_subscribe(notification_channel, NULL); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NON-NULL, NULL"); - - nc_status = lttng_notification_channel_subscribe(NULL, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NON-NULL"); - - nc_status = lttng_notification_channel_subscribe(notification_channel, dummy_invalid_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Subscribing to an invalid condition"); - - nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_invalid_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Unsubscribing from an invalid condition"); - - nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, "Unsubscribing from a valid unknown condition"); - /* Subscribe a valid low condition */ - nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition"); + nc_status = lttng_notification_channel_subscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to low condition"); /* Subscribe a valid high condition */ - nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition"); + nc_status = lttng_notification_channel_subscribe( + notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to high condition"); - nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); - - nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); + resume_application(); /* Wait for notification to happen */ stop_consumer(argv); lttng_start_tracing(session_name); /* Wait for high notification */ - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK - && notification - && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, "High notification received after intermediary communication"); lttng_notification_destroy(notification); notification = NULL; @@ -612,16 +743,22 @@ void test_notification_channel(const char *session_name, const char *channel_nam * waiting for consumption. */ - nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe with pending notification"); + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Unsubscribe with pending notification"); - nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "subscribe with pending notification"); + nc_status = lttng_notification_channel_subscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe with pending notification"); - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK - && notification - && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, "Low notification received after intermediary communication"); lttng_notification_destroy(notification); notification = NULL; @@ -631,9 +768,12 @@ void test_notification_channel(const char *session_name, const char *channel_nam resume_application(); lttng_start_tracing(session_name); - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, "High notification received after intermediary communication"); lttng_notification_destroy(notification); notification = NULL; @@ -643,9 +783,12 @@ void test_notification_channel(const char *session_name, const char *channel_nam resume_consumer(argv); wait_data_pending(session_name); - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, "Low notification received after re-subscription"); lttng_notification_destroy(notification); notification = NULL; @@ -655,77 +798,892 @@ void test_notification_channel(const char *session_name, const char *channel_nam /* Stop consumer to force a high notification */ lttng_start_tracing(session_name); - nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + nc_status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && - lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + lttng_condition_get_type(lttng_notification_get_condition( + notification)) == + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, "High notification"); lttng_notification_destroy(notification); notification = NULL; - /* Resume consumer to allow event consumption */ suspend_application(); + + /* Resume consumer to allow event consumption */ lttng_stop_tracing_no_wait(session_name); resume_consumer(argv); wait_data_pending(session_name); - nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe low condition with pending notification"); - nc_status = lttng_notification_channel_unsubscribe(notification_channel, high_condition); - ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe high condition with pending notification"); + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Unsubscribe low condition with pending notification"); + + nc_status = lttng_notification_channel_unsubscribe( + notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Unsubscribe high condition with pending notification"); end: lttng_notification_channel_destroy(notification_channel); - lttng_trigger_destroy(trigger); - lttng_action_destroy(action); + lttng_trigger_destroy(low_trigger); + lttng_trigger_destroy(high_trigger); + lttng_action_destroy(low_action); + lttng_action_destroy(high_action); lttng_condition_destroy(low_condition); lttng_condition_destroy(high_condition); - lttng_condition_destroy(dummy_invalid_condition); - lttng_condition_destroy(dummy_condition); } -int main(int argc, const char *argv[]) +static void create_tracepoint_event_rule_trigger(const char *event_pattern, + const char *trigger_name, + const char *filter, + unsigned int exclusion_count, + const char **exclusions, + enum lttng_domain_type domain_type, + struct lttng_condition **condition, + struct lttng_trigger **trigger) { - const char *session_name = NULL; - const char *channel_name = NULL; - const char *domain_type_string = NULL; - enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; - plan_tests(NUM_TESTS); + struct lttng_action *tmp_action = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_condition *tmp_condition = NULL; + struct lttng_trigger *tmp_trigger = NULL; + int ret; + + assert(event_pattern); + assert(trigger_name); + assert(condition); + assert(trigger); + + event_rule = lttng_event_rule_tracepoint_create(domain_type); + ok(event_rule, "Tracepoint event rule object creation"); + + event_rule_status = lttng_event_rule_tracepoint_set_pattern( + event_rule, event_pattern); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting tracepoint event rule pattern: %s", + event_pattern); + + if (filter) { + event_rule_status = lttng_event_rule_tracepoint_set_filter( + event_rule, filter); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting tracepoint event rule filter: %s", + filter); + } - /* Argument 6 and upward are named pipe location for consumerd control */ - named_pipe_args_start = 6; + if (exclusions) { + assert(domain_type == LTTNG_DOMAIN_UST); + assert(exclusion_count > 0); - if (argc < 7) { - fail("Missing parameter for tests to run %d", argc); - goto error; + event_rule_status = lttng_event_rule_tracepoint_set_exclusions( + event_rule, exclusion_count, exclusions); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting tracepoint event rule exclusions"); } - nb_args = argc; + tmp_condition = lttng_condition_event_rule_create(event_rule); + ok(tmp_condition, "Condition event rule object creation"); + /* Ownership was passed to condition */ + event_rule = NULL; - domain_type_string = argv[1]; - session_name = argv[2]; - channel_name = argv[3]; - app_pid = (pid_t) atoi(argv[4]); - app_state_file = argv[5]; + tmp_action = lttng_action_notify_create(); + ok(tmp_action, "Action event rule object creation"); - if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) { - domain_type = LTTNG_DOMAIN_UST; + tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action); + ok(tmp_trigger, "Trigger object creation %s", trigger_name); + + trigger_status = lttng_trigger_set_name(tmp_trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + + ret = lttng_register_trigger(tmp_trigger); + ok(ret == 0, "Trigger registration %s", trigger_name); + + *condition = tmp_condition; + *trigger = tmp_trigger; + + return; +} + +static char *get_next_notification_trigger_name( + struct lttng_notification_channel *notification_channel) +{ + struct lttng_notification *notification; + enum lttng_notification_channel_status status; + const struct lttng_evaluation *notification_evaluation; + char *trigger_name = NULL; + const char *name; + + /* Receive the next notification. */ + status = lttng_notification_channel_get_next_notification( + notification_channel, ¬ification); + + switch (status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + default: + /* Unhandled conditions / errors. */ + fail("error: Unknown notification channel status\n"); + goto end; } - if (!strcmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) { - domain_type = LTTNG_DOMAIN_KERNEL; + + notification_evaluation = + lttng_notification_get_evaluation(notification); + + switch (lttng_evaluation_get_type(notification_evaluation)) { + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + lttng_evaluation_event_rule_get_trigger_name( + notification_evaluation, &name); + + trigger_name = strdup(name); + break; + default: + fail("error: Wrong notification evaluation type \n"); + break; } - if (domain_type == LTTNG_DOMAIN_NONE) { - fail("Unknown domain type"); - goto error; + + lttng_notification_destroy(notification); + +end: + return trigger_name; +} + +static void test_tracepoint_event_rule_notification( + enum lttng_domain_type domain_type) +{ + int i; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *action = NULL; + struct lttng_condition *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "my_precious"; + const char *pattern; + + if (domain_type == LTTNG_DOMAIN_UST) { + pattern = "tp:tptest"; + } else { + pattern = "lttng_test_filter_event"; + } + + create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, + NULL, domain_type, &condition, &trigger); + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + /* Get 3 notifications */ + for (i = 0; i < 3; i++) { + char *name = get_next_notification_trigger_name( + notification_channel); + ok(strcmp(trigger_name, name) == 0, + "Received notification for the expected trigger name: %s", + trigger_name); + free(name); } - diag("Test trigger for domain %s with buffer_usage_low condition", domain_type_string); - test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); - diag("Test trigger for domain %s with buffer_usage_high condition", domain_type_string); - test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(condition); + return; +} + +static void test_tracepoint_event_rule_notification_filter( + enum lttng_domain_type domain_type) +{ + int i; + enum lttng_notification_channel_status nc_status; + + struct lttng_condition *ctrl_condition = NULL, *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL; + const char *ctrl_trigger_name = "control_trigger"; + const char *trigger_name = "trigger"; + const char *pattern; + int ctrl_count = 0, count = 0; + + if (domain_type == LTTNG_DOMAIN_UST) { + pattern = "tp:tptest"; + } else { + pattern = "lttng_test_filter_event"; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, + 0, NULL, domain_type, &ctrl_condition, &ctrl_trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, ctrl_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + /* + * Attach a filter expression to get notification only if the + * `intfield` is even. + */ + create_tracepoint_event_rule_trigger(pattern, trigger_name, + "(intfield & 1) == 0", 0, NULL, domain_type, &condition, + &trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + /* + * We registered 2 notifications triggers, one with a filter and one + * without (control). The one with a filter will only fired when the + * `intfield` is a multiple of 2. We should get two times as many + * control notifications as filter notifications. + */ + resume_application(); + + /* + * Get 3 notifications. We should get 1 for the regular trigger (with + * the filter) and 2 from the control trigger. This works whatever + * the order we receive the notifications. + */ + for (i = 0; i < 3; i++) { + char *name = get_next_notification_trigger_name( + notification_channel); + if (strcmp(ctrl_trigger_name, name) == 0) { + ctrl_count++; + } else if (strcmp(trigger_name, name) == 0) { + count++; + } + free(name); + } + ok(ctrl_count / 2 == count, + "Get twice as many control notif as of regular notif"); + + suspend_application(); + lttng_unregister_trigger(trigger); + lttng_unregister_trigger(ctrl_trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_trigger_destroy(ctrl_trigger); + lttng_condition_destroy(condition); + lttng_condition_destroy(ctrl_condition); + return; +} + +static void test_tracepoint_event_rule_notification_exclusion( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + struct lttng_condition *ctrl_condition = NULL, *condition = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL; + const char *ctrl_trigger_name = "control_exclusion_trigger"; + const char *trigger_name = "exclusion_trigger"; + const char *pattern = "tp:tptest*"; + const char *exclusions[4] = { + "tp:tptest2", "tp:tptest3", "tp:tptest4", "tp:tptest5"}; + int ctrl_count = 0, count = 0; + int i; + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL, + 0, NULL, domain_type, &ctrl_condition, &ctrl_trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, ctrl_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 4, + exclusions, domain_type, &condition, &trigger); + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + /* + * We registered 2 notifications triggers, one with an exclusion and + * one without (control). + * - The trigger with an exclusion will fire once every iteration. + * - The trigger without an exclusion will fire 5 times every + * iteration. + * + * We should get 5 times as many notifications from the control + * trigger. + */ + resume_application(); + + /* + * Get 6 notifications. We should get 1 for the regular trigger (with + * the exclusion) and 5 from the control trigger. This works whatever + * the order we receive the notifications. + */ + for (i = 0; i < 6; i++) { + char *name = get_next_notification_trigger_name( + notification_channel); + if (strcmp(ctrl_trigger_name, name) == 0) { + ctrl_count++; + } else if (strcmp(trigger_name, name) == 0) { + count++; + } + free(name); + } + ok(ctrl_count / 5 == count, + "Got 5 times as many control notif as of regular notif"); + + suspend_application(); + lttng_unregister_trigger(trigger); + lttng_unregister_trigger(ctrl_trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_trigger_destroy(ctrl_trigger); + lttng_condition_destroy(condition); + lttng_condition_destroy(ctrl_condition); + return; +} + +static void test_kprobe_event_rule_notification( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; + + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "kprobe_trigger"; + const char *symbol_name = "_do_fork"; + int i, ret; + + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_kprobe_create(); + ok(event_rule, "kprobe event rule object creation"); + + event_rule_status = lttng_event_rule_kprobe_set_source( + event_rule, symbol_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting kprobe event rule source: %s", symbol_name); + + event_rule_status = lttng_event_rule_kprobe_set_name( + event_rule, trigger_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting kprobe event rule name: %s", trigger_name); + + condition = lttng_condition_event_rule_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the triggers for condition */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Setup error on trigger creation"); + goto end; + } + + trigger_status = lttng_trigger_set_name(trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on trigger registration"); + goto end; + } + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < 3; i++) { + char *name = get_next_notification_trigger_name( + notification_channel); + ok(strcmp(trigger_name, name) == 0, + "Received notification for the expected trigger name: %s", + trigger_name); + free(name); + } + +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(condition); + return; +} + +static void test_uprobe_event_rule_notification( + enum lttng_domain_type domain_type, + const char *testapp_path, + const char *test_symbol_name) +{ + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; + + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_userspace_probe_location *probe_location = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = + NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "uprobe_trigger"; + int i, ret; + + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!lookup_method) { + fail("Setup error on userspace probe lookup method creation"); + goto end; + } + + probe_location = lttng_userspace_probe_location_function_create( + testapp_path, test_symbol_name, lookup_method); + if (!probe_location) { + fail("Setup error on userspace probe location creation"); + goto end; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_uprobe_create(); + ok(event_rule, "kprobe event rule object creation"); + + event_rule_status = lttng_event_rule_uprobe_set_location( + event_rule, probe_location); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting uprobe event rule location"); + + event_rule_status = lttng_event_rule_uprobe_set_name( + event_rule, trigger_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting uprobe event rule name: %s", trigger_name); + + condition = lttng_condition_event_rule_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the triggers for condition */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Setup error on trigger creation"); + goto end; + } + + trigger_status = lttng_trigger_set_name(trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on trigger registration"); + goto end; + } + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < 3; i++) { + char *name = get_next_notification_trigger_name( + notification_channel); + ok(strcmp(trigger_name, name) == 0, + "Received notification for the expected trigger name: %s", + trigger_name); + free(name); + } +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(condition); + return; +} + +static void test_syscall_event_rule_notification( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; + + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "syscall_trigger"; + const char *syscall_name = "openat"; + int i, ret; + + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_syscall_create(); + ok(event_rule, "syscall event rule object creation"); + + event_rule_status = lttng_event_rule_syscall_set_pattern( + event_rule, syscall_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting syscall event rule pattern: %s", syscall_name); + + condition = lttng_condition_event_rule_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the triggers for condition */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Setup error on trigger creation"); + goto end; + } + + trigger_status = lttng_trigger_set_name(trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on trigger registration"); + goto end; + } + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < 3; i++) { + char *name = get_next_notification_trigger_name( + notification_channel); + ok(strcmp(trigger_name, name) == 0, + "Received notification for the expected trigger name: %s", + trigger_name); + free(name); + } +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(condition); + return; +} + +static void test_syscall_event_rule_notification_filter( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + enum lttng_event_rule_status event_rule_status; + enum lttng_trigger_status trigger_status; + + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + struct lttng_action *action = NULL; + struct lttng_trigger *trigger = NULL; + const char *trigger_name = "syscall_trigger"; + const char *syscall_name = "openat"; + int i, ret; + + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + + event_rule = lttng_event_rule_syscall_create(); + ok(event_rule, "syscall event rule object creation"); + + event_rule_status = lttng_event_rule_syscall_set_pattern( + event_rule, syscall_name); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting syscall event rule pattern: %s", syscall_name); + + event_rule_status = lttng_event_rule_syscall_set_filter( + event_rule, "filename==\"/proc/cpuinfo\""); + ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, + "Setting syscall event rule pattern: %s", syscall_name); + + condition = lttng_condition_event_rule_create(event_rule); + ok(condition, "Condition event rule object creation"); + + /* Register the triggers for condition */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + fail("Setup error on trigger creation"); + goto end; + } + + trigger_status = lttng_trigger_set_name(trigger, trigger_name); + ok(trigger_status == LTTNG_TRIGGER_STATUS_OK, + "Setting name to trigger %s", trigger_name); + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on trigger registration"); + goto end; + } + + nc_status = lttng_notification_channel_subscribe( + notification_channel, condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, + "Subscribe to tracepoint event rule condition"); + + resume_application(); + + for (i = 0; i < 3; i++) { + char *name = get_next_notification_trigger_name( + notification_channel); + ok(strcmp(trigger_name, name) == 0, + "Received notification for the expected trigger name: %s", + trigger_name); + free(name); + } + +end: + suspend_application(); + lttng_unregister_trigger(trigger); + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + return; +} + +int main(int argc, const char *argv[]) +{ + int test_scenario; + const char *domain_type_string = NULL; + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + + if (argc < 5) { + fail("Missing test scenario, domain type, pid, or application state file argument(s)"); + goto error; + } + + test_scenario = atoi(argv[1]); + domain_type_string = argv[2]; + app_pid = (pid_t) atoi(argv[3]); + app_state_file = argv[4]; + + if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) { + domain_type = LTTNG_DOMAIN_UST; + } + if (!strcmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) { + domain_type = LTTNG_DOMAIN_KERNEL; + } + if (domain_type == LTTNG_DOMAIN_NONE) { + fail("Unknown domain type"); + goto error; + } + + /* + * Test cases are responsible for resuming the app when needed + * and making sure it's suspended when returning. + */ + suspend_application(); + + switch (test_scenario) { + case 1: + { + plan_tests(38); + + /* Test cases that need gen-ust-event testapp. */ + diag("Test basic notification error paths for domain %s", + domain_type_string); + test_invalid_channel_subscription(domain_type); + + diag("Test tracepoint event rule notifications for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification(domain_type); + + diag("Test tracepoint event rule notifications with filter for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_filter(domain_type); + break; + } + case 2: + { + const char *session_name, *channel_name; + /* Test cases that need a tracing session enabled. */ + plan_tests(99); + + /* + * Argument 7 and upward are named pipe location for consumerd + * control. + */ + named_pipe_args_start = 7; + + if (argc < 8) { + fail("Missing parameter for tests to run %d", argc); + goto error; + } + + nb_args = argc; + + session_name = argv[5]; + channel_name = argv[6]; + + test_subscription_twice(session_name, channel_name, + domain_type); + + diag("Test trigger for domain %s with buffer_usage_low condition", + domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, + domain_type, + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); + + diag("Test trigger for domain %s with buffer_usage_high condition", + domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, + domain_type, + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); + + diag("Test buffer usage notification channel api for domain %s", + domain_type_string); + test_buffer_usage_notification_channel(session_name, channel_name, + domain_type, argv); + break; + } + case 3: + { + /* + * Test cases that need a test app with more than one event + * type. + */ + plan_tests(19); + + /* + * At the moment, the only test case of this scenario is + * exclusion which is only supported by UST + */ + assert(domain_type == LTTNG_DOMAIN_UST); + diag("Test tracepoint event rule notifications with exclusion for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_exclusion(domain_type); + + break; + } + case 4: + { + plan_tests(10); + /* Test cases that need the kernel tracer. */ + assert(domain_type == LTTNG_DOMAIN_KERNEL); + + diag("Test kprobe event rule notifications for domain %s", + domain_type_string); + + test_kprobe_event_rule_notification(domain_type); + + break; + } + case 5: + { + plan_tests(19); + /* Test cases that need the kernel tracer. */ + assert(domain_type == LTTNG_DOMAIN_KERNEL); + + diag("Test syscall event rule notifications for domain %s", + domain_type_string); + + test_syscall_event_rule_notification(domain_type); + + diag("Test syscall filtering event rule notifications for domain %s", + domain_type_string); + + test_syscall_event_rule_notification_filter(domain_type); + + break; + } + case 6: + { + const char *testapp_path, *test_symbol_name; + + plan_tests(10); + + if (argc < 7) { + fail("Missing parameter for tests to run %d", argc); + goto error; + } + + testapp_path = argv[5]; + test_symbol_name = argv[6]; + /* Test cases that need the kernel tracer. */ + assert(domain_type == LTTNG_DOMAIN_KERNEL); + + diag("Test userspace-probe event rule notifications for domain %s", + domain_type_string); + + test_uprobe_event_rule_notification( + domain_type, testapp_path, test_symbol_name); + + break; + } + default: + abort(); + } - diag("Test notification channel api for domain %s", domain_type_string); - test_notification_channel(session_name, channel_name, domain_type, argv); error: return exit_status(); } diff --git a/tests/regression/tools/notification/test_notification_kernel_buffer_usage b/tests/regression/tools/notification/test_notification_kernel_buffer_usage new file mode 100755 index 000000000..36a238c5f --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_buffer_usage @@ -0,0 +1,126 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +SYSCALL_TESTAPP_NAME="gen-syscall-events" +SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME" + +USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary" +USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME" + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +TRACE_PATH=$(mktemp -d) +PAGE_SIZE=$(getconf PAGE_SIZE) + +DIR=$(readlink -f $TESTDIR) +NUM_TESTS=104 + +source $TESTDIR/utils/utils.sh + +function kernel_event_generator_toggle_state +{ + kernel_event_generator_suspended=$((kernel_event_generator_suspended==0)) +} + +function generate_filter_events +{ + /bin/echo -n "10" > /proc/lttng-test-filter-event 2> /dev/null +} + +function kernel_event_generator +{ + command_to_run=$1 + state_file=$2 + kernel_event_generator_suspended=0 + trap kernel_event_generator_toggle_state SIGUSR1 + + while (true); do + if [[ $kernel_event_generator_suspended -eq "1" ]]; then + touch $state_file + sleep 0.5 + else + if [[ -f $state_file ]]; then + rm $state_file 2> /dev/null + fi + $command_to_run + fi + done +} + +function test_buffer_usage_notification +{ + local consumerd_pipe=$1 + local event_name="lttng_test_filter_event" + + create_lttng_session_notap $SESSION_NAME $TRACE_PATH + + lttng_enable_kernel_channel_notap $SESSION_NAME $CHANNEL_NAME \ + --subbuf-size=$PAGE_SIZE + enable_kernel_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME + + kernel_event_generator generate_filter_events $TESTAPP_STATE_PATH & + APP_PID=$! + + $CURDIR/notification 2 LTTNG_DOMAIN_KERNEL $APP_PID $TESTAPP_STATE_PATH \ + $SESSION_NAME $CHANNEL_NAME ${consumerd_pipe[@]} + + destroy_lttng_session_notap $SESSION_NAME + + kill -9 $APP_PID + wait $APP_PID 2> /dev/null +} + +if [ "$(id -u)" == "0" ]; then + consumerd_pipe=() + + validate_lttng_modules_present + + # This is needed since the testpoint create a pipe with the consumer + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") + done + + modprobe lttng-test + + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + start_lttng_sessiond_notap + + test_buffer_usage_notification $consumerd_pipe + + stop_lttng_sessiond_notap + rmmod lttng-test + + rm -rf ${consumerd_pipe[@]} 2> /dev/null +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf $TRACE_PATH +rm -rf $TMPDIR diff --git a/tests/regression/tools/notification/test_notification_kernel_error b/tests/regression/tools/notification/test_notification_kernel_error new file mode 100755 index 000000000..fca7ef540 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_error @@ -0,0 +1,115 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +SYSCALL_TESTAPP_NAME="gen-syscall-events" +SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME" + +USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary" +USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME" + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +TRACE_PATH=$(mktemp -d) +PAGE_SIZE=$(getconf PAGE_SIZE) + +DIR=$(readlink -f $TESTDIR) +NUM_TESTS=104 + +source $TESTDIR/utils/utils.sh + +function kernel_event_generator_toggle_state +{ + kernel_event_generator_suspended=$((kernel_event_generator_suspended==0)) +} + +function generate_filter_events +{ + /bin/echo -n "10" > /proc/lttng-test-filter-event 2> /dev/null +} + +function kernel_event_generator +{ + command_to_run=$1 + state_file=$2 + kernel_event_generator_suspended=0 + trap kernel_event_generator_toggle_state SIGUSR1 + + while (true); do + if [[ $kernel_event_generator_suspended -eq "1" ]]; then + touch $state_file + sleep 0.5 + else + if [[ -f $state_file ]]; then + rm $state_file 2> /dev/null + fi + $command_to_run + fi + done +} + +function test_basic_error_path +{ + kernel_event_generator generate_filter_events $TESTAPP_STATE_PATH & + APP_PID=$! + + $CURDIR/notification 1 LTTNG_DOMAIN_KERNEL $APP_PID $TESTAPP_STATE_PATH + + kill -9 $APP_PID + wait $APP_PID 2> /dev/null +} + + +if [ "$(id -u)" == "0" ]; then + consumerd_pipe=() + + validate_lttng_modules_present + + # This is needed since the testpoint create a pipe with the consumer + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") + done + + modprobe lttng-test + + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + start_lttng_sessiond_notap + + test_basic_error_path + + stop_lttng_sessiond_notap + rmmod lttng-test + + rm -rf ${consumerd_pipe[@]} 2> /dev/null +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf $TRACE_PATH +rm -rf $TMPDIR diff --git a/tests/regression/tools/notification/test_notification_kernel_instrumentation b/tests/regression/tools/notification/test_notification_kernel_instrumentation new file mode 100755 index 000000000..236775aed --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_instrumentation @@ -0,0 +1,125 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +SYSCALL_TESTAPP_NAME="gen-syscall-events" +SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME" + +USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary" +USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME" + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +TRACE_PATH=$(mktemp -d) +PAGE_SIZE=$(getconf PAGE_SIZE) + +DIR=$(readlink -f $TESTDIR) +NUM_TESTS=104 + +source $TESTDIR/utils/utils.sh + +function kernel_event_generator_toggle_state +{ + kernel_event_generator_suspended=$((kernel_event_generator_suspended==0)) +} + +function generate_filter_events +{ + /bin/echo -n "10" > /proc/lttng-test-filter-event 2> /dev/null +} + +function generate_syscalls +{ + # Pass /dev/null so to generate the syscall right away. + $SYSCALL_TESTAPP_BIN /dev/null +} + +function userspace_probe_testapp +{ + $USERSPACE_PROBE_ELF_TESTAPP_BIN +} + +function kernel_event_generator +{ + command_to_run=$1 + state_file=$2 + kernel_event_generator_suspended=0 + trap kernel_event_generator_toggle_state SIGUSR1 + + while (true); do + if [[ $kernel_event_generator_suspended -eq "1" ]]; then + touch $state_file + sleep 0.5 + else + if [[ -f $state_file ]]; then + rm $state_file 2> /dev/null + fi + $command_to_run + fi + done +} + +function test_kernel_instrumentation_notification +{ + kernel_event_generator generate_filter_events $TESTAPP_STATE_PATH & + APP_PID=$! + + $CURDIR/notification 4 LTTNG_DOMAIN_KERNEL $APP_PID $TESTAPP_STATE_PATH + + kill -9 $APP_PID + wait $APP_PID 2> /dev/null +} + +if [ "$(id -u)" == "0" ]; then + consumerd_pipe=() + + validate_lttng_modules_present + + # This is needed since the testpoint create a pipe with the consumer + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") + done + + modprobe lttng-test + + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + start_lttng_sessiond_notap + + test_kernel_instrumentation_notification + + stop_lttng_sessiond_notap + rmmod lttng-test + + rm -rf ${consumerd_pipe[@]} 2> /dev/null +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf $TRACE_PATH +rm -rf $TMPDIR diff --git a/tests/regression/tools/notification/test_notification_kernel_syscall b/tests/regression/tools/notification/test_notification_kernel_syscall new file mode 100755 index 000000000..05f9e277d --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_syscall @@ -0,0 +1,125 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +SYSCALL_TESTAPP_NAME="gen-syscall-events" +SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME" + +USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary" +USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME" + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +TRACE_PATH=$(mktemp -d) +PAGE_SIZE=$(getconf PAGE_SIZE) + +DIR=$(readlink -f $TESTDIR) +NUM_TESTS=104 + +source $TESTDIR/utils/utils.sh + +function kernel_event_generator_toggle_state +{ + kernel_event_generator_suspended=$((kernel_event_generator_suspended==0)) +} + +function generate_filter_events +{ + /bin/echo -n "10" > /proc/lttng-test-filter-event 2> /dev/null +} + +function generate_syscalls +{ + # Pass /dev/null so to generate the syscall right away. + $SYSCALL_TESTAPP_BIN /dev/null +} + +function userspace_probe_testapp +{ + $USERSPACE_PROBE_ELF_TESTAPP_BIN +} + +function kernel_event_generator +{ + command_to_run=$1 + state_file=$2 + kernel_event_generator_suspended=0 + trap kernel_event_generator_toggle_state SIGUSR1 + + while (true); do + if [[ $kernel_event_generator_suspended -eq "1" ]]; then + touch $state_file + sleep 0.5 + else + if [[ -f $state_file ]]; then + rm $state_file 2> /dev/null + fi + $command_to_run + fi + done +} + +function test_kernel_syscall_notification +{ + kernel_event_generator generate_syscalls $TESTAPP_STATE_PATH & + APP_PID=$! + + $CURDIR/notification 5 LTTNG_DOMAIN_KERNEL $APP_PID $TESTAPP_STATE_PATH + + kill -9 $APP_PID + wait $APP_PID 2> /dev/null +} + +if [ "$(id -u)" == "0" ]; then + consumerd_pipe=() + + validate_lttng_modules_present + + # This is needed since the testpoint create a pipe with the consumer + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") + done + + modprobe lttng-test + + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + start_lttng_sessiond_notap + + test_kernel_syscall_notification + + stop_lttng_sessiond_notap + rmmod lttng-test + + rm -rf ${consumerd_pipe[@]} 2> /dev/null +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf $TRACE_PATH +rm -rf $TMPDIR diff --git a/tests/regression/tools/notification/test_notification_kernel_userspace_probe b/tests/regression/tools/notification/test_notification_kernel_userspace_probe new file mode 100755 index 000000000..253631028 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_userspace_probe @@ -0,0 +1,116 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +SYSCALL_TESTAPP_NAME="gen-syscall-events" +SYSCALL_TESTAPP_BIN="$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME" + +USERSPACE_PROBE_ELF_TESTAPP_NAME="userspace-probe-elf-binary" +USERSPACE_PROBE_ELF_TESTAPP_BIN="$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME" + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +TRACE_PATH=$(mktemp -d) +PAGE_SIZE=$(getconf PAGE_SIZE) + +DIR=$(readlink -f $TESTDIR) +NUM_TESTS=104 + +source $TESTDIR/utils/utils.sh + +function kernel_event_generator_toggle_state +{ + kernel_event_generator_suspended=$((kernel_event_generator_suspended==0)) +} + +function userspace_probe_testapp +{ + $USERSPACE_PROBE_ELF_TESTAPP_BIN +} + +function kernel_event_generator +{ + command_to_run=$1 + state_file=$2 + kernel_event_generator_suspended=0 + trap kernel_event_generator_toggle_state SIGUSR1 + + while (true); do + if [[ $kernel_event_generator_suspended -eq "1" ]]; then + touch $state_file + sleep 0.5 + else + if [[ -f $state_file ]]; then + rm $state_file 2> /dev/null + fi + $command_to_run + fi + done +} + +function test_kernel_userspace_probe_notification +{ + kernel_event_generator userspace_probe_testapp $TESTAPP_STATE_PATH & + APP_PID=$! + + $CURDIR/notification 6 LTTNG_DOMAIN_KERNEL \ + $APP_PID $TESTAPP_STATE_PATH \ + $USERSPACE_PROBE_ELF_TESTAPP_BIN "test_function" + + kill -9 $APP_PID + wait $APP_PID 2> /dev/null +} + +if [ "$(id -u)" == "0" ]; then + consumerd_pipe=() + + validate_lttng_modules_present + + # This is needed since the testpoint create a pipe with the consumer + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") + done + + modprobe lttng-test + + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + start_lttng_sessiond_notap + + test_kernel_userspace_probe_notification + + stop_lttng_sessiond_notap + rmmod lttng-test + + rm -rf ${consumerd_pipe[@]} 2> /dev/null +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf $TRACE_PATH +rm -rf $TMPDIR diff --git a/tests/regression/tools/notification/test_notification_ust_buffer_usage b/tests/regression/tools/notification/test_notification_ust_buffer_usage new file mode 100755 index 000000000..8488dceeb --- /dev/null +++ b/tests/regression/tools/notification/test_notification_ust_buffer_usage @@ -0,0 +1,106 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + + +TESTAPP_PATH="$TESTDIR/utils/testapp" + +GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events" +GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME" + +GEN_UST_NEVENTS_TESTAPP_NAME="gen-ust-nevents" +GEN_UST_NEVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_NEVENTS_TESTAPP_NAME/$GEN_UST_NEVENTS_TESTAPP_NAME" + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +NR_ITER=5 +NR_USEC_WAIT=5 + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +TRACE_PATH=$(mktemp -d) +PAGE_SIZE=$(getconf PAGE_SIZE) + +DIR=$(readlink -f $TESTDIR) + + +source $TESTDIR/utils/utils.sh + +function ust_event_generator_toggle_state +{ + ust_event_generator_suspended=$((ust_event_generator_suspended==0)) + +} +function ust_event_generator +{ + test_app=$1 + state_file=$2 + ust_event_generator_suspended=0 + trap ust_event_generator_toggle_state SIGUSR1 + + while (true); do + if [[ $ust_event_generator_suspended -eq "1" ]]; then + touch $state_file + sleep 0.5 + else + if [[ -f $state_file ]]; then + rm -rf $state_file 2> /dev/null + fi + taskset -c 0 $test_app -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 + fi + done +} + +function test_buffer_usage_notification +{ + consumerd_pipe=() + file_sync_after_first_event=$(mktemp -u) + event_name="tp:tptest" + + create_lttng_session_notap $SESSION_NAME $TRACE_PATH + + enable_ust_lttng_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size=$PAGE_SIZE + enable_ust_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME + + #This is needed since the testpoint create a pipe with the consumer type suffixed + for f in "$TESTPOINT_BASE_PATH"*; do + consumerd_pipe+=("$f") + done + + ust_event_generator $GEN_UST_EVENTS_TESTAPP_BIN $TESTAPP_STATE_PATH & + APP_PID=$! + + $CURDIR/notification 2 LTTNG_DOMAIN_UST $APP_PID $TESTAPP_STATE_PATH $SESSION_NAME $CHANNEL_NAME ${consumerd_pipe[@]} + + destroy_lttng_session_notap $SESSION_NAME + + # On ungraceful kill the app is cleaned up via the full_cleanup call + # Suppress kill message + kill -9 $APP_PID + wait $APP_PID 2> /dev/null + + # Just in case cleanup + rm -rf $TRACE_PATH +} + +LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}" +start_lttng_sessiond_notap + +test_buffer_usage_notification + +stop_lttng_sessiond_notap + +rm -rf $TMPDIR diff --git a/tests/regression/tools/notification/test_notification_ust_error b/tests/regression/tools/notification/test_notification_ust_error new file mode 100755 index 000000000..9b90b7efe --- /dev/null +++ b/tests/regression/tools/notification/test_notification_ust_error @@ -0,0 +1,85 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + + +TESTAPP_PATH="$TESTDIR/utils/testapp" + +GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events" +GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME" + +GEN_UST_NEVENTS_TESTAPP_NAME="gen-ust-nevents" +GEN_UST_NEVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_NEVENTS_TESTAPP_NAME/$GEN_UST_NEVENTS_TESTAPP_NAME" + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +NR_ITER=5 +NR_USEC_WAIT=5 + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +TRACE_PATH=$(mktemp -d) +PAGE_SIZE=$(getconf PAGE_SIZE) + +DIR=$(readlink -f $TESTDIR) + + +source $TESTDIR/utils/utils.sh + +function ust_event_generator_toggle_state +{ + ust_event_generator_suspended=$((ust_event_generator_suspended==0)) + +} +function ust_event_generator +{ + test_app=$1 + state_file=$2 + ust_event_generator_suspended=0 + trap ust_event_generator_toggle_state SIGUSR1 + + while (true); do + if [[ $ust_event_generator_suspended -eq "1" ]]; then + touch $state_file + sleep 0.5 + else + if [[ -f $state_file ]]; then + rm -rf $state_file 2> /dev/null + fi + taskset -c 0 $test_app -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 + fi + done +} + +function test_basic_error_path +{ + ust_event_generator $GEN_UST_EVENTS_TESTAPP_BIN $TESTAPP_STATE_PATH & + APP_PID=$! + + $CURDIR/notification 1 LTTNG_DOMAIN_UST $APP_PID $TESTAPP_STATE_PATH + + kill -9 $APP_PID + wait $APP_PID 2> /dev/null +} + +LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}" +start_lttng_sessiond_notap + +test_basic_error_path + +stop_lttng_sessiond_notap + +rm -rf $TMPDIR diff --git a/tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion b/tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion new file mode 100755 index 000000000..8f2ed739b --- /dev/null +++ b/tests/regression/tools/notification/test_notification_ust_event_rule_condition_exclusion @@ -0,0 +1,85 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +#This is needed since the testpoint create a pipe with the consumerd type suffixed +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) + + +TESTAPP_PATH="$TESTDIR/utils/testapp" + +GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events" +GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME" + +GEN_UST_NEVENTS_TESTAPP_NAME="gen-ust-nevents" +GEN_UST_NEVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_NEVENTS_TESTAPP_NAME/$GEN_UST_NEVENTS_TESTAPP_NAME" + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +NR_ITER=5 +NR_USEC_WAIT=5 + +SESSION_NAME="my_session" +CHANNEL_NAME="my_channel" + +TRACE_PATH=$(mktemp -d) +PAGE_SIZE=$(getconf PAGE_SIZE) + +DIR=$(readlink -f $TESTDIR) + + +source $TESTDIR/utils/utils.sh + +function ust_event_generator_toggle_state +{ + ust_event_generator_suspended=$((ust_event_generator_suspended==0)) + +} +function ust_event_generator +{ + test_app=$1 + state_file=$2 + ust_event_generator_suspended=0 + trap ust_event_generator_toggle_state SIGUSR1 + + while (true); do + if [[ $ust_event_generator_suspended -eq "1" ]]; then + touch $state_file + sleep 0.5 + else + if [[ -f $state_file ]]; then + rm -rf $state_file 2> /dev/null + fi + taskset -c 0 $test_app -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 + fi + done +} + +function test_event_rule_condition_exclusion_notification +{ + ust_event_generator $GEN_UST_NEVENTS_TESTAPP_BIN $TESTAPP_STATE_PATH & + APP_PID=$! + + $CURDIR/.libs/notification 3 LTTNG_DOMAIN_UST $APP_PID $TESTAPP_STATE_PATH + + kill -9 $APP_PID + wait $APP_PID 2> /dev/null +} + +LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}" +start_lttng_sessiond_notap + +test_event_rule_condition_exclusion_notification + +stop_lttng_sessiond_notap + +rm -rf $TMPDIR diff --git a/tests/regression/tools/rotation/schedule_api.c b/tests/regression/tools/rotation/schedule_api.c index 03c446fcb..1e19f5be3 100644 --- a/tests/regression/tools/rotation/schedule_api.c +++ b/tests/regression/tools/rotation/schedule_api.c @@ -22,7 +22,7 @@ const char *session_name; -bool schedules_equal(const struct lttng_rotation_schedule *a, +static bool schedules_equal(const struct lttng_rotation_schedule *a, const struct lttng_rotation_schedule *b) { bool equal = false; @@ -84,7 +84,7 @@ end: return equal; } -void test_add_null_session(void) +static void test_add_null_session(void) { enum lttng_rotation_status status; struct lttng_rotation_schedule *size_schedule = NULL; @@ -97,7 +97,7 @@ void test_add_null_session(void) lttng_rotation_schedule_destroy(size_schedule); } -void test_add_null_schedule(void) +static void test_add_null_schedule(void) { enum lttng_rotation_status status; @@ -106,7 +106,7 @@ void test_add_null_schedule(void) "NULL schedule rejected by lttng_session_add_rotation_schedule()"); } -void test_add_uninitialized_schedule(void) +static void test_add_uninitialized_schedule(void) { enum lttng_rotation_status status; struct lttng_rotation_schedule *size_schedule = NULL, @@ -132,7 +132,7 @@ void test_add_uninitialized_schedule(void) lttng_rotation_schedule_destroy(periodic_schedule); } -void test_remove_null_session(void) +static void test_remove_null_session(void) { enum lttng_rotation_status status; struct lttng_rotation_schedule *size_schedule = NULL; @@ -145,7 +145,7 @@ void test_remove_null_session(void) lttng_rotation_schedule_destroy(size_schedule); } -void test_remove_null_schedule(void) +static void test_remove_null_schedule(void) { enum lttng_rotation_status status; @@ -154,7 +154,7 @@ void test_remove_null_schedule(void) "NULL schedule rejected by lttng_session_remove_rotation_schedule()"); } -void test_remove_uninitialized_schedule(void) +static void test_remove_uninitialized_schedule(void) { enum lttng_rotation_status status; struct lttng_rotation_schedule *size_schedule = NULL, @@ -176,7 +176,7 @@ void test_remove_uninitialized_schedule(void) lttng_rotation_schedule_destroy(periodic_schedule); } -void test_uninitialized_schedule_get(void) +static void test_uninitialized_schedule_get(void) { uint64_t value; enum lttng_rotation_status status; @@ -200,7 +200,7 @@ void test_uninitialized_schedule_get(void) } -void test_add_list_remove_schedule( +static void test_add_list_remove_schedule( const struct lttng_rotation_schedule *original_schedule) { int ret; @@ -249,7 +249,7 @@ void test_add_list_remove_schedule( } -void test_add_list_remove_size_schedule(void) +static void test_add_list_remove_size_schedule(void) { struct lttng_rotation_schedule *size_schedule; @@ -261,7 +261,7 @@ void test_add_list_remove_size_schedule(void) lttng_rotation_schedule_destroy(size_schedule); } -void test_add_list_remove_periodic_schedule(void) +static void test_add_list_remove_periodic_schedule(void) { struct lttng_rotation_schedule *periodic_schedule; diff --git a/tests/regression/tools/snapshots/ust_test b/tests/regression/tools/snapshots/ust_test index 3f7ee2555..c2563afd8 100755 --- a/tests/regression/tools/snapshots/ust_test +++ b/tests/regression/tools/snapshots/ust_test @@ -358,7 +358,7 @@ function test_ust_local_snapshot_large_metadata () enable_ust_lttng_event_ok $SESSION_NAME $LM_EVENT $CHANNEL_NAME start_lttng_tracing_ok $SESSION_NAME lttng_snapshot_add_output_ok $SESSION_NAME file://$TRACE_PATH - $LM_BIN 1 1 + $LM_BIN --iter 1 --wait 1 ok $? "Start application to trace" lttng_snapshot_record $SESSION_NAME stop_lttng_tracing_ok $SESSION_NAME diff --git a/tests/regression/tools/trigger/Makefile.am b/tests/regression/tools/trigger/Makefile.am new file mode 100644 index 000000000..ba55d31b1 --- /dev/null +++ b/tests/regression/tools/trigger/Makefile.am @@ -0,0 +1,55 @@ +AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_srcdir)/tests -I$(top_srcdir)/tests/utils/ -I$(srcdir) +AM_LDFLAGS = + + +LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la +LIB_LTTNG_CTL = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la + +noinst_PROGRAMS = base_client trigger + +if NO_SHARED + +CLEANFILES = libpause_consumer.so libpause_consumer.so.debug +EXTRA_DIST = test_trigger_ust test_trigger_kernel base_client.c trigger.c consumer_testpoints.c + +else + +# In order to use testpoint, the helper library +# must be built as .so to be able to LD_PRELOAD it. +FORCE_SHARED_LIB_OPTIONS = -module -shared -avoid-version \ + -rpath $(abs_builddir) + +libpause_consumer_la_SOURCES = consumer_testpoints.c +libpause_consumer_la_LIBADD = \ + $(top_builddir)/src/common/libcommon.la \ + $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ + $(DL_LIBS) +libpause_consumer_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS) +noinst_LTLIBRARIES = libpause_consumer.la + +base_client_SOURCES = base_client.c +base_client_LDADD = $(LIB_LTTNG_CTL) + +trigger_SOURCES = trigger.c +trigger_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm + +noinst_SCRIPTS = test_trigger_ust test_trigger_kernel +EXTRA_DIST = test_trigger_ust test_trigger_kernel \ + test_add_trigger_cli \ + test_list_triggers_cli \ + test_remove_trigger_cli + +all-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + cp -f $(srcdir)/$$script $(builddir); \ + done; \ + fi + +clean-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + rm -f $(builddir)/$$script; \ + done; \ + fi +endif diff --git a/tests/regression/tools/trigger/base_client.c b/tests/regression/tools/trigger/base_client.c new file mode 100644 index 000000000..f5bd7f159 --- /dev/null +++ b/tests/regression/tools/trigger/base_client.c @@ -0,0 +1,252 @@ +/* + * base_client.c + * + * Base client application for testing of LTTng trigger API + * + * Copyright 2019 Jonathan Rajotte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *session_name = NULL; +enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; +const char *pattern = NULL; + +int parse_arguments(char **argv) { + const char *domain_type_string = NULL; + + session_name = argv[1]; + domain_type_string = argv[2]; + pattern = argv[3]; + + /* Parse arguments */ + /* Domain type */ + if (!strcasecmp("LTTNG_DOMAIN_UST", domain_type_string)) { + domain_type = LTTNG_DOMAIN_UST; + } + if (!strcasecmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) { + domain_type = LTTNG_DOMAIN_KERNEL; + } + if (!strcasecmp("LTTNG_DOMAIN_JUL", domain_type_string)) { + domain_type = LTTNG_DOMAIN_JUL; + } + if (!strcasecmp("LTTNG_DOMAIN_PYTHON", domain_type_string)) { + domain_type = LTTNG_DOMAIN_PYTHON; + } + if (!strcasecmp("LTTNG_DOMAIN_LOG4J", domain_type_string)) { + domain_type = LTTNG_DOMAIN_LOG4J; + } + if (domain_type == LTTNG_DOMAIN_NONE) { + printf("error: Unknown domain type\n"); + goto error; + } + + return 0; +error: + return 1; +} + +int main(int argc, char **argv) +{ + int ret = 0; + struct lttng_condition *condition = NULL; + + enum lttng_event_rule_status event_rule_status; + struct lttng_event_rule *event_rule = NULL; + + enum lttng_action_status action_status; + struct lttng_action *action = NULL; + + enum lttng_notification_channel_status nc_status; + struct lttng_notification_channel *notification_channel = NULL; + + const char* exclusions[] = { "sample_component:message2"}; + + struct lttng_trigger *trigger = NULL; + + if (argc < 4) { + printf("error: Missing arguments for tests\n"); + ret = 1; + goto end; + } + + ret = parse_arguments(argv); + if (ret) { + printf("error: Could not parse arguments\n"); + goto end; + } + + event_rule = lttng_event_rule_tracepoint_create(domain_type); + if (!event_rule) { + printf("error: Could not create condition object\n"); + ret = 1; + goto end; + } + + event_rule_status = lttng_event_rule_tracepoint_set_pattern(event_rule, pattern); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + printf("error: Could not set pattern\n"); + ret = 1; + goto end; + } + + event_rule_status = lttng_event_rule_tracepoint_set_filter(event_rule, "message=='Hello World'"); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + printf("error: Could not set pattern\n"); + ret = 1; + goto end; + } + + event_rule_status = lttng_event_rule_tracepoint_set_exclusions(event_rule, 1, exclusions); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { + printf("error: Could not set exclusions\n"); + ret = 1; + goto end; + } + + condition = lttng_condition_event_rule_create(event_rule); + if (!condition) { + printf("error: Could not create condition\n"); + ret = 1; + goto end; + } + /* Ownership was passed to condition */ + event_rule = NULL; + + action = lttng_action_notify_create(); + if (!action) { + printf("error: Could not create action notify\n"); + ret = 1; + goto end; + } + + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + printf("error: Could not create trigger\n"); + ret = 1; + goto end; + } + + ret = lttng_register_trigger(trigger); + + notification_channel = lttng_notification_channel_create( + lttng_session_daemon_notification_endpoint); + if (!notification_channel) { + printf("error: Could not create notification channel\n"); + ret = 1; + goto end; + } + /* + * An equivalent trigger might already be registered if an other app + * registered an equivalent trigger. + */ + if (ret < 0 && ret != -LTTNG_ERR_TRIGGER_EXISTS) { + printf("error: %s\n", lttng_strerror(ret)); + ret = 1; + goto end; + } + + nc_status = lttng_notification_channel_subscribe(notification_channel, condition); + if (nc_status != LTTNG_NOTIFICATION_CHANNEL_STATUS_OK) { + printf("error: Could not subscribe\n"); + ret = 1; + goto end; + } + + for (;;) { + struct lttng_notification *notification; + enum lttng_notification_channel_status status; + const struct lttng_evaluation *notification_evaluation; + const struct lttng_condition *notification_condition; + const char *name; + + /* Receive the next notification. */ + status = lttng_notification_channel_get_next_notification( + notification_channel, + ¬ification); + + switch (status) { + case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK: + break; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED: + ret = 1; + printf("error: No drop should be observed during this test app\n"); + goto end; + case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED: + /* + * The notification channel has been closed by the + * session daemon. This is typically caused by a session + * daemon shutting down (cleanly or because of a crash). + */ + printf("error: Notification channel was closed\n"); + ret = 1; + goto end; + default: + /* Unhandled conditions / errors. */ + printf("error: Unknown notification channel status\n"); + ret = 1; + goto end; + } + + notification_condition = lttng_notification_get_condition(notification); + notification_evaluation = lttng_notification_get_evaluation(notification); + switch (lttng_evaluation_get_type(notification_evaluation)) { + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + lttng_evaluation_event_rule_get_trigger_name(notification_evaluation, &name); + printf("Received nootification from trigger \"%s\"\n", name); + break; + default: + printf("error: Wrong notification evaluation type \n"); + break; + + } + + lttng_notification_destroy(notification); + } +end: + if (trigger) { + lttng_unregister_trigger(trigger); + } + lttng_event_rule_destroy(event_rule); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + lttng_action_destroy(action); + printf("exit: %d\n", ret); + return ret; +} diff --git a/tests/regression/tools/trigger/consumer_testpoints.c b/tests/regression/tools/trigger/consumer_testpoints.c new file mode 100644 index 000000000..1f5d83ecb --- /dev/null +++ b/tests/regression/tools/trigger/consumer_testpoints.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *pause_pipe_path; +static struct lttng_pipe *pause_pipe; +static int *data_consumption_state; +static enum lttng_consumer_type (*lttng_consumer_get_type)(void); + +int lttng_opt_verbose; +int lttng_opt_mi; +int lttng_opt_quiet; + +static +void __attribute__((destructor)) pause_pipe_fini(void) +{ + int ret; + + if (pause_pipe_path) { + ret = unlink(pause_pipe_path); + if (ret) { + PERROR("unlink pause pipe"); + } + } + + free(pause_pipe_path); + lttng_pipe_destroy(pause_pipe); +} + +/* + * We use this testpoint, invoked at the start of the consumerd's data handling + * thread to create a named pipe/FIFO which a test application can use to either + * pause or resume the consumption of data. + */ +int __testpoint_consumerd_thread_data(void) +{ + int ret = 0; + const char *pause_pipe_path_prefix, *domain; + + pause_pipe_path_prefix = lttng_secure_getenv( + "CONSUMER_PAUSE_PIPE_PATH"); + if (!pause_pipe_path_prefix) { + ret = -1; + goto end; + } + + /* + * These symbols are exclusive to the consumerd process, hence we can't + * rely on their presence in the sessiond. Not looking-up these symbols + * dynamically would not allow this shared object to be LD_PRELOAD-ed + * when launching the session daemon. + */ + data_consumption_state = dlsym(NULL, "data_consumption_paused"); + assert(data_consumption_state); + lttng_consumer_get_type = dlsym(NULL, "lttng_consumer_get_type"); + assert(lttng_consumer_get_type); + + switch (lttng_consumer_get_type()) { + case LTTNG_CONSUMER_KERNEL: + domain = "kernel"; + break; + case LTTNG_CONSUMER32_UST: + domain = "ust32"; + break; + case LTTNG_CONSUMER64_UST: + domain = "ust64"; + break; + default: + abort(); + } + + ret = asprintf(&pause_pipe_path, "%s-%s", pause_pipe_path_prefix, + domain); + if (ret < 1) { + ERR("Failed to allocate pause pipe path"); + goto end; + } + + DBG("Creating pause pipe at %s", pause_pipe_path); + pause_pipe = lttng_pipe_named_open(pause_pipe_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, O_NONBLOCK); + if (!pause_pipe) { + ERR("Failed to create pause pipe at %s", pause_pipe_path); + ret = -1; + goto end; + } + + /* Only the read end of the pipe is useful to us. */ + ret = lttng_pipe_write_close(pause_pipe); +end: + return ret; +} + +int __testpoint_consumerd_thread_data_poll(void) +{ + int ret = 0; + uint8_t value; + bool value_read = false; + + if (!pause_pipe) { + ret = -1; + goto end; + } + + /* Purge pipe and only consider the freshest value. */ + do { + ret = lttng_pipe_read(pause_pipe, &value, sizeof(value)); + if (ret == sizeof(value)) { + value_read = true; + } + } while (ret == sizeof(value)); + + ret = (errno == EAGAIN) ? 0 : -errno; + + if (value_read) { + *data_consumption_state = !!value; + DBG("Message received on pause pipe: %s data consumption", + *data_consumption_state ? "paused" : "resumed"); + } +end: + return ret; +} diff --git a/tests/regression/tools/trigger/test_add_trigger_cli b/tests/regression/tools/trigger/test_add_trigger_cli new file mode 100755 index 000000000..ff9bdbb54 --- /dev/null +++ b/tests/regression/tools/trigger/test_add_trigger_cli @@ -0,0 +1,330 @@ +#!/bin/bash +# +# Copyright (C) - 2020 EfficiOS, inc +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Test the `lttng add-trigger` command line interface. + +CURDIR="$(dirname "$0")" +TESTDIR="$CURDIR/../../.." + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +plan_tests 171 + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +# shellcheck disable=SC2119 +start_lttng_sessiond_notap + +tmp_stdout=$(mktemp -t test_parse_cli_trigger_stdout.XXXXXX) +tmp_stderr=$(mktemp -t test_parse_cli_trigger_stderr.XXXXXX) +uprobe_elf_binary="${TESTDIR}/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary" + +if [ "$(id -u)" == "0" ]; then + ist_root=1 +else + ist_root=0 +fi + +function test_success () +{ + local test_name="$1" + shift + + diag "${FULL_LTTNG_BIN} add-trigger $*" + "${FULL_LTTNG_BIN}" add-trigger "$@" > "${tmp_stdout}" 2> "${tmp_stderr}" + ok $? "${test_name}: exit code is 0" + + diff -u "${tmp_stdout}" <(echo "Trigger registered successfully.") + ok $? "${test_name}: expected stdout" + + diff -u "${tmp_stderr}" /dev/null + ok $? "${test_name}: expected stderr" +} + +function test_failure () +{ + local test_name="$1" + local error_msg="$2" + + shift 2 + + diag "${FULL_LTTNG_BIN} add-trigger $*" + "${FULL_LTTNG_BIN}" add-trigger "$@" > "${tmp_stdout}" 2> "${tmp_stderr}" + isnt $? 0 "${test_name}: exit code is not 0" + + diff -u "${tmp_stdout}" /dev/null + ok $? "${test_name}: expected stdout" + + diff -u "${tmp_stderr}" <(echo "${error_msg}") + ok $? "${test_name}: expected stderr" +} + +# top-level options +test_success "explicit id" \ + --id hohoho \ + --condition on-event some-event-id -u \ + --action notify + +# `--condition on-event` successes +test_success "--condition on-event some-event -u" \ + --condition on-event some-event -u \ + --action notify + +test_success "--condition on-event -a -u" \ + --condition on-event -a -u \ + --action notify + +test_success "--fire-once-after" \ + --condition on-event -u test-fire-once-after \ + --action notify \ + --fire-once-after=55 + +test_success "--fire-every" \ + --condition on-event -u test-fire-every \ + --action notify \ + --fire-every=55 + +skip $ist_root "non-root user: skipping kprobe tests" 12 || { + test_success "--condition on-event probe by symbol" \ + --condition on-event -k --probe=do_sys_open my_sys_open \ + --action notify + + test_success "--condition on-event probe by symbol with offset" \ + --condition on-event -k --probe=do_sys_open+10 my_sys_open \ + --action notify + + sys_open_addr=$(grep ' T do_sys_open$' /proc/kallsyms | cut -f 1 -d ' ') + + test_success "--condition on-event probe by address" \ + --condition on-event -k "--probe=${sys_open_addr}" my_sys_open \ + --action notify + + test_success "--condition on-event probe by address with offset" \ + --condition on-event -k "--probe=${sys_open_addr}+10" my_sys_open \ + --action notify +} + +skip $ist_root "non-root user: skipping uprobe tests" 6 || { + test_success "--condition on-event uprobe" \ + --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe \ + --action notify + + test_success "--condition on-event uprobe with elf prefix" \ + --condition on-event -k --userspace-probe=elf:${uprobe_elf_binary}:test_function ma-probe-2 \ + --action notify +} + +skip $ist_root "non-root user: skipping syscall tests" 9 || { + test_success "--condition on-event syscall" \ + --condition on-event -k --syscall open \ + --action notify + + test_success "--condition on-event syscall -a" \ + --condition on-event -k --syscall -a \ + --action notify + + test_success "--condition on-event syscall with filter" \ + --condition on-event -k --syscall --filter 'a > 2' open \ + --action notify +} + +# `--action notify` successes +test_success "--action notify" \ + --condition on-event some-event-notify -u \ + --action notify + +# `--action start-session` successes +test_success "--action start-session" \ + --condition on-event some-event-start-session -u \ + --action start-session ze-session + +# `--action stop-session` successes +test_success "--action stop-session foo" \ + --condition on-event some-event-stop-session -u \ + --action stop-session ze-session + +# `--action rotate-session` successes +test_success "--action rotate-session foo" \ + --condition on-event some-event-rotate-session -u \ + --action rotate-session ze-session + +# `--action snapshot-session` successes +test_success "--action snapshot-session foo" \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session ze-session + +test_success "--action snapshot-session with file URI" \ + --condition on-event some-event-snapshot-session2 -u \ + --action snapshot-session ze-session /hello + +test_success "--action snapshot-session with net URI" \ + --condition on-event some-event-snapshot-session3 -u \ + --action snapshot-session ze-session net://1.2.3.4 + +test_success "--action snapshot-session with ctrl/data URIs" \ + --condition on-event some-event-snapshot-session4 -u \ + --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1234 --data-url=tcp://1.2.3.4:1235 + +# top-level failures +test_failure "no args" "Missing --condition." + +test_failure "unknown option" \ + "Unknown option \`--hello\`" \ + --hello + +test_failure "missing --action" \ + "Need at least one --action." \ + --condition on-event hello -u + +test_failure "two --condition" \ + "A --condition was already given." \ + --condition on-event aaa -u \ + --condition on-event bbb -u \ + --action notify + +test_failure "missing argument to --id" \ + "Error: While parsing argument #1 (\`--id\`): Missing required argument for option \`--id\`" \ + --id + +for cmd in fire-once-after fire-every; do + test_failure "missing argument to --${cmd}" \ + "Error: While parsing argument #1 (\`--${cmd}\`): Missing required argument for option \`--${cmd}\`" \ + --condition on-event -u -a --action notify \ + --${cmd} + + test_failure "invalid argument to --${cmd}: non-digit character" \ + "Failed to parse \`123bob\` as an integer." \ + --condition on-event -u -a --action notify \ + --${cmd} 123bob + + test_failure "invalid argument to --${cmd}: empty string" \ + "Failed to parse \`\` as an integer." \ + --condition on-event -u -a --action notify \ + --${cmd} "" +done + +# `--condition` failures +test_failure "missing args after --condition" \ + "Missing condition name." \ + --condition +test_failure "unknown --condition" \ + "Unknown condition name: zoofest" \ + --condition zoofest + +# `--condition on-event` failures +test_failure "missing args after --condition on-event" \ + "Error: Need to provide either a tracepoint name or -a/--all." \ + --condition on-event +test_failure "missing domain in --condition on-event" \ + "Error: Please specify a domain (-k/-u/-j)." \ + --condition on-event -a +test_failure "extra args after --condition on-event" \ + "Error: Unexpected argument: bozo" \ + --condition on-event foo -u bozo +test_failure "--condition on-event: --all with --probe" \ + "Error: Can't use -a/--all with event rule of type probe." \ + --condition on-event --probe=do_sys_open --all +test_failure "--condition on-event: missing tracepoint name with --probe" \ + "Error: Need to provide either a tracepoint name or -a/--all." \ + --condition on-event -k --probe do_sys_open + +test_failure "--condition on-event: missing tracepoint name with --userspace-probe" \ + "Error: Need to provide either a tracepoint name or -a/--all." \ + --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function + +test_failure "--condition on-event: extra argument with --userspace-probe" \ + "Error: Unexpected argument: world" \ + --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_failure hello world + +test_failure "--condition on-event: missing tracepoint name with --syscall" \ + "Error: Need to provide either a tracepoint name or -a/--all." \ + --condition on-event -k --syscall + +test_failure "--condition on-event: extra argument with --syscall" \ + "Error: Unexpected argument: open" \ + --condition on-event -k --syscall open open + +test_failure "--condition on-event: both -a and a tracepoint name with --syscall" \ + "Error: Can't provide a tracepoint name with -a/--all." \ + --condition on-event -k --syscall -a open + +# `--action` failures +test_failure "missing args after --action" \ + "Missing action name." \ + --condition on-event -u -a \ + --action + +# `--action notify` failures +test_failure "extra arg after --action notify" \ + "Unexpected argument \`bob\`." \ + --condition on-event -u -a \ + --action notify bob + +# `--action start-session` failures +test_failure "missing arg after --action start-session" \ + "Missing session name." \ + --condition on-event some-event-start-session -u \ + --action start-session +test_failure "extra arg after --action start-session" \ + "Unexpected argument \`bob\`." \ + --condition on-event some-event-start-session -u \ + --action start-session ze-session bob + +# `--action stop-session` failures +test_failure "missing arg after --action stop-session" \ + "Missing session name." \ + --condition on-event some-event-stop-session -u \ + --action stop-session +test_failure "extra arg after --action stop-session" \ + "Unexpected argument \`bob\`." \ + --condition on-event some-event-stop-session -u \ + --action stop-session ze-session bob + +# `--action rotate-session` failures +test_failure "missing arg after --action rotate-session" \ + "Missing session name." \ + --condition on-event some-event-rotate-session -u \ + --action rotate-session +test_failure "extra arg after --action rotate-session" \ + "Unexpected argument \`bob\`." \ + --condition on-event some-event-rotate-session -u \ + --action rotate-session ze-session bob + +# `--action snapshot-session` failures +test_failure "missing arg after --action snapshot-session" \ + "Missing session name." \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session +test_failure "extra arg after --action snapshot-session" \ + "Unexpected argument \`bob\`." \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session ze-session /dest bob +test_failure "snapshot-session action, --max-size without destination" \ + "Can't provide a snapshot output max size without a snapshot output destination." \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session ze-session --max-size 10M +test_failure "snapshot-session action, --name without destination" \ + "Can't provide a snapshot output name without a snapshot output destination." \ + --condition on-event some-event-snapshot-session -u \ + --action snapshot-session ze-session --name hallo + + +# Cleanup +stop_lttng_sessiond_notap +rm -f "${tmp_stdout}" +rm -f "${tmp_stderr}" diff --git a/tests/regression/tools/trigger/test_list_triggers_cli b/tests/regression/tools/trigger/test_list_triggers_cli new file mode 100755 index 000000000..202a7b70a --- /dev/null +++ b/tests/regression/tools/trigger/test_list_triggers_cli @@ -0,0 +1,301 @@ +#!/bin/bash +# +# Copyright (C) - 2020 EfficiOS, inc +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Test the `lttng list-trigger` command line interface. + +CURDIR="$(dirname "$0")" +TESTDIR="$CURDIR/../../.." + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +plan_tests 41 + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +tmp_stdout=$(mktemp -t test_list_triggers_cli_stdout.XXXXXX) +tmp_stderr=$(mktemp -t test_list_triggers_cli_stderr.XXXXXX) +tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX) +uprobe_elf_binary=$(realpath "${TESTDIR}/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary") + +if [ "$(id -u)" == "0" ]; then + ist_root=1 +else + ist_root=0 +fi + +function add_trigger () +{ + "${FULL_LTTNG_BIN}" add-trigger "$@" + ok $? "add trigger \`$*\`: exit code is 0" +} + +function list_triggers () +{ + local test_name="$1" + local expected_stdout_file="$2" + + "${FULL_LTTNG_BIN}" list-triggers > "${tmp_stdout}" 2> "${tmp_stderr}" + ok $? "${test_name}: exit code is 0" + + diff -u "${expected_stdout_file}" "${tmp_stdout}" + ok $? "${test_name}: expected stdout" + + diff -u /dev/null "${tmp_stderr}" + ok $? "${test_name}: expected stderr" +} + +test_top_level_options () +{ + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + + add_trigger --id hello --condition on-event -u test-id --action notify + add_trigger --fire-once-after 123 --condition on-event -u test-fire-once-after --action notify + add_trigger --fire-every 124 --condition on-event -u test-fire-every --action notify + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T2 + firing policy: once after 123 occurences + condition: event rule hit + rule: test-fire-once-after (type: tracepoint, domain: ust) + actions: + notify + - id: T3 + firing policy: after every 124 occurences + condition: event rule hit + rule: test-fire-every (type: tracepoint, domain: ust) + actions: + notify + - id: hello + condition: event rule hit + rule: test-id (type: tracepoint, domain: ust) + actions: + notify + EOF + + list_triggers "top level options" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_tracepoint () +{ + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + add_trigger --condition on-event -u -a --action notify + add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify + add_trigger --condition on-event 'hello*' -u -x 'hello2,hello3,hello4' --action notify + add_trigger --id BCD --condition on-event -u gerboise --loglevel INFO --action notify + add_trigger --condition on-event -u lemming --loglevel-only WARNING --action notify + + cat > "${tmp_expected_stdout}" <<- EOF + - id: ABC + condition: event rule hit + rule: aaa (type: tracepoint, domain: ust, filter: p == 2) + actions: + notify + - id: BCD + condition: event rule hit + rule: gerboise (type: tracepoint, domain: ust, log level <= TRACE_INFO) + actions: + notify + - id: T1 + condition: event rule hit + rule: * (type: tracepoint, domain: ust) + actions: + notify + - id: T3 + condition: event rule hit + rule: hello* (type: tracepoint, domain: ust, exclusions: hello2,hello3,hello4) + actions: + notify + - id: T5 + condition: event rule hit + rule: lemming (type: tracepoint, domain: ust, log level == TRACE_WARNING) + actions: + notify + EOF + + list_triggers "on-event, tracepoint event rule" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_probe () +{ + local sys_open_addr + + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + sys_open_addr=$(grep ' T do_sys_open$' /proc/kallsyms | cut -f 1 -d ' ') + + add_trigger --condition on-event -k --probe=do_sys_open my_sys_open --action notify + add_trigger --condition on-event -k --probe=do_sys_open+10 my_sys_open --action notify + add_trigger --condition on-event -k --probe="${sys_open_addr}" my_sys_open --action notify + add_trigger --condition on-event -k --probe="${sys_open_addr}+10" my_sys_open --action notify + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T1 + condition: event rule hit + rule: my_sys_open (type: probe, location: do_sys_open) + actions: + notify + - id: T2 + condition: event rule hit + rule: my_sys_open (type: probe, location: do_sys_open+10) + actions: + notify + - id: T3 + condition: event rule hit + rule: my_sys_open (type: probe, location: ${sys_open_addr}) + actions: + notify + - id: T4 + condition: event rule hit + rule: my_sys_open (type: probe, location: ${sys_open_addr}+10) + actions: + notify + EOF + + list_triggers "on-event, probe event rule" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_userspace_probe () +{ + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + add_trigger --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe --action notify + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T1 + condition: event rule hit + rule: ma-probe (type: userspace probe, location: ${uprobe_elf_binary}:test_function) + actions: + notify + EOF + + list_triggers "on-event, userspace-probe event rule" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_syscall () +{ + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + add_trigger --condition on-event -k --syscall open --action notify + add_trigger --condition on-event -k --syscall ptrace --filter 'a > 2' --action notify + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T1 + condition: event rule hit + - rule: open (type: syscall) + actions: + notify + - id: T2 + condition: event rule hit + - rule: ptrace (type: syscall, filter: a > 2) + actions: + notify + EOF + + list_triggers "on-event, syscall event rule" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_snapshot_action () +{ + start_lttng_sessiond_notap + + add_trigger --condition on-event -u some-event --action snapshot-session ze-session + add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path + add_trigger --condition on-event -u some-event --action snapshot-session ze-session file:///some/other/path + add_trigger --condition on-event -u some-event --action snapshot-session ze-session net://1.2.3.4 + add_trigger --condition on-event -u some-event --action snapshot-session ze-session net://1.2.3.4:1234:1235 + add_trigger --condition on-event -u some-event --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1111 --data-url=tcp://1.2.3.4:1112 + add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path --max-size=1234 + add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path --name=meh + + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T1 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\` + - id: T2 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, path: /some/path + - id: T3 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, path: /some/other/path + - id: T4 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, url: net://1.2.3.4 + - id: T5 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, url: net://1.2.3.4:1234:1235 + - id: T6 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, control url: tcp://1.2.3.4:1111, data url: tcp://1.2.3.4:1112 + - id: T7 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, path: /some/path, max size: 1234 + - id: T8 + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + actions: + snapshot session \`ze-session\`, path: /some/path, name: meh + EOF + + list_triggers "snapshot action" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_top_level_options +test_on_event_tracepoint +skip $ist_root "non-root user: skipping kprobe tests" 7 || test_on_event_probe +skip $ist_root "non-root user: skipping uprobe tests" 4 || test_on_event_userspace_probe +skip $ist_root "non-root user: skipping syscall tests" 5 || test_on_event_syscall +test_snapshot_action + +# Cleanup +rm -f "${tmp_stdout}" +rm -f "${tmp_stderr}" +rm -f "${tmp_expected_stdout}" diff --git a/tests/regression/tools/trigger/test_remove_trigger_cli b/tests/regression/tools/trigger/test_remove_trigger_cli new file mode 100755 index 000000000..4b3cce50d --- /dev/null +++ b/tests/regression/tools/trigger/test_remove_trigger_cli @@ -0,0 +1,110 @@ +#!/bin/bash +# +# Copyright (C) - 2020 EfficiOS, inc +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Test the `lttng remove-trigger` command line interface. + +CURDIR="$(dirname "$0")" +TESTDIR="$CURDIR/../../.." + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +plan_tests 17 + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +tmp_stdout=$(mktemp -t test_list_triggers_cli_stdout.XXXXXX) +tmp_stderr=$(mktemp -t test_list_triggers_cli_stderr.XXXXXX) +tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX) + +function add_trigger () +{ + "${FULL_LTTNG_BIN}" add-trigger "$@" + ok $? "add trigger \`$*\`: exit code is 0" +} + +function list_triggers () +{ + local test_name="$1" + local expected_stdout_file="$2" + + "${FULL_LTTNG_BIN}" list-triggers > "${tmp_stdout}" 2> "${tmp_stderr}" + ok $? "${test_name}: exit code is 0" + + diff -u "${expected_stdout_file}" "${tmp_stdout}" + ok $? "${test_name}: expected stdout" + + diff -u /dev/null "${tmp_stderr}" + ok $? "${test_name}: expected stderr" +} + +function remove_trigger () +{ + local id="$1" + local test_name="remove trigger ${id}" + + "${FULL_LTTNG_BIN}" remove-trigger "${id}" > "${tmp_stdout}" 2> "${tmp_stderr}" + ok $? "${test_name}: exit code is 0" + + diff -u <(echo "Removed trigger \`${id}\`.") "${tmp_stdout}" + ok $? "${test_name}: expected stdout" + + diff -u /dev/null "${tmp_stderr}" + ok $? "${test_name}: expected stderr" +} + +# shellcheck disable=SC2119 +start_lttng_sessiond_notap + +# Add a few triggers +add_trigger --condition on-event -u -a --action notify +add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify + +cat > "${tmp_expected_stdout}" <<- EOF +- id: ABC + condition: event rule hit + rule: aaa (type: tracepoint, domain: ust, filter: p == 2) + actions: + notify +- id: T1 + condition: event rule hit + rule: * (type: tracepoint, domain: ust) + actions: + notify +EOF +list_triggers "two triggers left" "${tmp_expected_stdout}" + +remove_trigger "ABC" + +cat > "${tmp_expected_stdout}" <<- EOF +- id: T1 + condition: event rule hit + rule: * (type: tracepoint, domain: ust) + actions: + notify +EOF +list_triggers "one trigger left" "${tmp_expected_stdout}" + +remove_trigger "T1" + +list_triggers "no triggers left" "/dev/null" + +# Cleanup +stop_lttng_sessiond_notap +rm -f "${tmp_stdout}" +rm -f "${tmp_stderr}" +rm -f "${tmp_expected_stdout}" diff --git a/tests/regression/tools/notification/test_notification_kernel b/tests/regression/tools/trigger/test_trigger_kernel similarity index 72% rename from tests/regression/tools/notification/test_notification_kernel rename to tests/regression/tools/trigger/test_trigger_kernel index 81035b831..cc6fc5816 100755 --- a/tests/regression/tools/notification/test_notification_kernel +++ b/tests/regression/tools/trigger/test_trigger_kernel @@ -1,8 +1,19 @@ #!/bin/bash # -# Copyright (C) 2017 Jonathan Rajotte-Julien +# Copyright (C) - 2017 Jonathan Rajotte-Julien # -# SPDX-License-Identifier: LGPL-2.1-only +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA CURDIR=$(dirname $0)/ TESTDIR=$CURDIR/../../../ @@ -12,6 +23,7 @@ TMPDIR=$(mktemp -d) #This is needed since the testpoint create a pipe with the consumerd type suffixed TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOIT_ARGS="CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LTTNG_TESTPOINT_ENABLE=1" TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) @@ -34,7 +46,7 @@ NUM_TESTS=104 source $TESTDIR/utils/utils.sh -function kernel_event_generator_toggle_state +function kernel_event_generator_toogle_state { kernel_event_generator_suspended=$((kernel_event_generator_suspended==0)) @@ -43,7 +55,7 @@ function kernel_event_generator { state_file=$1 kernel_event_generator_suspended=0 - trap kernel_event_generator_toggle_state SIGUSR1 + trap kernel_event_generator_toogle_state SIGUSR1 while (true); do if [[ $kernel_event_generator_suspended -eq "1" ]]; then diff --git a/tests/regression/tools/notification/test_notification_ust b/tests/regression/tools/trigger/test_trigger_ust similarity index 67% rename from tests/regression/tools/notification/test_notification_ust rename to tests/regression/tools/trigger/test_trigger_ust index ad8b0b021..82f79a8e6 100755 --- a/tests/regression/tools/notification/test_notification_ust +++ b/tests/regression/tools/trigger/test_trigger_ust @@ -1,8 +1,19 @@ #!/bin/bash # -# Copyright (C) 2017 Jonathan Rajotte-Julien +# Copyright (C) - 2017 Jonathan Rajotte-Julien # -# SPDX-License-Identifier: LGPL-2.1-only +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA CURDIR=$(dirname $0)/ TESTDIR=$CURDIR/../../../ @@ -12,6 +23,7 @@ TMPDIR=$(mktemp -d) #This is needed since the testpoint create a pipe with the consumerd type suffixed TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOIT_ARGS="CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LTTNG_TESTPOINT_ENABLE=1" TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so) @@ -34,7 +46,7 @@ DIR=$(readlink -f $TESTDIR) source $TESTDIR/utils/utils.sh -function ust_event_generator_toggle_state +function ust_event_generator_toogle_state { ust_event_generator_suspended=$((ust_event_generator_suspended==0)) @@ -43,7 +55,7 @@ function ust_event_generator { state_file=$1 ust_event_generator_suspended=0 - trap ust_event_generator_toggle_state SIGUSR1 + trap ust_event_generator_toogle_state SIGUSR1 while (true); do if [[ $ust_event_generator_suspended -eq "1" ]]; then @@ -53,7 +65,7 @@ function ust_event_generator if [[ -f $state_file ]]; then rm -rf $state_file 2> /dev/null fi - taskset -c 0 $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1 + taskset -c 0 $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT > /dev/null 2>&1 fi done } diff --git a/tests/regression/tools/trigger/trigger.c b/tests/regression/tools/trigger/trigger.c new file mode 100644 index 000000000..88ef99d48 --- /dev/null +++ b/tests/regression/tools/trigger/trigger.c @@ -0,0 +1,729 @@ +/* + * notification.c + * + * Tests suite for LTTng notification API + * + * Copyright (C) 2017 Jonathan Rajotte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NUM_TESTS 104 + +int nb_args = 0; +int named_pipe_args_start = 0; +pid_t app_pid = -1; +const char *app_state_file = NULL; + +static +void wait_on_file(const char *path, bool file_exist) +{ + if (!path) { + return; + } + for (;;) { + int ret; + struct stat buf; + + ret = stat(path, &buf); + if (ret == -1 && errno == ENOENT) { + if (file_exist) { + (void) poll(NULL, 0, 10); /* 10 ms delay */ + continue; /* retry */ + } + break; /* File does not exist */ + } + if (ret) { + perror("stat"); + exit(EXIT_FAILURE); + } + break; /* found */ + } +} + +int write_pipe(const char *path, uint8_t data) +{ + int ret = 0; + int fd = 0; + + fd = open(path, O_WRONLY | O_NONBLOCK); + if (fd < 0) { + perror("Could not open consumer control named pipe"); + goto end; + } + + ret = write(fd, &data , sizeof(data)); + if (ret < 1) { + perror("Named pipe write failed"); + if (close(fd)) { + perror("Named pipe close failed"); + } + ret = -1; + goto end; + } + + ret = close(fd); + if (ret < 0) { + perror("Name pipe closing failed"); + ret = -1; + goto end; + } +end: + return ret; +} + +int stop_consumer(const char **argv) +{ + int ret = 0; + for (int i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 49); + } + return ret; +} + +int resume_consumer(const char **argv) +{ + int ret = 0; + for (int i = named_pipe_args_start; i < nb_args; i++) { + ret = write_pipe(argv[i], 0); + } + return ret; +} + +int suspend_application() +{ + int ret; + struct stat buf; + + if (!stat(app_state_file, &buf)) { + fail("App is already in a suspended state."); + ret = -1; + goto error; + } + + /* + * Send SIGUSR1 to application instructing it to bypass tracepoint. + */ + ret = kill(app_pid, SIGUSR1); + if (ret) { + fail("SIGUSR1 failed. errno %d", errno); + ret = -1; + goto error; + } + + wait_on_file(app_state_file, true); + +error: + return ret; + +} + +int resume_application() +{ + int ret; + struct stat buf; + + ret = stat(app_state_file, &buf); + if (ret == -1 && errno == ENOENT) { + fail("State file does not exist"); + goto error; + } + if (ret) { + perror("stat"); + goto error; + } + + ret = kill(app_pid, SIGUSR1); + if (ret) { + fail("SIGUSR1 failed. errno %d", errno); + ret = -1; + goto error; + } + + wait_on_file(app_state_file, false); + +error: + return ret; + +} + + +void test_triggers_buffer_usage_condition(const char *session_name, + const char *channel_name, + enum lttng_domain_type domain_type, + enum lttng_condition_type condition_type) +{ + enum lttng_condition_status condition_status; + struct lttng_action *action; + + /* Set-up */ + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + /* Test lttng_register_trigger with null value */ + ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected"); + + /* Test: register a trigger */ + unsigned int test_vector_size = 5; + for (unsigned int i = 0; i < pow(2,test_vector_size); i++) { + int loop_ret = 0; + char *test_tuple_string = NULL; + unsigned int mask_position = 0; + bool session_name_set = false; + bool channel_name_set = false; + bool threshold_ratio_set = false; + bool threshold_byte_set = false; + bool domain_type_set = false; + + struct lttng_trigger *trigger = NULL; + struct lttng_condition *condition = NULL; + + /* Create base condition */ + switch (condition_type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + condition = lttng_condition_buffer_usage_low_create(); + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + condition = lttng_condition_buffer_usage_high_create(); + break; + default: + loop_ret = 1; + goto loop_end; + } + + if (!condition) { + loop_ret = 1; + goto loop_end; + + } + + /* Prepare the condition for trigger registration test */ + + /* Set session name */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_session_name( + condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + session_name_set = true; + } + mask_position++; + + /* Set channel name */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_channel_name( + condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + channel_name_set = true; + } + mask_position++; + + /* Set threshold ratio */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + condition, 0.0); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + threshold_ratio_set = true; + } + mask_position++; + + /* Set threshold byte */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_threshold( + condition, 0); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + threshold_byte_set = true; + } + mask_position++; + + /* Set domain type */ + if ((1 << mask_position) & i) { + condition_status = lttng_condition_buffer_usage_set_domain_type( + condition, LTTNG_DOMAIN_UST); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + loop_ret = 1; + goto loop_end; + } + domain_type_set = true; + } + + /* Safety check */ + if (mask_position != test_vector_size -1) { + assert("Logic error for test vector generation"); + } + + loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s", + session_name_set ? "set" : "unset", + channel_name_set ? "set" : "unset", + threshold_ratio_set ? "set" : "unset", + threshold_byte_set ? "set" : "unset", + domain_type_set? "set" : "unset"); + if (!test_tuple_string || loop_ret < 0) { + loop_ret = 1; + goto loop_end; + } + + /* Create trigger */ + trigger = lttng_trigger_create(condition, action); + if (!trigger) { + loop_ret = 1; + goto loop_end; + } + + loop_ret = lttng_register_trigger(trigger); + +loop_end: + if (loop_ret == 1) { + fail("Setup error occurred for tuple: %s", test_tuple_string); + goto loop_cleanup; + } + + /* This combination happens three times */ + if (session_name_set && channel_name_set + && (threshold_ratio_set || threshold_byte_set) + && domain_type_set) { + ok(loop_ret == 0, "Trigger is registered: %s", test_tuple_string); + + /* + * Test that a trigger cannot be registered + * multiple time. + */ + loop_ret = lttng_register_trigger(trigger); + ok(loop_ret == -LTTNG_ERR_TRIGGER_EXISTS, "Re-register trigger fails as expected: %s", test_tuple_string); + + /* Test that a trigger can be unregistered */ + loop_ret = lttng_unregister_trigger(trigger); + ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string); + + /* + * Test that unregistration of a non-previously + * registered trigger fail. + */ + loop_ret = lttng_unregister_trigger(trigger); + ok(loop_ret == -LTTNG_ERR_TRIGGER_NOT_FOUND, "Unregister of a non-registerd trigger fails as expected: %s", test_tuple_string); + } else { + ok(loop_ret == -LTTNG_ERR_INVALID_TRIGGER, "Trigger is invalid as expected and cannot be registered: %s", test_tuple_string); + } + +loop_cleanup: + free(test_tuple_string); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + } + +end: + lttng_action_destroy(action); +} + +static +void wait_data_pending(const char *session_name) +{ + int ret; + + do { + ret = lttng_data_pending(session_name); + assert(ret >= 0); + } while (ret != 0); +} + +void test_notification_channel(const char *session_name, const char *channel_name, const enum lttng_domain_type domain_type, const char **argv) +{ + int ret = 0; + enum lttng_condition_status condition_status; + enum lttng_notification_channel_status nc_status; + + struct lttng_action *action = NULL; + struct lttng_notification *notification = NULL; + struct lttng_notification_channel *notification_channel = NULL; + struct lttng_trigger *trigger = NULL; + + struct lttng_condition *low_condition = NULL; + struct lttng_condition *high_condition = NULL; + struct lttng_condition *dummy_invalid_condition = NULL; + struct lttng_condition *dummy_condition = NULL; + + double low_ratio = 0.0; + double high_ratio = 0.99; + + /* Set-up */ + action = lttng_action_notify_create(); + if (!action) { + fail("Setup error on action creation"); + goto end; + } + + /* Create a dummy, empty condition for later test */ + dummy_invalid_condition = lttng_condition_buffer_usage_low_create(); + if (!dummy_invalid_condition) { + fail("Setup error on condition creation"); + goto end; + } + + /* Create a valid dummy condition with a ratio of 0.5 */ + dummy_condition = lttng_condition_buffer_usage_low_create(); + if (!dummy_condition) { + fail("Setup error on dummy_condition creation"); + goto end; + + } + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + dummy_condition, 0.5); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + dummy_condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on dummy_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + dummy_condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on dummy_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + dummy_condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on dummy_condition creation"); + goto end; + } + + /* Register a low condition with a ratio */ + low_condition = lttng_condition_buffer_usage_low_create(); + if (!low_condition) { + fail("Setup error on low_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + low_condition, low_ratio); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + low_condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + low_condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + low_condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on low_condition creation"); + goto end; + + } + + /* Register a high condition with a ratio */ + high_condition = lttng_condition_buffer_usage_high_create(); + if (!high_condition) { + fail("Setup error on high_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_threshold_ratio( + high_condition, high_ratio); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + + condition_status = lttng_condition_buffer_usage_set_session_name( + high_condition, session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_channel_name( + high_condition, channel_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + condition_status = lttng_condition_buffer_usage_set_domain_type( + high_condition, domain_type); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + fail("Setup error on high_condition creation"); + goto end; + } + + /* Register the triggers for low and high condition */ + trigger = lttng_trigger_create(low_condition, action); + if (!trigger) { + fail("Setup error on low trigger creation"); + goto end; + } + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on low trigger registration"); + goto end; + } + + lttng_trigger_destroy(trigger); + trigger = NULL; + + trigger = lttng_trigger_create(high_condition, action); + if (!trigger) { + fail("Setup error on high trigger creation"); + goto end; + } + + ret = lttng_register_trigger(trigger); + if (ret) { + fail("Setup error on high trigger registration"); + goto end; + } + + /* Begin testing */ + notification_channel = lttng_notification_channel_create(lttng_session_daemon_notification_endpoint); + ok(notification_channel, "Notification channel object creation"); + if (!notification_channel) { + goto end; + } + + /* Basic error path check */ + nc_status = lttng_notification_channel_subscribe(NULL, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NULL"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, NULL); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NON-NULL, NULL"); + + nc_status = lttng_notification_channel_subscribe(NULL, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NON-NULL"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Subscribing to an invalid condition"); + + nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_invalid_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Unsubscribing from an invalid condition"); + + nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, "Unsubscribing from a valid unknown condition"); + + /* Subscribe a valid low condition */ + nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition"); + + /* Subscribe a valid high condition */ + nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done"); + + /* Wait for notification to happen */ + stop_consumer(argv); + lttng_start_tracing(session_name); + + /* Wait for high notification */ + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK + && notification + && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + /* + * Test that communication still work even if there is notification + * waiting for consumption. + */ + + nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe with pending notification"); + + nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "subscribe with pending notification"); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK + && notification + && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + "Low notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + /* Stop consumer to force a high notification */ + stop_consumer(argv); + resume_application(); + lttng_start_tracing(session_name); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification received after intermediary communication"); + lttng_notification_destroy(notification); + notification = NULL; + + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, + "Low notification received after re-subscription"); + lttng_notification_destroy(notification); + notification = NULL; + + stop_consumer(argv); + resume_application(); + /* Stop consumer to force a high notification */ + lttng_start_tracing(session_name); + + nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification && + lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, + "High notification"); + lttng_notification_destroy(notification); + notification = NULL; + + /* Resume consumer to allow event consumption */ + suspend_application(); + lttng_stop_tracing_no_wait(session_name); + resume_consumer(argv); + wait_data_pending(session_name); + + nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe low condition with pending notification"); + nc_status = lttng_notification_channel_unsubscribe(notification_channel, high_condition); + ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe high condition with pending notification"); + +end: + lttng_notification_channel_destroy(notification_channel); + lttng_trigger_destroy(trigger); + lttng_action_destroy(action); + lttng_condition_destroy(low_condition); + lttng_condition_destroy(high_condition); + lttng_condition_destroy(dummy_invalid_condition); + lttng_condition_destroy(dummy_condition); +} + +int main(int argc, const char *argv[]) +{ + const char *session_name = NULL; + const char *channel_name = NULL; + const char *domain_type_string = NULL; + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + + plan_tests(NUM_TESTS); + + /* Argument 6 and upward are named pipe location for consumerd control */ + named_pipe_args_start = 6; + + if (argc < 7) { + fail("Missing parameter for tests to run %d", argc); + goto error; + } + + nb_args = argc; + + domain_type_string = argv[1]; + session_name = argv[2]; + channel_name = argv[3]; + app_pid = (pid_t) atoi(argv[4]); + app_state_file = argv[5]; + + if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) { + domain_type = LTTNG_DOMAIN_UST; + } + if (!strcmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) { + domain_type = LTTNG_DOMAIN_KERNEL; + } + if (domain_type == LTTNG_DOMAIN_NONE) { + fail("Unknown domain type"); + goto error; + } + + diag("Test trigger for domain %s with buffer_usage_low condition", domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); + diag("Test trigger for domain %s with buffer_usage_high condition", domain_type_string); + test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); + + diag("Test notification channel api for domain %s", domain_type_string); + test_notification_channel(session_name, channel_name, domain_type, argv); +error: + return exit_status(); +} + diff --git a/tests/regression/ust/clock-override/lttng-ust-clock-override-test.c b/tests/regression/ust/clock-override/lttng-ust-clock-override-test.c index b2c20bee3..dace1d99c 100644 --- a/tests/regression/ust/clock-override/lttng-ust-clock-override-test.c +++ b/tests/regression/ust/clock-override/lttng-ust-clock-override-test.c @@ -49,6 +49,7 @@ const char *plugin_description(void) return "Freeze time with 1KHz for regression test"; } +void lttng_ust_clock_plugin_init(void); void lttng_ust_clock_plugin_init(void) { int ret; diff --git a/tests/regression/ust/fork/fork.c b/tests/regression/ust/fork/fork.c index 55c9d49bc..7030a293d 100644 --- a/tests/regression/ust/fork/fork.c +++ b/tests/regression/ust/fork/fork.c @@ -34,7 +34,7 @@ int main(int argc, char **argv, char *env[]) return 1; } if (result == 0) { - char *args[] = { "fork2", NULL }; + char *args[] = { (char *) "fork2", NULL }; tracepoint(ust_tests_fork, after_fork_child, getpid()); diff --git a/tests/regression/ust/getcpu-override/lttng-ust-getcpu-override-test.c b/tests/regression/ust/getcpu-override/lttng-ust-getcpu-override-test.c index f1b707425..c38706311 100644 --- a/tests/regression/ust/getcpu-override/lttng-ust-getcpu-override-test.c +++ b/tests/regression/ust/getcpu-override/lttng-ust-getcpu-override-test.c @@ -18,7 +18,7 @@ static long nprocessors; -int plugin_getcpu(void) +static int plugin_getcpu(void) { /* Generate a sequence based on the number of configurated processor * by using sequence[i] % nb_configured_processors. Where sequence @@ -57,6 +57,7 @@ int plugin_getcpu(void) return ret; } +void lttng_ust_getcpu_plugin_init(void); void lttng_ust_getcpu_plugin_init(void) { int ret; diff --git a/tests/regression/ust/high-throughput/main.c b/tests/regression/ust/high-throughput/main.c index b59c9241f..fab148790 100644 --- a/tests/regression/ust/high-throughput/main.c +++ b/tests/regression/ust/high-throughput/main.c @@ -21,11 +21,11 @@ #define TRACEPOINT_DEFINE #include "tp.h" -void inthandler(int sig) +static void inthandler(int sig) { } -int init_int_handler(void) +static int init_int_handler(void) { int result; struct sigaction act; diff --git a/tests/regression/ust/low-throughput/tp.h b/tests/regression/ust/low-throughput/tp.h index 3fa8c9a2d..3ec3c40c1 100644 --- a/tests/regression/ust/low-throughput/tp.h +++ b/tests/regression/ust/low-throughput/tp.h @@ -14,7 +14,7 @@ #include TRACEPOINT_EVENT(tp, slow, - TP_ARGS(unsigned int, c, char *, thread_name), + TP_ARGS(unsigned int, c, const char *, thread_name), TP_FIELDS( ctf_integer(unsigned int, counter, c) ctf_string(th_name, thread_name) diff --git a/tests/regression/ust/multi-lib/callsites.c b/tests/regression/ust/multi-lib/callsites.c index eaa613708..a67f102b4 100644 --- a/tests/regression/ust/multi-lib/callsites.c +++ b/tests/regression/ust/multi-lib/callsites.c @@ -18,7 +18,9 @@ #define VALUE (-1) #endif -void call_tracepoint(void) { +void call_tracepoint(void); +void call_tracepoint(void) +{ tracepoint(multi, tp, VALUE); } diff --git a/tests/regression/ust/multi-lib/multi-lib-test.c b/tests/regression/ust/multi-lib/multi-lib-test.c index 6a91089df..6ebb13d7a 100644 --- a/tests/regression/ust/multi-lib/multi-lib-test.c +++ b/tests/regression/ust/multi-lib/multi-lib-test.c @@ -15,6 +15,7 @@ #include "callsites.h" #endif +void exec_callsite(); void exec_callsite() { #if HAS_CALLSITES @@ -22,7 +23,7 @@ void exec_callsite() #endif } -void print_list(void) +static void print_list(void) { fprintf(stderr, "Test list (-t X):\n"); fprintf(stderr, "\t0: dlopen() all libraries pass in arguments and execute " @@ -31,7 +32,8 @@ void print_list(void) fprintf(stderr, "\t2: simulate the upgrade of a library containing the callsites using dlopen() and dlclose(). \n"); } -int dl_open_all(int nb_libraries, char **libraries) +#if HAS_CALLSITES +static int dl_open_all(int nb_libraries, char **libraries) { int i, ret = 0; void **handles; @@ -56,12 +58,14 @@ error: free(handles); return ret; } +#endif +#if HAS_CALLSITES /* * Takes 2 paths to libraries, dlopen() the first, trace, dlopen() the second, * and dlclose the first to simulate the upgrade of a library. */ -int upgrade_lib(int nb_libraries, char **libraries) +static int upgrade_lib(int nb_libraries, char **libraries) { int i, ret = 0; void *handles[2]; @@ -92,12 +96,14 @@ int upgrade_lib(int nb_libraries, char **libraries) error: return ret; } +#endif +#if !HAS_CALLSITES /* * Simulate the upgrade of a library containing a callsite. * Receives two libraries containing callsites for the same tracepoint. */ -int upgrade_callsite(int nb_libraries, char **libraries) +static int upgrade_callsite(int nb_libraries, char **libraries) { int ret = 0; void *handles[2]; @@ -159,6 +165,7 @@ int upgrade_callsite(int nb_libraries, char **libraries) error: return ret; } +#endif int main(int argc, const char **argv) { diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 09de39e8c..1b69b1f6f 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -17,11 +17,13 @@ TESTS = test_kernel_data \ test_utils_compat_poll \ test_string_utils \ test_notification \ + test_event_rule \ test_directory_handle \ test_relayd_backward_compat_group_by_session \ ini_config/test_ini_config \ test_fd_tracker \ - test_uuid + test_uuid \ + test_buffer_view LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la @@ -39,7 +41,8 @@ noinst_PROGRAMS = test_uri test_session test_kernel_data \ test_utils_expand_path test_utils_compat_poll \ test_string_utils test_notification test_directory_handle \ test_relayd_backward_compat_group_by_session \ - test_fd_tracker test_uuid + test_fd_tracker test_uuid \ + test_buffer_view test_fd_tracker test_event_rule test_condition if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS += test_ust_data @@ -59,6 +62,7 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) $(top_builddir)/src/bin/lttng-sessiond/kernel.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/ht-cleanup.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/notification-thread.$(OBJEXT) \ + $(top_builddir)/src/bin/lttng-sessiond/action-executor.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/lttng-syscall.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/channel.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/agent.$(OBJEXT) \ @@ -182,6 +186,14 @@ test_string_utils_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSTRINGUTILS) $(DL_LIBS) # Notification api test_notification_SOURCES = test_notification.c test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS) +# +# Event rule api +test_event_rule_SOURCES = test_event_rule.c +test_event_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) + +# Condition api +test_condition_SOURCES = test_condition.c +test_condition_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) # relayd backward compat for groou-by-session utilities test_relayd_backward_compat_group_by_session_SOURCES = test_relayd_backward_compat_group_by_session.c @@ -195,3 +207,7 @@ test_fd_tracker_LDADD = $(LIBTAP) $(LIBFDTRACKER) $(DL_LIBS) -lurcu $(LIBCOMMON) # uuid unit test test_uuid_SOURCES = test_uuid.c test_uuid_LDADD = $(LIBTAP) $(LIBCOMMON) + +# buffer view unit test +test_buffer_view_SOURCES = test_buffer_view.c +test_buffer_view_LDADD = $(LIBTAP) $(LIBCOMMON) diff --git a/tests/unit/test_buffer_view.c b/tests/unit/test_buffer_view.c new file mode 100644 index 000000000..b6c772d64 --- /dev/null +++ b/tests/unit/test_buffer_view.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) - EfficiOS, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by as + * published by the Free Software Foundation; only version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +static const int TEST_COUNT = 5; + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +static void test_validate_string(void) +{ + const char buf[] = {'A', 'l', 'l', 'o', '\0'}; + struct lttng_buffer_view view = lttng_buffer_view_init(buf, 0, 5); + struct lttng_buffer_view view_minus_one = + lttng_buffer_view_init(buf, 0, 4); + + ok1(!lttng_buffer_view_validate_string(&view, buf, 4)); + ok1(lttng_buffer_view_validate_string(&view, buf, 5)); + ok1(!lttng_buffer_view_validate_string(&view, buf, 6)); + + ok1(!lttng_buffer_view_validate_string(&view_minus_one, buf, 4)); + ok1(!lttng_buffer_view_validate_string(&view_minus_one, buf, 5)); +} + +int main(void) +{ + plan_tests(TEST_COUNT); + + test_validate_string(); + + return exit_status(); +} diff --git a/tests/unit/test_condition.c b/tests/unit/test_condition.c new file mode 100644 index 000000000..9ae793fda --- /dev/null +++ b/tests/unit/test_condition.c @@ -0,0 +1,108 @@ +/* + * test_condition.c + * + * Unit tests for the condition API. + * + * Copyright (C) 2019 Jonathan Rajotte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 3 + +void test_condition_event_rule(void) +{ + int ret; + struct lttng_event_rule *tracepoint = NULL; + const struct lttng_event_rule *tracepoint_tmp = NULL; + enum lttng_event_rule_status status; + struct lttng_condition *condition = NULL; + struct lttng_condition *condition_from_buffer = NULL; + enum lttng_condition_status condition_status; + const char *pattern="my_event_*"; + const char *filter="msg_id == 23 && size >= 2048"; + const char *exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"}; + struct lttng_dynamic_buffer buffer; + struct lttng_buffer_view view; + + lttng_dynamic_buffer_init(&buffer); + + tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_UST); + ok(tracepoint, "tracepoint UST_DOMAIN"); + + status = lttng_event_rule_tracepoint_set_pattern(tracepoint, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern"); + + status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter"); + + status = lttng_event_rule_tracepoint_set_loglevel_range(tracepoint, LTTNG_LOGLEVEL_WARNING); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel"); + + status = lttng_event_rule_tracepoint_set_exclusions(tracepoint, 3, exclusions); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting exclusions"); + + condition = lttng_condition_event_rule_create(tracepoint); + ok(condition, "created condition"); + + condition_status = lttng_condition_event_rule_get_rule(condition, &tracepoint_tmp); + ok(condition_status == LTTNG_CONDITION_STATUS_OK, "getting event rule"); + ok(tracepoint == tracepoint_tmp, "Ownership transfer is good"); + + ret = lttng_condition_serialize(condition, &buffer, NULL); + ok(ret == 0, "Condition serialized"); + + view = lttng_buffer_view_from_dynamic_buffer(&buffer, 0, -1); + (void) lttng_condition_create_from_buffer(&view, &condition_from_buffer); + ok(condition_from_buffer, "condition from buffer is non null"); + + ok(lttng_condition_is_equal(condition, condition_from_buffer), "serialized and from buffer are equal"); + + lttng_dynamic_buffer_reset(&buffer); + lttng_condition_destroy(condition); + lttng_condition_destroy(condition_from_buffer); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_condition_event_rule(); + return exit_status(); +} diff --git a/tests/unit/test_directory_handle.c b/tests/unit/test_directory_handle.c index f3213cb74..7bbb648d1 100644 --- a/tests/unit/test_directory_handle.c +++ b/tests/unit/test_directory_handle.c @@ -126,7 +126,8 @@ end: } /* Remove "file1" from the test folder hierarchy. */ -int remove_file_from_hierarchy(struct lttng_directory_handle *test_dir_handle, +static int remove_file_from_hierarchy( + struct lttng_directory_handle *test_dir_handle, const char *test_root_name) { int ret; diff --git a/tests/unit/test_event_rule.c b/tests/unit/test_event_rule.c new file mode 100644 index 000000000..590364518 --- /dev/null +++ b/tests/unit/test_event_rule.c @@ -0,0 +1,152 @@ +/* + * test_event_rule.c + * + * Unit tests for the notification API. + * + * Copyright (C) 2019 Jonathan Rajotte + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +/* For error.h */ +int lttng_opt_quiet = 1; +int lttng_opt_verbose; +int lttng_opt_mi; + +#define NUM_TESTS 31 + +void test_event_rule_tracepoint_ust(void) +{ + int ret; + unsigned int count; + struct lttng_event_rule *tracepoint = NULL; + struct lttng_event_rule *tracepoint_from_buffer = NULL; + enum lttng_event_rule_status status; + enum lttng_domain_type domain_type; + enum lttng_loglevel_type loglevel_type; + const char *pattern="my_event_*"; + const char *filter="msg_id == 23 && size >= 2048"; + const char *tmp; + const char *exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"}; + struct lttng_dynamic_buffer buffer; + struct lttng_buffer_view view; + + lttng_dynamic_buffer_init(&buffer); + + tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_UST); + ok(tracepoint, "tracepoint UST_DOMAIN"); + + status = lttng_event_rule_tracepoint_get_domain_type(tracepoint, &domain_type); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get tracepoint domain"); + ok(domain_type == LTTNG_DOMAIN_UST, "domain is UST"); + + status = lttng_event_rule_tracepoint_set_pattern(tracepoint, pattern); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern"); + status = lttng_event_rule_tracepoint_get_pattern(tracepoint, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern"); + ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal"); + + status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter"); + status = lttng_event_rule_tracepoint_get_filter(tracepoint, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter"); + ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal"); + + status = lttng_event_rule_tracepoint_set_loglevel_all(tracepoint); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting all loglevel"); + status = lttng_event_rule_tracepoint_get_loglevel_type(tracepoint, &loglevel_type); + ok(loglevel_type == LTTNG_EVENT_LOGLEVEL_ALL, "getting loglevel type all"); + status = lttng_event_rule_tracepoint_get_loglevel(tracepoint, &ret); + ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset loglevel value"); + + status = lttng_event_rule_tracepoint_set_loglevel(tracepoint, LTTNG_LOGLEVEL_INFO); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting single loglevel"); + status = lttng_event_rule_tracepoint_get_loglevel_type(tracepoint, &loglevel_type); + ok(loglevel_type == LTTNG_EVENT_LOGLEVEL_SINGLE, "getting loglevel type single"); + status = lttng_event_rule_tracepoint_get_loglevel(tracepoint, &ret); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get loglevel value"); + ok(ret == LTTNG_LOGLEVEL_INFO, "loglevel value is equal"); + + status = lttng_event_rule_tracepoint_set_loglevel_range(tracepoint, LTTNG_LOGLEVEL_WARNING); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel"); + status = lttng_event_rule_tracepoint_get_loglevel_type(tracepoint, &loglevel_type); + ok(loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE, "getting loglevel type range"); + status = lttng_event_rule_tracepoint_get_loglevel(tracepoint, &ret); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get loglevel value"); + ok(ret == LTTNG_LOGLEVEL_WARNING, "loglevel valuei is equal"); + + status = lttng_event_rule_tracepoint_set_exclusions(tracepoint, 3, exclusions); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting exclusions"); + + status = lttng_event_rule_tracepoint_get_exclusions_count(tracepoint, &count); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting exclusion count"); + ok(count == 3, "count is %d/3", count); + + for (int i = 0; i < count; i++) { + status = lttng_event_rule_tracepoint_get_exclusion_at_index(tracepoint, i, &tmp); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting exclusion at index %d", i); + ok(!strncmp(exclusions[i], tmp, strlen(exclusions[i])), "%s == %s", tmp, exclusions[i]); + } + + lttng_event_rule_serialize(tracepoint, &buffer, NULL); + view = lttng_buffer_view_from_dynamic_buffer(&buffer, 0, -1); + lttng_event_rule_create_from_buffer(&view, &tracepoint_from_buffer); + + ok(lttng_event_rule_is_equal(tracepoint, tracepoint_from_buffer), "serialized and from buffer are equal"); + + + + + lttng_dynamic_buffer_reset(&buffer); + lttng_event_rule_destroy(tracepoint); + lttng_event_rule_destroy(tracepoint_from_buffer); +} + +void test_event_rule_tracepoint(void) +{ + struct lttng_event_rule *tracepoint = NULL; + + diag("Testing lttng_event_rule_tracepoint"); + tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_NONE); + ok(!tracepoint, "Domain type restriction on create"); + + test_event_rule_tracepoint_ust(); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_event_rule_tracepoint(); + return exit_status(); +} diff --git a/tests/unit/test_fd_tracker.c b/tests/unit/test_fd_tracker.c index cb192ff80..928f3755a 100644 --- a/tests/unit/test_fd_tracker.c +++ b/tests/unit/test_fd_tracker.c @@ -76,7 +76,7 @@ void get_temporary_directories(char **_test_directory, char **_unlink_directory) } } -int fd_count(void) +static int fd_count(void) { DIR *dir; struct dirent *entry; diff --git a/tests/unit/test_relayd_backward_compat_group_by_session.c b/tests/unit/test_relayd_backward_compat_group_by_session.c index 769f8809e..d2c0573ae 100644 --- a/tests/unit/test_relayd_backward_compat_group_by_session.c +++ b/tests/unit/test_relayd_backward_compat_group_by_session.c @@ -18,12 +18,12 @@ #define NUM_TESTS_PER_TEST 1 struct test { - char *stream_path; - char *session_name; - char *hostname; - char *creation_time; - char *extra_path; - char *leftover; + const char *stream_path; + const char *session_name; + const char *hostname; + const char *creation_time; + const char *extra_path; + const char *leftover; bool is_valid; }; diff --git a/tests/unit/test_session.c b/tests/unit/test_session.c index 94e93cd80..2103a8f46 100644 --- a/tests/unit/test_session.c +++ b/tests/unit/test_session.c @@ -66,7 +66,7 @@ static char *get_random_string(void) /* * Return 0 if session name is found, else -1 */ -static int find_session_name(char *name) +static int find_session_name(const char *name) { struct ltt_session *iter; @@ -110,7 +110,7 @@ static void empty_session_list(void) /* * Test creation of 1 session */ -static int create_one_session(char *name) +static int create_one_session(const char *name) { int ret; enum lttng_error_code ret_code; diff --git a/tests/unit/test_utils_expand_path.c b/tests/unit/test_utils_expand_path.c index e6f5e0c8b..d7e7f2b05 100644 --- a/tests/unit/test_utils_expand_path.c +++ b/tests/unit/test_utils_expand_path.c @@ -25,19 +25,19 @@ int lttng_opt_verbose = 3; int lttng_opt_mi; struct valid_test_input { - char *input; - char *relative_part; - char *absolute_part; + const char *input; + const char *relative_part; + const char *absolute_part; }; struct tree_symlink { - char *orig; - char *dest; + const char *orig; + const char *dest; }; struct symlink_test_input { - char *input; - char *expected_result; + const char *input; + const char *expected_result; }; /* Valid test cases */ diff --git a/tests/unit/test_utils_parse_size_suffix.c b/tests/unit/test_utils_parse_size_suffix.c index 27dbf2c9a..4340a69ec 100644 --- a/tests/unit/test_utils_parse_size_suffix.c +++ b/tests/unit/test_utils_parse_size_suffix.c @@ -19,7 +19,7 @@ int lttng_opt_verbose = 3; int lttng_opt_mi; struct valid_test_input { - char *input; + const char *input; uint64_t expected_result; }; @@ -69,7 +69,7 @@ static struct valid_test_input valid_tests_inputs[] = { static const int num_valid_tests = sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]); /* Invalid test cases */ -static char *invalid_tests_inputs[] = { +static const char *invalid_tests_inputs[] = { "", " ", "-1", diff --git a/tests/unit/test_utils_parse_time_suffix.c b/tests/unit/test_utils_parse_time_suffix.c index 09708edd0..6a1c3280b 100644 --- a/tests/unit/test_utils_parse_time_suffix.c +++ b/tests/unit/test_utils_parse_time_suffix.c @@ -20,7 +20,7 @@ int lttng_opt_verbose = 3; int lttng_opt_mi; struct valid_test_input { - char *input; + const char *input; uint64_t expected_result; }; @@ -56,7 +56,7 @@ static struct valid_test_input valid_tests_inputs[] = { static const int num_valid_tests = sizeof(valid_tests_inputs) / sizeof(valid_tests_inputs[0]); /* Invalid test cases */ -static char *invalid_tests_inputs[] = { +static const char *invalid_tests_inputs[] = { "", " ", "-1", diff --git a/tests/utils/tap/tap.c b/tests/utils/tap/tap.c index 9c967f82d..7395f6fc5 100644 --- a/tests/utils/tap/tap.c +++ b/tests/utils/tap/tap.c @@ -40,7 +40,7 @@ static unsigned int test_count = 0; /* Number of tests that have been run */ static unsigned int e_tests = 0; /* Expected number of tests to run */ static unsigned int failures = 0; /* Number of tests that failed */ static char *todo_msg = NULL; -static char *todo_msg_fixed = "libtap malloc issue"; +static const char *todo_msg_fixed = "libtap malloc issue"; static int todo = 0; static int test_died = 0; @@ -68,8 +68,8 @@ static void _cleanup(void); * test_comment -- a comment to print afterwards, may be NULL */ unsigned int -_gen_result(int ok, const char *func, char *file, unsigned int line, - char *test_name, ...) +_gen_result(int ok, const char *func, const char *file, unsigned int line, + const char *test_name, ...) { va_list ap; char *local_test_name = NULL; @@ -270,7 +270,7 @@ plan_tests(unsigned int tests) } unsigned int -diag(char *fmt, ...) +diag(const char *fmt, ...) { va_list ap; @@ -294,7 +294,7 @@ _expected_tests(unsigned int tests) } int -skip(unsigned int n, char *fmt, ...) +skip(unsigned int n, const char *fmt, ...) { va_list ap; char *skip_msg = NULL; diff --git a/tests/utils/tap/tap.h b/tests/utils/tap/tap.h index 045499a98..ab9aad1aa 100644 --- a/tests/utils/tap/tap.h +++ b/tests/utils/tap/tap.h @@ -75,15 +75,15 @@ #define skip_end() } while(0); -unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...); +unsigned int _gen_result(int, const char *, const char *, unsigned int, const char *, ...); int plan_no_plan(void); int plan_skip_all(char *); int plan_tests(unsigned int); -unsigned int diag(char *, ...); +unsigned int diag(const char *, ...); -int skip(unsigned int, char *, ...); +int skip(unsigned int, const char *, ...); void todo_start(char *, ...); void todo_end(void); diff --git a/tests/utils/testapp/gen-syscall-events-callstack/Makefile.am b/tests/utils/testapp/gen-syscall-events-callstack/Makefile.am index 81800a50a..3b10c274d 100644 --- a/tests/utils/testapp/gen-syscall-events-callstack/Makefile.am +++ b/tests/utils/testapp/gen-syscall-events-callstack/Makefile.am @@ -10,7 +10,7 @@ AM_CFLAGS += -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer # available, we assume that the PIE is not enabled by default so we do not # need to disable it. if LINKER_HAVE_NO_PIE_OPTION -AM_CFLAGS += -no-pie +AM_LDFLAGS += -no-pie endif noinst_PROGRAMS = gen-syscall-events-callstack diff --git a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c index ead6fe387..29472572f 100644 --- a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c +++ b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c @@ -13,6 +13,41 @@ #include "utils.h" #define MAX_LEN 16 + +static +int open_read_close(const char *path) +{ + int fd, ret; + char buf[MAX_LEN]; + /* + * Start generating syscalls. We use syscall(2) to prevent libc to change + * the underlying syscall. e.g. calling openat(2) instead of open(2). + */ + fd = syscall(SYS_openat, AT_FDCWD, path, O_RDONLY); + if (fd < 0) { + perror("open"); + ret = -1; + goto error; + } + + ret = syscall(SYS_read, fd, buf, MAX_LEN); + if (ret < 0) { + perror("read"); + ret = -1; + goto error; + } + + ret = syscall(SYS_close, fd); + if (ret == -1) { + perror("close"); + ret = -1; + goto error; + } + +error: + return ret; +} + /* * The process waits for the creation of a file passed as argument from an * external processes to execute a syscall and exiting. This is useful for tests @@ -21,8 +56,7 @@ */ int main(int argc, char **argv) { - int fd, ret; - char buf[MAX_LEN]; + int ret; char *start_file; if (argc != 2) { @@ -47,23 +81,14 @@ int main(int argc, char **argv) * Start generating syscalls. We use syscall(2) to prevent libc to change * the underlying syscall. e.g. calling openat(2) instead of open(2). */ - fd = syscall(SYS_openat, AT_FDCWD, "/proc/cpuinfo", O_RDONLY); - if (fd < 0) { - perror("open"); - ret = -1; - goto error; - } - - ret = syscall(SYS_read, fd, buf, MAX_LEN); - if (ret < 0) { - perror("read"); + ret = open_read_close("/proc/cpuinfo"); + if (ret == -1) { ret = -1; goto error; } - ret = syscall(SYS_close, fd); + ret = open_read_close("/proc/cmdline"); if (ret == -1) { - perror("close"); ret = -1; goto error; } diff --git a/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c b/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c index 52e5f3cb9..329368731 100644 --- a/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c +++ b/tests/utils/testapp/gen-ust-nevents/gen-ust-nevents.c @@ -7,6 +7,7 @@ #define _LGPL_SOURCE #include +#include #include #include #include @@ -21,9 +22,17 @@ #define TRACEPOINT_DEFINE #include "tp.h" +static struct option long_options[] = +{ + /* These options set a flag. */ + {"iter", required_argument, 0, 'i'}, + {"wait", required_argument, 0, 'w'}, + {0, 0, 0, 0} +}; + int main(int argc, char **argv) { - int i, netint, ret = 0; + int i, netint, ret = 0, option_index, option; long values[] = { 1, 2, 3 }; char text[10] = "test"; double dbl = 2.0; @@ -31,19 +40,29 @@ int main(int argc, char **argv) unsigned int nr_iter = 100; useconds_t nr_usec = 0; + while ((option = getopt_long(argc, argv, "i:w:", + long_options, &option_index)) != -1) { + switch (option) { + case 'i': + nr_iter = atoi(optarg); + break; + case 'w': + nr_usec = atoi(optarg); + break; + case '?': + /* getopt_long already printed an error message. */ + break; + default: + ret = -1; + goto end; + } + } + if (set_signal_handler()) { ret = -1; goto end; } - if (argc >= 2) { - nr_iter = atoi(argv[1]); - } - - if (argc == 3) { - /* By default, don't wait unless user specifies. */ - nr_usec = atoi(argv[2]); - } for (i = 0; i < nr_iter; i++) { netint = htonl(i); diff --git a/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.c b/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.c index 09a2c327d..c5e721136 100644 --- a/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.c +++ b/tests/utils/testapp/gen-ust-tracef/gen-ust-tracef.c @@ -23,7 +23,7 @@ const char *str = "test string"; -void create_file(const char *path) +static void create_file(const char *path) { int ret; diff --git a/tests/utils/testapp/userspace-probe-elf-binary/Makefile.am b/tests/utils/testapp/userspace-probe-elf-binary/Makefile.am index aa27a82ed..af76ae7fc 100644 --- a/tests/utils/testapp/userspace-probe-elf-binary/Makefile.am +++ b/tests/utils/testapp/userspace-probe-elf-binary/Makefile.am @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only # no optimization -AM_CFLAGS = -O0 +AM_CFLAGS += -O0 noinst_LTLIBRARIES = libfoo.la libfoo_la_SOURCES = foo.c foo.h diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh index d0f76bc6f..7da125654 100644 --- a/tests/utils/utils.sh +++ b/tests/utils/utils.sh @@ -392,7 +392,7 @@ function start_lttng_relayd_opt() DIR=$(readlink -f "$TESTDIR") - if [ -z $(pgrep $RELAYD_MATCH) ]; then + if [ -z $(pgrep -f $RELAYD_MATCH) ]; then # shellcheck disable=SC2086 $DIR/../src/bin/lttng-relayd/$RELAYD_BIN $process_mode $opt 1> $OUTPUT_DEST 2> $ERROR_OUTPUT_DEST #$DIR/../src/bin/lttng-relayd/$RELAYD_BIN $opt -vvv >>/tmp/relayd.log 2>&1 & @@ -441,7 +441,7 @@ function stop_lttng_relayd_opt() local retval=0 local pids= - pids=$(pgrep "$RELAYD_MATCH") + pids=$(pgrep -f "$RELAYD_MATCH") if [ -z "$pids" ]; then if [ "$withtap" -eq "1" ]; then pass "No relay daemon to kill" @@ -460,7 +460,7 @@ function stop_lttng_relayd_opt() else out=1 while [ -n "$out" ]; do - out=$(pgrep "$RELAYD_MATCH") + out=$(pgrep -f "$RELAYD_MATCH") if [ -n "$dtimeleft_s" ]; then if [ $dtimeleft_s -lt 0 ]; then out= @@ -538,7 +538,7 @@ function start_lttng_sessiond_opt() : "${LTTNG_SESSION_CONFIG_XSD_PATH="${DIR}/../src/common/config/"}" export LTTNG_SESSION_CONFIG_XSD_PATH - if [ -z "$(pgrep "${SESSIOND_MATCH}")" ]; then + if [ -z "$(pgrep -f "${SESSIOND_MATCH}")" ]; then # Have a load path ? if [ -n "$load_path" ]; then # shellcheck disable=SC2086 @@ -590,10 +590,10 @@ function stop_lttng_sessiond_opt() local retval=0 local runas_pids= - runas_pids=$(pgrep "$RUNAS_MATCH") + runas_pids=$(pgrep -f "$RUNAS_MATCH") local pids= - pids=$(pgrep "$SESSIOND_MATCH") + pids=$(pgrep -f "$SESSIOND_MATCH") if [ -n "$runas_pids" ]; then pids="$pids $runas_pids" @@ -617,7 +617,7 @@ function stop_lttng_sessiond_opt() else out=1 while [ -n "$out" ]; do - out=$(pgrep "${SESSIOND_MATCH}") + out=$(pgrep -f "${SESSIOND_MATCH}") if [ -n "$dtimeleft_s" ]; then if [ $dtimeleft_s -lt 0 ]; then out= @@ -629,7 +629,7 @@ function stop_lttng_sessiond_opt() done out=1 while [ -n "$out" ]; do - out=$(pgrep "$CONSUMERD_MATCH") + out=$(pgrep -f "$CONSUMERD_MATCH") if [ -n "$dtimeleft_s" ]; then if [ $dtimeleft_s -lt 0 ]; then out= @@ -683,7 +683,7 @@ function sigstop_lttng_sessiond_opt() return fi - PID_SESSIOND="$(pgrep "${SESSIOND_MATCH}") $(pgrep "$RUNAS_MATCH")" + PID_SESSIOND="$(pgrep -f "${SESSIOND_MATCH}") $(pgrep -f "$RUNAS_MATCH")" if [ "$withtap" -eq "1" ]; then diag "Sending SIGSTOP to lt-$SESSIOND_BIN and $SESSIOND_BIN pids: $(echo "$PID_SESSIOND" | tr '\n' ' ')" @@ -697,7 +697,7 @@ function sigstop_lttng_sessiond_opt() else out=1 while [ $out -ne 0 ]; do - pid="$(pgrep "$SESSIOND_MATCH")" + pid="$(pgrep -f "$SESSIOND_MATCH")" # Wait until state becomes stopped for session # daemon(s). @@ -745,7 +745,7 @@ function stop_lttng_consumerd_opt() local retval=0 - PID_CONSUMERD="$(pgrep "$CONSUMERD_MATCH")" + PID_CONSUMERD="$(pgrep -f "$CONSUMERD_MATCH")" if [ -z "$PID_CONSUMERD" ]; then if [ "$withtap" -eq "1" ]; then @@ -765,7 +765,7 @@ function stop_lttng_consumerd_opt() else out=1 while [ $out -ne 0 ]; do - pid="$(pgrep "$CONSUMERD_MATCH")" + pid="$(pgrep -f "$CONSUMERD_MATCH")" # If consumerds are still present check their status. # A zombie status qualifies the consumerd as *killed* @@ -812,7 +812,7 @@ function sigstop_lttng_consumerd_opt() local withtap=$1 local signal=SIGSTOP - PID_CONSUMERD="$(pgrep "$CONSUMERD_MATCH")" + PID_CONSUMERD="$(pgrep -f "$CONSUMERD_MATCH")" diag "Sending SIGSTOP to $CONSUMERD_BIN pids: $(echo "$PID_CONSUMERD" | tr '\n' ' ')" @@ -828,7 +828,7 @@ function sigstop_lttng_consumerd_opt() else out=1 while [ $out -ne 0 ]; do - pid="$(pgrep "$CONSUMERD_MATCH")" + pid="$(pgrep -f "$CONSUMERD_MATCH")" # Wait until state becomes stopped for all # consumers. -- 2.34.1