From ebdb334b4fdb77857d4bd7224c939f06d5d38f81 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Mon, 8 Mar 2021 15:59:01 -0500 Subject: [PATCH] SoW-2020-0003: Trace Hit Counters Signed-off-by: Francis Deslauriers Change-Id: Id6c9d61d879ac008e1645fcfc283c3fbe6e6eb49 --- .gitignore | 1 + DO_NO_MERGE.txt | 3 + configure.ac | 6 + doc/examples/map/Makefile | 36 + doc/examples/map/demo.sh | 59 + doc/examples/map/instrumented-app.c | 45 + doc/examples/map/query-example.c | 195 + .../map/tracepoint-incr-value-example.c | 10 + .../map/tracepoint-incr-value-example.h | 42 + doc/man/Makefile.am | 6 +- doc/man/lttng-add-map.1.txt | 81 + doc/man/lttng-add-trigger.1.txt | 80 +- doc/man/lttng-disable-map.1.txt | 51 + doc/man/lttng-enable-map.1.txt | 48 + doc/man/lttng-list.1.txt | 11 +- doc/man/lttng-view-map.1.txt | 58 + include/Makefile.am | 98 +- include/lttng/action/action.h | 1 + include/lttng/action/group-internal.h | 5 + include/lttng/action/incr-value-internal.h | 42 + include/lttng/action/incr-value.h | 79 + include/lttng/condition/condition.h | 3 +- include/lttng/condition/evaluation-internal.h | 5 +- include/lttng/condition/event-rule-internal.h | 72 - include/lttng/condition/on-event-internal.h | 113 + .../condition/{event-rule.h => on-event.h} | 53 +- include/lttng/event-rule/event-rule.h | 6 +- .../event-rule/kernel-function-internal.h | 39 + include/lttng/event-rule/kernel-function.h | 75 + ...obe-internal.h => kernel-probe-internal.h} | 14 +- .../event-rule/{kprobe.h => kernel-probe.h} | 27 +- include/lttng/event-rule/syscall.h | 2 + .../lttng/event-rule/tracepoint-internal.h | 15 +- include/lttng/event-rule/tracepoint.h | 52 +- ...-internal.h => userspace-probe-internal.h} | 16 +- .../{uprobe.h => userspace-probe.h} | 27 +- include/lttng/kernel-function-internal.h | 106 + include/lttng/kernel-function.h | 87 + include/lttng/log-level-rule-internal.h | 64 + include/lttng/log-level-rule.h | 89 + include/lttng/lttng-error.h | 14 + include/lttng/lttng.h | 9 +- include/lttng/map-key-internal.h | 118 + include/lttng/map-key.h | 35 + include/lttng/map/map-internal.h | 228 ++ include/lttng/map/map-query-internal.h | 145 + include/lttng/map/map-query.h | 93 + include/lttng/map/map.h | 258 ++ include/lttng/trigger/trigger-internal.h | 9 + src/bin/lttng-sessiond/Makefile.am | 5 +- src/bin/lttng-sessiond/action-executor.c | 26 +- src/bin/lttng-sessiond/agent.c | 29 +- src/bin/lttng-sessiond/buffer-registry.c | 240 ++ src/bin/lttng-sessiond/buffer-registry.h | 47 + src/bin/lttng-sessiond/clear.c | 1 - src/bin/lttng-sessiond/client.c | 219 + src/bin/lttng-sessiond/cmd.c | 999 ++++- src/bin/lttng-sessiond/cmd.h | 16 + src/bin/lttng-sessiond/condition-internal.c | 45 +- src/bin/lttng-sessiond/condition-internal.h | 2 + src/bin/lttng-sessiond/dispatch.c | 3 + .../event-notifier-error-accounting.c | 820 ++++ .../event-notifier-error-accounting.h | 70 + src/bin/lttng-sessiond/event.c | 391 +- src/bin/lttng-sessiond/event.h | 42 + src/bin/lttng-sessiond/kernel.c | 579 ++- src/bin/lttng-sessiond/kernel.h | 20 + src/bin/lttng-sessiond/main.c | 50 +- src/bin/lttng-sessiond/map.c | 442 +++ src/bin/lttng-sessiond/map.h | 45 + src/bin/lttng-sessiond/modprobe.c | 31 +- .../notification-thread-commands.c | 44 +- .../notification-thread-commands.h | 13 + .../notification-thread-events.c | 640 ++- .../notification-thread-internal.h | 14 +- src/bin/lttng-sessiond/notification-thread.c | 20 + src/bin/lttng-sessiond/register.c | 2 +- src/bin/lttng-sessiond/save.c | 222 ++ src/bin/lttng-sessiond/session.h | 1 + src/bin/lttng-sessiond/sessiond-config.c | 1 + src/bin/lttng-sessiond/sessiond-config.h | 2 + src/bin/lttng-sessiond/testpoint.h | 2 + src/bin/lttng-sessiond/trace-kernel.c | 354 +- src/bin/lttng-sessiond/trace-kernel.h | 57 +- src/bin/lttng-sessiond/trace-ust.c | 259 +- src/bin/lttng-sessiond/trace-ust.h | 82 +- src/bin/lttng-sessiond/ust-abi-internal.h | 4 +- src/bin/lttng-sessiond/ust-app.c | 3526 +++++++++++++++-- src/bin/lttng-sessiond/ust-app.h | 139 +- src/bin/lttng-sessiond/ust-consumer.c | 8 +- src/bin/lttng-sessiond/ust-ctl-internal.h | 3 +- src/bin/lttng-sessiond/ust-registry.c | 637 ++- src/bin/lttng-sessiond/ust-registry.h | 123 +- src/bin/lttng/Makefile.am | 7 +- src/bin/lttng/command.h | 4 + src/bin/lttng/commands/add_map.c | 288 ++ src/bin/lttng/commands/add_trigger.c | 373 +- src/bin/lttng/commands/disable_map.c | 175 + src/bin/lttng/commands/enable_map.c | 175 + src/bin/lttng/commands/list.c | 128 + src/bin/lttng/commands/list_triggers.c | 243 +- src/bin/lttng/commands/view_map.c | 651 +++ src/bin/lttng/lttng.c | 8 + src/common/Makefile.am | 15 +- src/common/actions/action.c | 7 + src/common/actions/group.c | 8 + src/common/actions/incr-value.c | 508 +++ src/common/conditions/condition.c | 8 +- .../conditions/{event-rule.c => on-event.c} | 687 +++- src/common/config/config-session-abi.h | 11 + src/common/config/session-config.c | 383 +- src/common/config/session.xsd | 43 + src/common/error.c | 14 + src/common/evaluation.c | 14 +- src/common/event-rule/event-rule.c | 29 +- src/common/event-rule/kernel-function.c | 437 ++ .../event-rule/{kprobe.c => kernel-probe.c} | 188 +- src/common/event-rule/syscall.c | 9 + src/common/event-rule/tracepoint.c | 294 +- .../{uprobe.c => userspace-probe.c} | 189 +- src/common/hashtable/hashtable.c | 21 +- src/common/index-allocator.c | 121 + src/common/index-allocator.h | 36 + src/common/kernel-ctl/kernel-ctl.c | 77 + src/common/kernel-ctl/kernel-ctl.h | 16 + src/common/kernel-ctl/kernel-ioctl.h | 23 + src/common/kernel-function.c | 726 ++++ src/common/log-level-rule.c | 298 ++ src/common/lttng-kernel.h | 126 +- src/common/map-key/map-key.c | 561 +++ src/common/map-query.c | 676 ++++ src/common/map.c | 1235 ++++++ src/common/notification.c | 2 +- src/common/sessiond-comm/sessiond-comm.h | 31 + src/{bin/lttng-sessiond => common}/shm.c | 38 + src/{bin/lttng-sessiond => common}/shm.h | 2 + src/common/trigger.c | 110 +- src/common/ust-consumer/ust-consumer.c | 45 +- src/common/utils.c | 10 + src/common/utils.h | 1 + src/lib/lttng-ctl/lttng-ctl.c | 333 +- tests/regression/Makefile.am | 5 + tests/regression/kernel/Makefile.am | 19 +- tests/regression/kernel/test_kernel_function | 62 + tests/regression/tools/Makefile.am | 2 +- tests/regression/tools/map/Makefile.am | 30 + tests/regression/tools/map/map_base_test.sh | 578 +++ tests/regression/tools/map/test_map_kernel | 322 ++ tests/regression/tools/map/test_map_query.c | 125 + tests/regression/tools/map/test_map_ust | 416 ++ .../regression/tools/notification/Makefile.am | 19 +- .../tools/notification/base_client.c | 52 +- .../tools/notification/notification.c | 851 +++- .../tools/notification/sessiond_testpoints.c | 113 + .../test_notification_kernel_capture | 55 + .../notification/test_notification_multi_app | 11 +- ...test_notification_notifier_discarded_count | 254 ++ .../test_notification_ust_capture | 40 + tests/regression/tools/save-load/Makefile.am | 3 +- .../tools/save-load/load-42-maps.lttng | 67 + tests/regression/tools/save-load/test_load | 23 +- tests/regression/tools/save-load/test_save | 19 +- .../tools/trigger/start-stop/test_start_stop | 49 +- .../tools/trigger/test_add_trigger_cli | 10 +- .../tools/trigger/test_list_triggers_cli | 214 +- .../tools/trigger/test_remove_trigger_cli | 19 +- .../tools/trigger/utils/notification-client.c | 4 +- tests/unit/Makefile.am | 101 +- tests/unit/test_action.c | 120 + tests/unit/test_condition.c | 30 +- tests/unit/test_event_rule.c | 84 +- tests/unit/test_log_level_rule.c | 187 + tests/unit/test_map.c | 439 ++ tests/unit/test_map_key.c | 132 + tests/unit/test_map_query.c | 291 ++ tests/unit/test_ust_data.c | 9 +- .../gen-syscall-events/gen-syscall-events.c | 1 - tests/utils/utils.sh | 115 +- 178 files changed, 25368 insertions(+), 1893 deletions(-) create mode 100644 DO_NO_MERGE.txt create mode 100644 doc/examples/map/Makefile create mode 100755 doc/examples/map/demo.sh create mode 100644 doc/examples/map/instrumented-app.c create mode 100644 doc/examples/map/query-example.c create mode 100644 doc/examples/map/tracepoint-incr-value-example.c create mode 100644 doc/examples/map/tracepoint-incr-value-example.h create mode 100644 doc/man/lttng-add-map.1.txt create mode 100644 doc/man/lttng-disable-map.1.txt create mode 100644 doc/man/lttng-enable-map.1.txt create mode 100644 doc/man/lttng-view-map.1.txt create mode 100644 include/lttng/action/incr-value-internal.h create mode 100644 include/lttng/action/incr-value.h delete mode 100644 include/lttng/condition/event-rule-internal.h create mode 100644 include/lttng/condition/on-event-internal.h rename include/lttng/condition/{event-rule.h => on-event.h} (71%) create mode 100644 include/lttng/event-rule/kernel-function-internal.h create mode 100644 include/lttng/event-rule/kernel-function.h rename include/lttng/event-rule/{kprobe-internal.h => kernel-probe-internal.h} (66%) rename include/lttng/event-rule/{kprobe.h => kernel-probe.h} (74%) rename include/lttng/event-rule/{uprobe-internal.h => userspace-probe-internal.h} (65%) rename include/lttng/event-rule/{uprobe.h => userspace-probe.h} (74%) create mode 100644 include/lttng/kernel-function-internal.h create mode 100644 include/lttng/kernel-function.h create mode 100644 include/lttng/log-level-rule-internal.h create mode 100644 include/lttng/log-level-rule.h create mode 100644 include/lttng/map-key-internal.h create mode 100644 include/lttng/map-key.h create mode 100644 include/lttng/map/map-internal.h create mode 100644 include/lttng/map/map-query-internal.h create mode 100644 include/lttng/map/map-query.h create mode 100644 include/lttng/map/map.h create mode 100644 src/bin/lttng-sessiond/event-notifier-error-accounting.c create mode 100644 src/bin/lttng-sessiond/event-notifier-error-accounting.h create mode 100644 src/bin/lttng-sessiond/map.c create mode 100644 src/bin/lttng-sessiond/map.h create mode 100644 src/bin/lttng/commands/add_map.c create mode 100644 src/bin/lttng/commands/disable_map.c create mode 100644 src/bin/lttng/commands/enable_map.c create mode 100644 src/bin/lttng/commands/view_map.c create mode 100644 src/common/actions/incr-value.c rename src/common/conditions/{event-rule.c => on-event.c} (54%) create mode 100644 src/common/event-rule/kernel-function.c rename src/common/event-rule/{kprobe.c => kernel-probe.c} (66%) rename src/common/event-rule/{uprobe.c => userspace-probe.c} (63%) create mode 100644 src/common/index-allocator.c create mode 100644 src/common/index-allocator.h create mode 100644 src/common/kernel-function.c create mode 100644 src/common/log-level-rule.c create mode 100644 src/common/map-key/map-key.c create mode 100644 src/common/map-query.c create mode 100644 src/common/map.c rename src/{bin/lttng-sessiond => common}/shm.c (86%) rename src/{bin/lttng-sessiond => common}/shm.h (85%) create mode 100755 tests/regression/kernel/test_kernel_function create mode 100644 tests/regression/tools/map/Makefile.am create mode 100644 tests/regression/tools/map/map_base_test.sh create mode 100755 tests/regression/tools/map/test_map_kernel create mode 100644 tests/regression/tools/map/test_map_query.c create mode 100755 tests/regression/tools/map/test_map_ust create mode 100644 tests/regression/tools/notification/sessiond_testpoints.c create mode 100755 tests/regression/tools/notification/test_notification_kernel_capture create mode 100755 tests/regression/tools/notification/test_notification_notifier_discarded_count create mode 100755 tests/regression/tools/notification/test_notification_ust_capture create mode 100644 tests/regression/tools/save-load/load-42-maps.lttng create mode 100644 tests/unit/test_action.c create mode 100644 tests/unit/test_log_level_rule.c create mode 100644 tests/unit/test_map.c create mode 100644 tests/unit/test_map_key.c create mode 100644 tests/unit/test_map_query.c diff --git a/.gitignore b/.gitignore index 3c76b61f2..5e2b0a189 100644 --- a/.gitignore +++ b/.gitignore @@ -148,6 +148,7 @@ health_check /tests/unit/test_buffer_view /tests/unit/test_kernel_probe /tests/unit/test_event_expr_to_bytecode +/tests/unit/test_log_level_rule /tests/utils/testapp/gen-ust-nevents-str/gen-ust-nevents-str /tests/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary /tests/utils/testapp/userspace-probe-elf-cxx-binary/userspace-probe-elf-cxx-binary diff --git a/DO_NO_MERGE.txt b/DO_NO_MERGE.txt new file mode 100644 index 000000000..4b2b5fa0e --- /dev/null +++ b/DO_NO_MERGE.txt @@ -0,0 +1,3 @@ +captures +trigger error counter +map+thc diff --git a/configure.ac b/configure.ac index 9a40705af..1edbfadd0 100644 --- a/configure.ac +++ b/configure.ac @@ -247,6 +247,7 @@ LTTNG_PTHREAD_GETNAME_NP # Check if clock_gettime, timer_create, timer_settime, and timer_delete are available in lib rt, and if so, # add -lrt to LIBS AC_CHECK_LIB([rt], [clock_gettime, timer_create, timer_settime, timer_delete]) +AC_CHECK_LIB([m], [floor, log10]) # Checks for dl. AC_CHECK_LIB([dl], [dlopen], [ @@ -425,6 +426,7 @@ _AC_DEFINE_AND_SUBST([DEFAULT_ROTATE_PENDING_TIMER], [500000]) # Command short descriptions _AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_ADD_CONTEXT], [Add context fields to a channel]) +_AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_ADD_MAP], [Add map]) _AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_CREATE], [Create a tracing session]) _AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_CLEAR], [Clear a tracing session]) _AC_DEFINE_QUOTED_AND_SUBST([CMD_DESCR_DESTROY], [Tear down tracing sessions]) @@ -1079,6 +1081,9 @@ AC_SUBST(lttngactionincludedir) lttngconditionincludedir="${includedir}/lttng/condition" AC_SUBST(lttngconditionincludedir) +lttngmapincludedir="${includedir}/lttng/map" +AC_SUBST(lttngmapincludedir) + lttngnotificationincludedir="${includedir}/lttng/notification" AC_SUBST(lttngnotificationincludedir) @@ -1154,6 +1159,7 @@ AC_CONFIG_FILES([ tests/regression/tools/notification/Makefile tests/regression/tools/rotation/Makefile tests/regression/tools/base-path/Makefile + tests/regression/tools/map/Makefile tests/regression/tools/metadata/Makefile tests/regression/tools/tracker/Makefile tests/regression/tools/working-directory/Makefile diff --git a/doc/examples/map/Makefile b/doc/examples/map/Makefile new file mode 100644 index 000000000..83b60a7ee --- /dev/null +++ b/doc/examples/map/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (C) 2021 Francis Deslauriers +# +# SPDX-License-Identifier: MIT + + +LOCAL_CPPFLAGS += -I. -g -O0 +LIBS_INSTRUMENTED_APP = -ldl -llttng-ust +LIBS_QUERY_EXAMPLE = -llttng-ctl +AM_V_P := : + +all: instrumented-app query-example + +tracepoint-incr-value-example.o: tracepoint-incr-value-example.c tracepoint-incr-value-example.h + @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \ + $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \ + $(CFLAGS) -c -o $@ $< + +instrumented-app.o: instrumented-app.c + @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \ + $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \ + $(CFLAGS) -c -o $@ $< + +instrumented-app: instrumented-app.o tracepoint-incr-value-example.o + @if $(AM_V_P); then set -x; else echo " CCLD $@"; fi; \ + $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \ + $(CFLAGS) instrumented-app.o tracepoint-incr-value-example.o $(LIBS_INSTRUMENTED_APP) + +query-example: query-example.c + @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \ + $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \ + $(CFLAGS) -o $@ $< $(LIBS_QUERY_EXAMPLE) + +.PHONY: clean +clean: + rm -f *.o *.a instrumented-app query-example diff --git a/doc/examples/map/demo.sh b/doc/examples/map/demo.sh new file mode 100755 index 000000000..ae4809305 --- /dev/null +++ b/doc/examples/map/demo.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# +# Copyright (C) 2021 Francis Deslauriers +# +# SPDX-License-Identifier: MIT + +SESSION_NAME="incr_value_ex_sess" +MAP_NAME="incr_value_ex_map" +TRIGGER_NAME="incr_value_ex_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 "1. Creating a session" +lttng create $SESSION_NAME +echo "" + +echo "2. Creating a UST map with default configuration" +lttng add-map --userspace $MAP_NAME +lttng list $SESSION_NAME --map=$MAP_NAME +echo "" + +echo "3. Registering a incr-value trigger named \"$TRIGGER_NAME\" for user-space events" +echo " The \"$TRIGGER_NAME\" trigger has 2 distinct \`incr-value\` actions." +lttng add-trigger --id $TRIGGER_NAME \ + --condition on-event -u "incr_value_ex:*" \ + --action incr-value --session $SESSION_NAME --map $MAP_NAME --key 'Total number of events' \ + --action incr-value --session $SESSION_NAME --map $MAP_NAME --key '${PROVIDER_NAME} -> ${EVENT_NAME}' +lttng list-triggers +echo "" + + +echo "4. Start the tracing and run the application for 10 seconds" +lttng start +timeout 10 ./instrumented-app > /dev/null +echo "" + +echo "5. Stop tracing" +lttng stop +echo "" + +echo "6. View the $MAP_NAME map" +lttng view-map $MAP_NAME +echo "" + +echo "7. View only on key of the $MAP_NAME map" +lttng view-map $MAP_NAME --key "incr_value_ex -> event1" +echo "" + + +echo "8. Query a specific value using the C API" +./query-example $SESSION_NAME $MAP_NAME "Total number of events" +echo "" + +lttng destroy -a +lttng remove-trigger $TRIGGER_NAME diff --git a/doc/examples/map/instrumented-app.c b/doc/examples/map/instrumented-app.c new file mode 100644 index 000000000..5c16a5e09 --- /dev/null +++ b/doc/examples/map/instrumented-app.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: MIT + * + */ + +#include "tracepoint-incr-value-example.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_example:my_event1\"\n", + time_str, tv.tv_usec); + + tracepoint(incr_value_ex, event1, i); + if (i % 2 == 0) { + tracepoint(incr_value_ex, event2, i); + } + + if (i % 3 == 0) { + tracepoint(incr_value_ex, event3, i); + } + + sleep(1); + } + return 0; +} diff --git a/doc/examples/map/query-example.c b/doc/examples/map/query-example.c new file mode 100644 index 000000000..d03812cdd --- /dev/null +++ b/doc/examples/map/query-example.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +#define LOG(fmt, ...) printf("# " fmt "\n", ##__VA_ARGS__); +#define ERR(fmt, ...) fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); + +int main(int argc, char *argv[]) { + enum lttng_map_query_status query_status; + enum lttng_error_code ret_code; + enum lttng_map_status map_status; + struct lttng_domain *domains = NULL; + struct lttng_map_content *map_content = NULL; + struct lttng_map_list *map_list = NULL; + const struct lttng_map *map = NULL; + const struct lttng_map_key_value_pair_list *kv_list; + const struct lttng_map_key_value_pair *kv_pair; + unsigned int map_idx, map_count, list_idx, list_count; + int ret, nb_domains; + struct lttng_map_query *map_query = NULL; + struct lttng_handle *handle = NULL; + int64_t value; + const char *session_name, *map_name, *key, *wanted_key = NULL; + + if (argc < 3) { + ERR("Missing argument(s)"); + ERR("Usage: %s SESSION-NAME MAP-NAME [KEY]", argv[0]); + ret = -1; + goto end; + } + + session_name = argv[1]; + map_name = argv[2]; + if (argc > 3) { + wanted_key = argv[3]; + } + + nb_domains = lttng_list_domains(session_name, &domains); + if (nb_domains < 0) { + ret = -1; + goto end; + } + + handle = lttng_create_handle(session_name, &domains[0]); + if (!handle) { + ret = -1; + goto end; + } + + LOG("Listing all maps of the \"%s\" session", session_name); + + ret_code = lttng_list_maps(handle, &map_list); + if (ret_code != LTTNG_OK) { + ERR("Getting list of all maps"); + ret = -1; + goto end; + } + + map_status = lttng_map_list_get_count(map_list, &map_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Getting the number of maps"); + ret = -1; + goto end; + } + + for (map_idx = 0; map_idx < map_count; map_idx++) { + const char *curr_map_name; + const struct lttng_map *curr_map; + curr_map = lttng_map_list_get_at_index(map_list, map_idx); + if (!curr_map) { + ERR("Getting map at index %u", map_idx); + ret = -1; + goto end; + } + + + map_status = lttng_map_get_name(curr_map, &curr_map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Getting the map #%u name", map_idx); + ret = -1; + goto end; + } + + if (strcmp(curr_map_name, map_name) == 0) { + LOG("Found \"%s\" map", map_name); + map = curr_map; + break; + } + } + if (!map) { + ERR("Can't find map \"%s\" in \"%s\" session", map_name, session_name); + ret = -1; + goto end; + } + + LOG("Creating a map query_object"); + map_query = lttng_map_query_create(LTTNG_MAP_QUERY_CONFIG_CPU_ALL, + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL, + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL); + if (!map_query) { + ERR("Creating the map query object"); + ret = -1; + goto end; + } + + LOG("Query option: report values for each CPU individually"); + query_status = lttng_map_query_set_sum_by_cpu(map_query, false); + if (query_status != LTTNG_MAP_QUERY_STATUS_OK){ + ERR("Setting the sum by option"); + ret = -1; + goto end; + } + + if (wanted_key) { + LOG("Query option: filter in only \"%s\" key", wanted_key); + query_status = lttng_map_query_add_key_filter(map_query, + wanted_key); + if (query_status != LTTNG_MAP_QUERY_STATUS_OK) { + ERR("Setting the targeted key"); + ret = -1; + goto end; + } + } + + LOG("Execute query against the \"%s\" map", map_name); + ret_code = lttng_list_map_content(handle, map, map_query, &map_content); + if (ret_code != LTTNG_OK) { + ERR("Executing the query on map"); + ret = -1; + goto end; + } + + map_status = lttng_map_content_get_count(map_content, &list_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Getting the number of key value pair list"); + ret = -1; + goto end; + } + + LOG("Printing query result:"); + for (list_idx = 0; list_idx < list_count; list_idx++) { + unsigned int kv_pair_idx, kv_pair_count; + uint64_t cpu; + + kv_list = lttng_map_content_get_at_index(map_content, list_idx); + if (!kv_list) { + ERR("Getting key value pair list at index 0"); + ret = -1; + goto end; + } + + cpu = lttng_map_key_value_pair_list_get_cpu(kv_list); + + LOG("=== CPU: %"PRIu64" ===", cpu); + + map_status = lttng_map_key_value_pair_list_get_count(kv_list, + &kv_pair_count); + + for (kv_pair_idx = 0; kv_pair_idx < kv_pair_count; kv_pair_idx++) { + kv_pair = lttng_map_key_value_pair_list_get_at_index( + kv_list, kv_pair_idx); + if (!kv_pair) { + ERR("Getting key value pair at index %u", + kv_pair_idx); + ret = -1; + goto end; + } + lttng_map_key_value_pair_get_key(kv_pair, &key); + lttng_map_key_value_pair_get_value(kv_pair, &value); + + LOG("Key: \"%s\", value: %"PRId64, key, value); + } + LOG(); + } + + ret = 0; +end: + lttng_map_query_destroy(map_query); + lttng_destroy_handle(handle); + lttng_map_content_destroy(map_content); + lttng_map_list_destroy(map_list); + return ret; +} diff --git a/doc/examples/map/tracepoint-incr-value-example.c b/doc/examples/map/tracepoint-incr-value-example.c new file mode 100644 index 000000000..e97c02de2 --- /dev/null +++ b/doc/examples/map/tracepoint-incr-value-example.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: MIT + * + */ + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES +#include "tracepoint-incr-value-example.h" diff --git a/doc/examples/map/tracepoint-incr-value-example.h b/doc/examples/map/tracepoint-incr-value-example.h new file mode 100644 index 000000000..2b6b65e93 --- /dev/null +++ b/doc/examples/map/tracepoint-incr-value-example.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER incr_value_ex + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./tracepoint-incr-value-example.h" + +#if !defined(_TRACEPOINT_TRIGGER_EXAMPLE_H) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define _TRACEPOINT_TRIGGER_EXAMPLE_H + +#include + +TRACEPOINT_EVENT(incr_value_ex, event1, + TP_ARGS(int, iteration), + TP_FIELDS( + ctf_integer(uint64_t, iteration, iteration) + ) +) + +TRACEPOINT_EVENT(incr_value_ex, event2, + TP_ARGS(int, iteration), + TP_FIELDS( + ctf_integer(uint64_t, iteration, iteration) + ) +) + +TRACEPOINT_EVENT(incr_value_ex, event3, + TP_ARGS(int, iteration), + TP_FIELDS( + ctf_integer(uint64_t, iteration, iteration) + ) +) + +#endif /* _TRACEPOINT_TRIGGER_EXAMPLE_H */ + +#include diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 5ae6ffbe3..5d5599c40 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -39,7 +39,11 @@ MAN1_NAMES = \ lttng-clear \ lttng-add-trigger \ lttng-remove-trigger \ - lttng-list-triggers + lttng-list-triggers \ + lttng-add-map \ + lttng-enable-map \ + lttng-disable-map \ + lttng-view-map MAN3_NAMES = MAN8_NAMES = lttng-sessiond lttng-relayd diff --git a/doc/man/lttng-add-map.1.txt b/doc/man/lttng-add-map.1.txt new file mode 100644 index 000000000..e2661d3b1 --- /dev/null +++ b/doc/man/lttng-add-map.1.txt @@ -0,0 +1,81 @@ +lttng-add-map(1) +================ +:revdate: 30 Mars 2021 + + +NAME +---- +lttng-add-map - Create LTTng map + + +SYNOPSIS +-------- + +Create a Linux kernel map: +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *add-map* option:--kernel + [option:--session='SESSION'] [--bitness='BITNESS'] + [option:--coalesce-hits] + [option:--may-key-count='MAX_KEY_COUNT'] + 'MAP' + +Create a user space map: +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *add-map* option:--userspace + [option:--session='SESSION'] [--bitness='BITNESS'] + [(option:--per-pid | option:--per-uid)] + [option:--coalesce-hits] + [option:--may-key-count='MAX_KEY_COUNT'] + 'MAP' + + +DESCRIPTION +----------- + +The `lttng add-map` command is used to create maps. + +A map is key-value data structure used to count events occurences. When +creating a map, many parameters related to this data structure can be +fine-tuned. + +When 'MAP' does not name an existing map, a map named +'MAP' is created. + +OPTIONS +------- + +option:-k, option:--kernel:: + Create map in the Linux kernel domain. + +option:-u, option:--userspace:: + Create map in the user space domain. + +option:-s 'SESSION', option:--session='SESSION':: + Create map in the tracing session named 'SESSION' + instead of the current tracing session. + +option:--bitness='BITNESS':: + Set the counter bucket size to BITNESS bits. + +option:--coalesce-hits:: + Coalesces hits from identical event rules. + +option:--per-pid:: + Keep one event counter per process. + +option:--per-uid:: + Keep one event counter for all processes of a single user. + +include::common-cmd-help-options.txt[] + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-enable-map(1), +man:lttng-disable-map(1), +man:lttng-view-map(1), +man:lttng-list(1), +man:lttng-add-trigger(1), +man:lttng(1) diff --git a/doc/man/lttng-add-trigger.1.txt b/doc/man/lttng-add-trigger.1.txt index 393e8c920..b06e865bf 100644 --- a/doc/man/lttng-add-trigger.1.txt +++ b/doc/man/lttng-add-trigger.1.txt @@ -1,6 +1,6 @@ lttng-add-trigger(1) ===================== -:revdate: 17 January 2020 +:revdate: 27 May 2020 NAME @@ -56,6 +56,11 @@ Event rule: `on-event [event rule arguments]`:: - It is not possible to use filter operands that use values from the context. ++ +Fields to capture can be specified with the option:--capture option, followed by +a capture expression. Zero or more captures can be configured. See the +<> section below for more information. + [[actions]] Actions ~~~~~~~ @@ -73,6 +78,11 @@ Notify: *notify*:: Some details about the condition evaluation are sent along with the notification. +Increment value: *incr-value* -s sess-name -m map-name --key KEY_FORMAT:: + This action causes the tracer to increment the value of a counter + in a map. If the map is absent or disabled when the condition is met, + nothing is done. + 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 @@ -96,6 +106,74 @@ Snapshot session: *snapshot-session* session-name:: the given name exist at the time the condition is met, nothing is done. + +[[capture-expr]] +Capture expression +~~~~~~~~~~~~~~~~~~ + +A capture expression can be specified with the option:--capture option when +creating a new on-event condition. If the capture expression corresponds with an +event's field when tracing, the runtime dynamic value corresponding to the +capture expression is captured. + +NOTE: Make sure to **single-quote** the capture expression when running +the command from a shell, as capture expressions typically include +characters having a special meaning for most shells. + +* Supported field types: + - integer, + - unsigned integer, + - floating point value, + - fixed-size array of integers, + - variable-size array of integers (sequence), + - enumeration, + - text string, + - element of any allowing previous type. + +* The dynamic value of an event field is captured by using its name as a C + identifier. ++ +The square bracket notation is available, like in the C +language, to access array/sequence field. +Only a constant, positive integer number can be used within square +brackets. If the index is out of bounds, the capture expression +evaluates to `unavailable`. ++ +An enumeration field's value is an integer. ++ +When the capture's field does not exist, the capture expression +evaluates to `unavailable`. ++ +Examples: `my_field`, `target_cpu`, `seq[7]` + +* The dynamic value of a statically-known context field is captured by + prefixing its name with `$ctx.`. See man:lttng-add-context(1) to get a list of + available contexts. ++ +When the expression's statically-known context field does not exist, +the capture expression evaluates to `unavailable`. ++ +Examples: `$ctx.prio`, `$ctx.preemptible`, +`$ctx.perf:cpu:stalled-cycles-frontend`. ++ +NOTE: The statically-known context field does NOT need to be added using the +man:lttng-add-context(1) command. The statically-known context fields are +always available in the context of triggers. + +* The dynamic value of an application-specific context field is captured by + prefixing its name with `$app.` (follows the format used to add such a context + field with the man:lttng-add-context(1) command). ++ +When the expression's application-specific context field does not exist, +the capture expression evaluates to `unavailable`. ++ +Example: `$app.server:cur_user`. ++ +NOTE: The application-specific context field does NOT need to be added using the +man:lttng-add-context(1) command. The application-specific context fields fields +are always available in the context of triggers. + + OPTIONS ------- diff --git a/doc/man/lttng-disable-map.1.txt b/doc/man/lttng-disable-map.1.txt new file mode 100644 index 000000000..a46a2c4a2 --- /dev/null +++ b/doc/man/lttng-disable-map.1.txt @@ -0,0 +1,51 @@ +lttng-disable-map(1) +==================== +:revdate: 30 Mars 2021 + + +NAME +---- +lttng-disable-map - Disable LTTng map + + +SYNOPSIS +-------- + +Disable an existing map: + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *disable-map* (option:--userspace | option:--kernel) + [option:--session='SESSION'] 'MAP' + + +DESCRIPTION +----------- + +The `lttng disable-map` command is used to disable existing maps. Disabling a +map prevents any further modification of the content of the map by the tracers +until it's enabled again using `lttng-enable-map(1)`. Even when disabled, the +content of the map is available via the `lttng-view-map(1)` command. + +OPTIONS +------- +option:-k, option:--kernel:: + Disable map in the Linux kernel domain. + +option:-u, option:--userspace:: + Disable map in the user space domain. + +option:-s 'SESSION', option:--session='SESSION':: + Disable map in the tracing session named 'SESSION' + instead of the current tracing session. + +include::common-cmd-help-options.txt[] + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-add-map(1), +man:lttng-enable-map(1), +man:lttng-view-map(1), +man:lttng(1) diff --git a/doc/man/lttng-enable-map.1.txt b/doc/man/lttng-enable-map.1.txt new file mode 100644 index 000000000..7b56911b8 --- /dev/null +++ b/doc/man/lttng-enable-map.1.txt @@ -0,0 +1,48 @@ +lttng-enable-map(1) +=================== +:revdate: 30 Mars 2021 + + +NAME +---- +lttng-enable-map - Enable LTTng map + + +SYNOPSIS +-------- + +Enable an existing map: + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *enable-map* (option:--userspace | option:--kernel) + [option:--session='SESSION'] 'MAP' + + +DESCRIPTION +----------- + +The `lttng enable-map` command is used to enable existing maps. + +OPTIONS +------- +option:-k, option:--kernel:: + Enable map in the Linux kernel domain. + +option:-u, option:--userspace:: + Enable map in the user space domain. + +option:-s 'SESSION', option:--session='SESSION':: + Enable map in the tracing session named 'SESSION' + instead of the current tracing session. + +include::common-cmd-help-options.txt[] + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-add-map(1), +man:lttng-disable-map(1), +man:lttng-view-map(1), +man:lttng(1) diff --git a/doc/man/lttng-list.1.txt b/doc/man/lttng-list.1.txt index 0cb19fe48..cbb25137f 100644 --- a/doc/man/lttng-list.1.txt +++ b/doc/man/lttng-list.1.txt @@ -5,7 +5,7 @@ lttng-list(1) NAME ---- -lttng-list - List LTTng tracing sessions, domains, channels, and events +lttng-list - List LTTng tracing sessions, domains, channels, maps, and events SYNOPSIS @@ -26,10 +26,10 @@ List tracing session's domains: [verse] *lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *list* option:--domain 'SESSION' -List tracing session's channels and event rules: +List tracing session's channels, maps, and event rules: [verse] -*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *list* [option:--channel='CHANNEL'] 'SESSION' +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *list* [option:--channel='CHANNEL'] [option:--map='MAP'] 'SESSION' DESCRIPTION @@ -52,7 +52,7 @@ listed event sources. Providing a tracing session name 'SESSION' targets a specific tracing session. If the option:--domain option is used, domains containing at least one channel in the selected tracing session are listed. Otherwise, -all the domains, channels, and event rules of the selected tracing +all the domains, channels, maps, and event rules of the selected tracing session are listed along with its details (trace path, for example), except when the option:--channel option is used to isolate a specific channel by name. @@ -84,6 +84,9 @@ Target option:-c 'CHANNEL', option:--channel='CHANNEL':: Only list the details of the channel named 'CHANNEL'. +option:-m 'MAP', option:--map='MAP':: + Only list the details of the map named 'MAP'. + Listing ~~~~~~~ diff --git a/doc/man/lttng-view-map.1.txt b/doc/man/lttng-view-map.1.txt new file mode 100644 index 000000000..de7e40ee0 --- /dev/null +++ b/doc/man/lttng-view-map.1.txt @@ -0,0 +1,58 @@ +lttng-view-map(1) +================= +:revdate: 30 Mars 2021 + + +NAME +---- +lttng-view-map - View LTTng map + + +SYNOPSIS +-------- + +View the content of a map: + +[verse] +*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *view-map* [(option:--userspace | option:--kernel)] + [option:--session='SESSION'] [option:--key='KEY'] 'MAP' + + +DESCRIPTION +----------- + +The `lttng view-map` command is used to view the content of maps. +This command prints all the key value pairs contained in this map. + +On LTTng-UST maps created with the `--per-pid` option, the content of the map +of every running applications and the aggregated per-pid map. The aggregated +per-pid map contains the per-key-value pair sum of the maps of applications +that exited since the start of the session. + + +OPTIONS +------- +option:-k, option:--kernel:: + View map in the Linux kernel domain. + +option:-u, option:--userspace:: + View map in the user space domain. + +option:-s 'SESSION', option:--session='SESSION':: + View map in the tracing session named 'SESSION' + instead of the current tracing session. + +option:--key='KEY':: + Only show entries for KEY. + +include::common-cmd-help-options.txt[] + +include::common-cmd-footer.txt[] + + +SEE ALSO +-------- +man:lttng-add-map(1), +man:lttng-enable-map(1), +man:lttng-disable-map(1), +man:lttng(1) diff --git a/include/Makefile.am b/include/Makefile.am index b75602b27..9405907c2 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -97,34 +97,40 @@ CLEANFILES = version.i.tmp DISTCLEANFILES = version.i lttnginclude_HEADERS = \ - lttng/health.h \ - lttng/lttng.h \ - lttng/constant.h \ lttng/channel.h \ + lttng/clear-handle.h \ + lttng/clear.h \ + lttng/constant.h \ + lttng/destruction-handle.h \ lttng/domain.h \ - lttng/event.h \ + lttng/endpoint.h \ lttng/event-expr.h \ lttng/event-field-value.h \ + lttng/event.h \ lttng/handle.h \ - lttng/session.h \ - lttng/lttng-error.h \ - lttng/snapshot.h \ - lttng/save.h \ + lttng/health.h \ + lttng/kernel-function.h \ + lttng/kernel-probe.h \ lttng/load.h \ - lttng/endpoint.h \ - lttng/rotation.h \ lttng/location.h \ - lttng/userspace-probe.h \ + lttng/log-level-rule.h \ + lttng/map/map.h \ + lttng/map/map-query.h \ + lttng/map-key.h \ + lttng/lttng-error.h \ + lttng/lttng.h \ + lttng/rotation.h \ + lttng/save.h \ lttng/session-descriptor.h \ - lttng/destruction-handle.h \ - lttng/clear.h \ - lttng/clear-handle.h \ + lttng/session.h \ + lttng/snapshot.h \ lttng/tracker.h \ - lttng/kernel-probe.h + lttng/userspace-probe.h lttngactioninclude_HEADERS= \ lttng/action/action.h \ lttng/action/group.h \ + lttng/action/incr-value.h \ lttng/action/notify.h \ lttng/action/rotate-session.h \ lttng/action/snapshot-session.h \ @@ -134,7 +140,7 @@ lttngactioninclude_HEADERS= \ lttngconditioninclude_HEADERS= \ lttng/condition/condition.h \ lttng/condition/buffer-usage.h \ - lttng/condition/event-rule.h \ + lttng/condition/on-event.h \ lttng/condition/session-consumed-size.h \ lttng/condition/session-rotation.h \ lttng/condition/evaluation.h @@ -143,54 +149,66 @@ lttngnotificationinclude_HEADERS= \ lttng/notification/channel.h \ lttng/notification/notification.h +lttngmapinclude_HEADERS= \ + lttng/map/map.h \ + lttng/map/map-query.h + lttngtriggerinclude_HEADERS= \ lttng/trigger/trigger.h lttngeventruleinclude_HEADERS= \ lttng/event-rule/event-rule.h \ - lttng/event-rule/kprobe.h \ + lttng/event-rule/kernel-function.h \ + lttng/event-rule/kernel-probe.h \ lttng/event-rule/syscall.h \ - lttng/event-rule/uprobe.h \ + lttng/event-rule/userspace-probe.h \ lttng/event-rule/tracepoint.h noinst_HEADERS = \ - lttng/snapshot-internal.h \ - lttng/health-internal.h \ - lttng/save-internal.h \ - lttng/load-internal.h \ lttng/action/action-internal.h \ lttng/action/group-internal.h \ + lttng/action/incr-value-internal.h \ lttng/action/notify-internal.h \ lttng/action/rotate-session-internal.h \ lttng/action/snapshot-session-internal.h \ lttng/action/start-session-internal.h \ lttng/action/stop-session-internal.h \ - lttng/condition/condition-internal.h \ + lttng/channel-internal.h \ lttng/condition/buffer-usage-internal.h \ - lttng/condition/event-rule-internal.h \ - lttng/condition/session-consumed-size-internal.h \ + lttng/condition/condition-internal.h \ lttng/condition/evaluation-internal.h \ + lttng/condition/on-event-internal.h \ + lttng/condition/session-consumed-size-internal.h \ lttng/condition/session-rotation-internal.h \ - lttng/notification/notification-internal.h \ - lttng/trigger/trigger-internal.h \ - lttng/endpoint-internal.h \ - lttng/notification/channel-internal.h \ - lttng/channel-internal.h \ lttng/domain-internal.h \ - lttng/event-internal.h \ + lttng/endpoint-internal.h \ lttng/event-expr-internal.h \ lttng/event-field-value-internal.h \ - lttng/rotate-internal.h \ - lttng/ref-internal.h \ - lttng/location-internal.h \ - lttng/userspace-probe-internal.h \ - lttng/session-internal.h \ - lttng/session-descriptor-internal.h \ - lttng/kernel-probe-internal.h \ + lttng/event-internal.h \ lttng/event-rule/event-rule-internal.h \ - lttng/event-rule/kprobe-internal.h \ + lttng/event-rule/kernel-function-internal.h \ + lttng/event-rule/kernel-probe-internal.h \ lttng/event-rule/syscall-internal.h \ - lttng/event-rule/uprobe-internal.h \ lttng/event-rule/tracepoint-internal.h \ + lttng/event-rule/userspace-probe-internal.h \ + lttng/health-internal.h \ + lttng/kernel-function-internal.h \ + lttng/kernel-probe-internal.h \ + lttng/load-internal.h \ + lttng/location-internal.h \ + lttng/log-level-rule-internal.h \ + lttng/map/map-internal.h \ + lttng/map/map-query-internal.h \ + lttng/map-key-internal.h \ + lttng/notification/channel-internal.h \ + lttng/notification/notification-internal.h \ + lttng/ref-internal.h \ + lttng/rotate-internal.h \ + lttng/save-internal.h \ + lttng/session-descriptor-internal.h \ + lttng/session-internal.h \ + lttng/snapshot-internal.h \ + lttng/trigger/trigger-internal.h \ + lttng/userspace-probe-internal.h \ version.h \ version.i diff --git a/include/lttng/action/action.h b/include/lttng/action/action.h index be7e397d0..ad451f219 100644 --- a/include/lttng/action/action.h +++ b/include/lttng/action/action.h @@ -22,6 +22,7 @@ enum lttng_action_type { LTTNG_ACTION_TYPE_ROTATE_SESSION = 3, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION = 4, LTTNG_ACTION_TYPE_GROUP = 5, + LTTNG_ACTION_TYPE_INCREMENT_VALUE = 6, }; enum lttng_action_status { diff --git a/include/lttng/action/group-internal.h b/include/lttng/action/group-internal.h index cddee55ed..d587ef8c9 100644 --- a/include/lttng/action/group-internal.h +++ b/include/lttng/action/group-internal.h @@ -26,4 +26,9 @@ extern ssize_t lttng_action_group_create_from_payload( struct lttng_payload_view *view, struct lttng_action **group); +LTTNG_HIDDEN +extern struct lttng_action *lttng_action_group_get_mutable_at_index( + struct lttng_action *group, + unsigned int index); + #endif /* LTTNG_ACTION_GROUP_INTERNAL_H */ diff --git a/include/lttng/action/incr-value-internal.h b/include/lttng/action/incr-value-internal.h new file mode 100644 index 000000000..c98105eb0 --- /dev/null +++ b/include/lttng/action/incr-value-internal.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_ACTION_INCR_VALUE_INTERNAL_H +#define LTTNG_ACTION_INCR_VALUE_INTERNAL_H + +#include + +#include + +struct lttng_action; +struct lttng_payload_view; +struct lttng_map_key; + +/* + * Create a "incr-value" action from a payload 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_incr_value_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **action); + +LTTNG_HIDDEN +enum lttng_action_status lttng_action_incr_value_borrow_key_mutable( + const struct lttng_action *action, struct lttng_map_key **key); + +LTTNG_HIDDEN +enum lttng_action_status lttng_action_incr_value_set_tracer_token( + struct lttng_action *action, uint64_t token); + +LTTNG_HIDDEN +uint64_t lttng_action_incr_value_get_tracer_token( + const struct lttng_action *action); + +#endif /* LTTNG_ACTION_INCR_VALUE_INTERNAL_H */ diff --git a/include/lttng/action/incr-value.h b/include/lttng/action/incr-value.h new file mode 100644 index 000000000..40f1e719b --- /dev/null +++ b/include/lttng/action/incr-value.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_ACTION_INCR_VALUE_H +#define LTTNG_ACTION_INCR_VALUE_H + +#include +#include + +struct lttng_action; +struct lttng_map_key; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Create a newly allocated incr-value 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_incr_value_create(void); + +/* + * Set the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + */ +extern enum lttng_action_status lttng_action_incr_value_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_INCREMENT_VALUE. + */ +extern enum lttng_action_status lttng_action_incr_value_get_session_name( + const struct lttng_action *action, const char **session_name); + +/* + * Set the map name of an lttng_action object of type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + */ +extern enum lttng_action_status lttng_action_incr_value_set_map_name( + struct lttng_action *action, const char *map_name); + +/* + * Get the map name of an lttng_action object of type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + */ +extern enum lttng_action_status lttng_action_incr_value_get_map_name( + const struct lttng_action *action, const char **map_name); + +/* + * Set key allocation policy for an lttng_action object type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + * + * The caller retains ownership of the passed lttng_map_key. + */ +extern enum lttng_action_status +lttng_action_incr_value_set_key(struct lttng_action *action, + struct lttng_map_key *key); + +/* + * Get key allocation policy for an lttng_action object type + * LTTNG_ACTION_TYPE_INCREMENT_VALUE. + */ +extern enum lttng_action_status +lttng_action_incr_value_get_key(const struct lttng_action *action, + const struct lttng_map_key **key); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_INCR_VALUE_H */ diff --git a/include/lttng/condition/condition.h b/include/lttng/condition/condition.h index 78a206df3..97be5923d 100644 --- a/include/lttng/condition/condition.h +++ b/include/lttng/condition/condition.h @@ -21,7 +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, + LTTNG_CONDITION_TYPE_ON_EVENT = 105, }; enum lttng_condition_status { @@ -30,6 +30,7 @@ enum lttng_condition_status { LTTNG_CONDITION_STATUS_UNKNOWN = -2, LTTNG_CONDITION_STATUS_INVALID = -3, LTTNG_CONDITION_STATUS_UNSET = -4, + LTTNG_CONDITION_STATUS_UNSUPPORTED = -4, }; /* diff --git a/include/lttng/condition/evaluation-internal.h b/include/lttng/condition/evaluation-internal.h index 15ae4af4d..eaef721fb 100644 --- a/include/lttng/condition/evaluation-internal.h +++ b/include/lttng/condition/evaluation-internal.h @@ -9,6 +9,7 @@ #define LTTNG_EVALUATION_INTERNAL_H #include +#include #include #include #include @@ -38,7 +39,9 @@ void lttng_evaluation_init(struct lttng_evaluation *evaluation, enum lttng_condition_type type); LTTNG_HIDDEN -ssize_t lttng_evaluation_create_from_payload(struct lttng_payload_view *view, +ssize_t lttng_evaluation_create_from_payload( + const struct lttng_condition *condition, + struct lttng_payload_view *view, struct lttng_evaluation **evaluation); LTTNG_HIDDEN diff --git a/include/lttng/condition/event-rule-internal.h b/include/lttng/condition/event-rule-internal.h deleted file mode 100644 index 0f2f3fbf6..000000000 --- a/include/lttng/condition/event-rule-internal.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#ifndef LTTNG_CONDITION_EVENT_RULE_INTERNAL_H -#define LTTNG_CONDITION_EVENT_RULE_INTERNAL_H - -#include -#include -#include -#include -#include - -struct lttng_capture_descriptor { - struct lttng_event_expr *event_expression; - struct lttng_bytecode *bytecode; -}; - -struct lttng_condition_event_rule { - struct lttng_condition parent; - struct lttng_event_rule *rule; - - /* Array of `struct lttng_capture_descriptor *`. */ - struct lttng_dynamic_pointer_array capture_descriptors; -}; - -struct lttng_evaluation_event_rule { - struct lttng_evaluation parent; - char *name; -}; - -struct lttng_evaluation_event_rule_comm { - /* Includes the null terminator. */ - uint32_t trigger_name_length; - /* Trigger name. */ - char payload[]; -} LTTNG_PACKED; - -LTTNG_HIDDEN -ssize_t lttng_condition_event_rule_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **condition); - -LTTNG_HIDDEN -enum lttng_condition_status -lttng_condition_event_rule_borrow_rule_mutable( - 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_payload( - struct lttng_payload_view *view, - struct lttng_evaluation **_evaluation); - -LTTNG_HIDDEN -enum lttng_error_code -lttng_condition_event_rule_generate_capture_descriptor_bytecode( - struct lttng_condition *condition); - -LTTNG_HIDDEN -const struct lttng_bytecode * -lttng_condition_event_rule_get_capture_bytecode_at_index( - const struct lttng_condition *condition, unsigned int index); - -#endif /* LTTNG_CONDITION_EVENT_RULE_INTERNAL_H */ diff --git a/include/lttng/condition/on-event-internal.h b/include/lttng/condition/on-event-internal.h new file mode 100644 index 000000000..e19b50276 --- /dev/null +++ b/include/lttng/condition/on-event-internal.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_CONDITION_ON_EVENT_INTERNAL_H +#define LTTNG_CONDITION_ON_EVENT_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include + +struct lttng_capture_descriptor { + struct lttng_event_expr *event_expression; + struct lttng_bytecode *bytecode; +}; + +struct lttng_condition_on_event { + struct lttng_condition parent; + struct lttng_event_rule *rule; + + LTTNG_OPTIONAL(uint64_t) error_count; + /* + * Internal use only. + * Error accounting counter index. + */ + LTTNG_OPTIONAL(uint64_t) error_counter_index; + + /* Array of `struct lttng_capture_descriptor *`. */ + struct lttng_dynamic_pointer_array capture_descriptors; +}; + +struct lttng_evaluation_event_rule { + struct lttng_evaluation parent; + char *name; + + /* MessagePack-encoded captured event field values. */ + struct lttng_dynamic_buffer capture_payload; + + /* + * The content of this array event field value is the decoded + * version of `capture_payload` above. + * + * This is a cache: it's not serialized/deserialized in + * communications from/to the library and the session daemon. + */ + struct lttng_event_field_value *captured_values; +}; + +struct lttng_evaluation_event_rule_comm { + /* Includes the null terminator. */ + uint32_t trigger_name_length; + /* Trigger name. */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_condition_on_event_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **condition); + +LTTNG_HIDDEN +enum lttng_condition_status +lttng_condition_on_event_borrow_rule_mutable( + const struct lttng_condition *condition, + struct lttng_event_rule **rule); + +LTTNG_HIDDEN +void lttng_condition_on_event_set_error_counter_index( + struct lttng_condition *condition, uint64_t error_counter_index); + +LTTNG_HIDDEN +uint64_t lttng_condition_on_event_get_error_counter_index( + const struct lttng_condition *condition); + +LTTNG_HIDDEN +uint64_t lttng_condition_on_event_get_error_count( + const struct lttng_condition *condition); + +LTTNG_HIDDEN +void lttng_condition_on_event_set_error_count(struct lttng_condition *condition, + uint64_t error_count); + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_event_rule_create( + const struct lttng_condition_on_event *condition, + const char* trigger_name, + const char *capture_payload, size_t capture_payload_size, + bool decode_capture_payload); + +LTTNG_HIDDEN +ssize_t lttng_evaluation_event_rule_create_from_payload( + const struct lttng_condition_on_event *condition, + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation); + +LTTNG_HIDDEN +enum lttng_error_code +lttng_condition_on_event_generate_capture_descriptor_bytecode( + struct lttng_condition *condition); + +LTTNG_HIDDEN +const struct lttng_bytecode * +lttng_condition_on_event_get_capture_bytecode_at_index( + const struct lttng_condition *condition, unsigned int index); + +#endif /* LTTNG_CONDITION_ON_EVENT_INTERNAL_H */ diff --git a/include/lttng/condition/event-rule.h b/include/lttng/condition/on-event.h similarity index 71% rename from include/lttng/condition/event-rule.h rename to include/lttng/condition/on-event.h index 91fce32d6..ca10781ed 100644 --- a/include/lttng/condition/event-rule.h +++ b/include/lttng/condition/on-event.h @@ -5,8 +5,8 @@ * */ -#ifndef LTTNG_CONDITION_EVENT_RULE_H -#define LTTNG_CONDITION_EVENT_RULE_H +#ifndef LTTNG_CONDITION_ON_EVENT_H +#define LTTNG_CONDITION_ON_EVENT_H #include #include @@ -17,6 +17,7 @@ extern "C" { #endif struct lttng_event_expr; +struct lttng_event_field_value; /** * Event rule conditions allows an action to be taken whenever an event matching @@ -35,7 +36,7 @@ struct lttng_event_expr; * 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( +extern struct lttng_condition *lttng_condition_on_event_create( struct lttng_event_rule *rule); /* @@ -48,8 +49,8 @@ extern struct lttng_condition *lttng_condition_event_rule_create( * 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, +extern enum lttng_condition_status lttng_condition_on_event_get_rule( + const struct lttng_condition *condition, const struct lttng_event_rule **rule); /** @@ -74,6 +75,30 @@ lttng_evaluation_event_rule_get_trigger_name( const struct lttng_evaluation *evaluation, const char **name); +/* + * Sets `*field_val` to the array event field value of the event rule + * condition evaluation `evaluation` which contains its captured values. + * + * Returns: + * + * `LTTNG_EVALUATION_STATUS_OK`: + * Success. + * + * `*field_val` is an array event field value with a length of at + * least one. + * + * `LTTNG_EVALUATION_STATUS_INVALID`: + * * `evaluation` is `NULL`. + * * The type of the condition of `evaluation` is not + * `LTTNG_CONDITION_TYPE_ON_EVENT`. + * * The condition of `evaluation` has no capture descriptors. + * * `field_val` is `NULL`. + */ +extern enum lttng_evaluation_status +lttng_evaluation_get_captured_values( + const struct lttng_evaluation *evaluation, + const struct lttng_event_field_value **field_val); + /* * Appends (transfering the ownership) the capture descriptor `expr` to * the event rule condition `condition`. @@ -89,7 +114,7 @@ lttng_evaluation_event_rule_get_trigger_name( * `LTTNG_CONDITION_STATUS_INVALID`: * * `condition` is `NULL`. * * The type of `condition` is not - * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * `LTTNG_CONDITION_TYPE_ON_EVENT`. * * `expr` is `NULL`. * * `expr` is not a locator expression, that is, its type is not * one of: @@ -98,9 +123,11 @@ lttng_evaluation_event_rule_get_trigger_name( * * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD` * * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD` * * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT` + * `LTTNG_CONDITION_STATUS_UNSUPPORTED`: + * * The associated event-rule does not support runtime capture. */ extern enum lttng_condition_status -lttng_condition_event_rule_append_capture_descriptor( +lttng_condition_on_event_append_capture_descriptor( struct lttng_condition *condition, struct lttng_event_expr *expr); @@ -116,11 +143,11 @@ lttng_condition_event_rule_append_capture_descriptor( * `LTTNG_CONDITION_STATUS_INVALID`: * * `condition` is `NULL`. * * The type of `condition` is not - * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * `LTTNG_CONDITION_TYPE_ON_EVENT`. * * `count` is `NULL`. */ extern enum lttng_condition_status -lttng_condition_event_rule_get_capture_descriptor_count( +lttng_condition_on_event_get_capture_descriptor_count( const struct lttng_condition *condition, unsigned int *count); /* @@ -129,17 +156,17 @@ lttng_condition_event_rule_get_capture_descriptor_count( * * * `condition` is `NULL`. * * The type of `condition` is not - * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`. + * `LTTNG_CONDITION_TYPE_ON_EVENT`. * * `index` is greater than or equal to the number of capture * descriptors in `condition` (as returned by - * lttng_condition_event_rule_get_capture_descriptor_count()). + * lttng_condition_on_event_get_capture_descriptor_count()). */ extern const struct lttng_event_expr * -lttng_condition_event_rule_get_capture_descriptor_at_index( +lttng_condition_on_event_get_capture_descriptor_at_index( const struct lttng_condition *condition, unsigned int index); #ifdef __cplusplus } #endif -#endif /* LTTNG_CONDITION_EVENT_RULE_H */ +#endif /* LTTNG_CONDITION_ON_EVENT_H */ diff --git a/include/lttng/event-rule/event-rule.h b/include/lttng/event-rule/event-rule.h index e097dd488..eec673e00 100644 --- a/include/lttng/event-rule/event-rule.h +++ b/include/lttng/event-rule/event-rule.h @@ -18,9 +18,9 @@ enum lttng_event_rule_type { LTTNG_EVENT_RULE_TYPE_UNKNOWN = -1, LTTNG_EVENT_RULE_TYPE_TRACEPOINT = 0, LTTNG_EVENT_RULE_TYPE_SYSCALL = 1, - LTTNG_EVENT_RULE_TYPE_KPROBE = 2, - LTTNG_EVENT_RULE_TYPE_KRETPROBE = 3, - LTTNG_EVENT_RULE_TYPE_UPROBE = 4, + LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE = 2, + LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION = 3, + LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE = 4, }; enum lttng_event_rule_status { diff --git a/include/lttng/event-rule/kernel-function-internal.h b/include/lttng/event-rule/kernel-function-internal.h new file mode 100644 index 000000000..e7a3c380e --- /dev/null +++ b/include/lttng/event-rule/kernel-function-internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_KERNEL_FUNCTION_INTERNAL_H +#define LTTNG_EVENT_RULE_KERNEL_FUNCTION_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_event_rule_kernel_function { + struct lttng_event_rule parent; + char *name; + struct lttng_kernel_function_location *location; +}; + +struct lttng_event_rule_kernel_function_comm { + /* Includes terminator `\0`. */ + uint32_t name_len; + uint32_t location_len; + /* + * Payload is composed of, in that order: + * - name (null terminated), + * - kernel function location object. + */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kernel_function_create_from_payload( + struct lttng_payload_view *payload, + struct lttng_event_rule **rule); + +#endif /* LTTNG_EVENT_RULE_KERNEL_FUNCTION_INTERNAL_H */ diff --git a/include/lttng/event-rule/kernel-function.h b/include/lttng/event-rule/kernel-function.h new file mode 100644 index 000000000..aaa3f755d --- /dev/null +++ b/include/lttng/event-rule/kernel-function.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_EVENT_RULE_KERNEL_FUNCTION_H +#define LTTNG_EVENT_RULE_KERNEL_FUNCTION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_kernel_function_location; + +/* + * Create a newly allocated kernel function event rule. + * + * The location is copied internally. + * + * Returns a new event rule on success, NULL on failure. The returned event rule + * must be destroyed using lttng_event_rule_destroy(). + */ +extern struct lttng_event_rule *lttng_event_rule_kernel_function_create( + const struct lttng_kernel_function_location *location); + +/* + * Get the kernel function location of a kernel function 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_kernel_function_get_location( + const struct lttng_event_rule *rule, + const struct lttng_kernel_function_location **location); + +/* + * Set the name of a kernel function event rule. + * + * The name is copied internally. + * + * Returns 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_kernel_function_set_event_name( + struct lttng_event_rule *rule, const char *name); + +/* + * Get the name of a kernel function 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_kernel_function_get_event_name( + const struct lttng_event_rule *rule, const char **name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVENT_RULE_KERNEL_FUNCTION_H */ diff --git a/include/lttng/event-rule/kprobe-internal.h b/include/lttng/event-rule/kernel-probe-internal.h similarity index 66% rename from include/lttng/event-rule/kprobe-internal.h rename to include/lttng/event-rule/kernel-probe-internal.h index 7d2ecc8cc..50c02d851 100644 --- a/include/lttng/event-rule/kprobe-internal.h +++ b/include/lttng/event-rule/kernel-probe-internal.h @@ -5,21 +5,21 @@ * */ -#ifndef LTTNG_EVENT_RULE_KPROBE_INTERNAL_H -#define LTTNG_EVENT_RULE_KPROBE_INTERNAL_H +#ifndef LTTNG_EVENT_RULE_KERNEL_PROBE_INTERNAL_H +#define LTTNG_EVENT_RULE_KERNEL_PROBE_INTERNAL_H #include #include #include -#include +#include -struct lttng_event_rule_kprobe { +struct lttng_event_rule_kernel_probe { struct lttng_event_rule parent; char *name; struct lttng_kernel_probe_location *location; }; -struct lttng_event_rule_kprobe_comm { +struct lttng_event_rule_kernel_probe_comm { /* Includes terminator `\0`. */ uint32_t name_len; uint32_t location_len; @@ -32,8 +32,8 @@ struct lttng_event_rule_kprobe_comm { } LTTNG_PACKED; LTTNG_HIDDEN -ssize_t lttng_event_rule_kprobe_create_from_payload( +ssize_t lttng_event_rule_kernel_probe_create_from_payload( struct lttng_payload_view *payload, struct lttng_event_rule **rule); -#endif /* LTTNG_EVENT_RULE_KPROBE_INTERNAL_H */ +#endif /* LTTNG_EVENT_RULE_KERNEL_PROBE_INTERNAL_H */ diff --git a/include/lttng/event-rule/kprobe.h b/include/lttng/event-rule/kernel-probe.h similarity index 74% rename from include/lttng/event-rule/kprobe.h rename to include/lttng/event-rule/kernel-probe.h index e3d7563a4..4efd4d394 100644 --- a/include/lttng/event-rule/kprobe.h +++ b/include/lttng/event-rule/kernel-probe.h @@ -5,8 +5,8 @@ * */ -#ifndef LTTNG_EVENT_RULE_KPROBE_H -#define LTTNG_EVENT_RULE_KPROBE_H +#ifndef LTTNG_EVENT_RULE_KERNEL_PROBE_H +#define LTTNG_EVENT_RULE_KERNEL_PROBE_H #include @@ -19,21 +19,12 @@ struct lttng_kernel_probe_location; /* * Create a newly allocated kprobe event rule. * - * Returns a new event rule on success, NULL on failure. The returned event rule - * must be destroyed using lttng_event_rule_destroy(). - */ -extern struct lttng_event_rule *lttng_event_rule_kprobe_create(void); - -/* - * Set the kernel probe location of a kprobe event rule. - * * The location is copied internally. * - * Returns LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID - * if invalid parameters are passed. + * Returns a new event rule on success, NULL on failure. The returned event rule + * must be destroyed using lttng_event_rule_destroy(). */ -extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_location( - struct lttng_event_rule *rule, +extern struct lttng_event_rule *lttng_event_rule_kernel_probe_create( const struct lttng_kernel_probe_location *location); /* @@ -48,7 +39,7 @@ extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_location( * 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_kprobe_get_location( +extern enum lttng_event_rule_status lttng_event_rule_kernel_probe_get_location( const struct lttng_event_rule *rule, const struct lttng_kernel_probe_location **location); @@ -60,7 +51,7 @@ extern enum lttng_event_rule_status lttng_event_rule_kprobe_get_location( * Returns 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( +extern enum lttng_event_rule_status lttng_event_rule_kernel_probe_set_event_name( struct lttng_event_rule *rule, const char *name); /* @@ -74,11 +65,11 @@ extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( * 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( +extern enum lttng_event_rule_status lttng_event_rule_kernel_probe_get_event_name( const struct lttng_event_rule *rule, const char **name); #ifdef __cplusplus } #endif -#endif /* LTTNG_EVENT_RULE_KPROBE_H */ +#endif /* LTTNG_EVENT_RULE_KERNEL_PROBE_H */ diff --git a/include/lttng/event-rule/syscall.h b/include/lttng/event-rule/syscall.h index baf8432c1..c581b809c 100644 --- a/include/lttng/event-rule/syscall.h +++ b/include/lttng/event-rule/syscall.h @@ -17,6 +17,8 @@ extern "C" { /* * Create a newly allocated syscall event rule. * + * The default pattern is '*'. + * * Returns a new event rule on success, NULL on failure. This event rule must be * destroyed using lttng_event_rule_destroy(). */ diff --git a/include/lttng/event-rule/tracepoint-internal.h b/include/lttng/event-rule/tracepoint-internal.h index 227fe6de9..6c4d438d5 100644 --- a/include/lttng/event-rule/tracepoint-internal.h +++ b/include/lttng/event-rule/tracepoint-internal.h @@ -10,10 +10,12 @@ #include #include +#include #include #include #include #include +#include struct lttng_event_rule_tracepoint { struct lttng_event_rule parent; @@ -27,11 +29,8 @@ struct lttng_event_rule_tracepoint { /* Filter. */ char *filter_expression; - /* Loglevel. */ - struct { - enum lttng_loglevel_type type; - int value; - } loglevel; + /* Log level. */ + struct lttng_log_level_rule *log_level_rule; /* Exclusions. */ struct lttng_dynamic_pointer_array exclusions; @@ -46,19 +45,19 @@ struct lttng_event_rule_tracepoint { 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; /* Includes terminator `\0`. */ uint32_t pattern_len; /* Includes terminator `\0`. */ uint32_t filter_expression_len; + /* enum lttng_log_level_rule_comm + payload if any */ + uint32_t log_level_rule_len; uint32_t exclusions_count; uint32_t exclusions_len; /* * Payload is composed of, in that order: * - pattern (null terminated), * - filter expression (null terminated), + * - log level rule serialized object, * - exclusions (32 bit length + null terminated string). */ char payload[]; diff --git a/include/lttng/event-rule/tracepoint.h b/include/lttng/event-rule/tracepoint.h index e24b0fbe1..62c1db38d 100644 --- a/include/lttng/event-rule/tracepoint.h +++ b/include/lttng/event-rule/tracepoint.h @@ -10,6 +10,7 @@ #include #include +#include #include #ifdef __cplusplus @@ -19,6 +20,8 @@ extern "C" { /* * Create a newly allocated tracepoint event rule. * + * The default pattern is '*'. + * * Returns a new event rule on success, NULL on failure. This event rule must be * destroyed using lttng_event_rule_destroy(). */ @@ -90,58 +93,31 @@ extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter( const struct lttng_event_rule *rule, const char **expression); /* - * Set the single log level 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_log_level( - struct lttng_event_rule *rule, int level); - -/* - * Set the log level range lower bound of a tracepoint event rule. + * Set the log level rule 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_log_level_range_lower_bound( - struct lttng_event_rule *rule, int level); +lttng_event_rule_tracepoint_set_log_level_rule(struct lttng_event_rule *rule, + const struct lttng_log_level_rule *log_level_rule); /* - * Set the log level to all of a tracepoint event rule. + * Get the log level rule 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_log_level_all(struct lttng_event_rule *rule); - -/* - * Get the log level type of a tracepoint event rule. + * The caller does not assume the ownership of the returned log level rule. The + * log level rule shall only only be used for the duration of the event rule's + * lifetime, or before a different log level rule is set. * - * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the log level type output + * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the log level rule output * parameter on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter - * is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a log level was not set prior + * is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a log level rule was not set prior * to this call. */ extern enum lttng_event_rule_status -lttng_event_rule_tracepoint_get_log_level_type( +lttng_event_rule_tracepoint_get_log_level_rule( const struct lttng_event_rule *rule, - enum lttng_loglevel_type *type); - -/* - * Get the log level of a tracepoint event rule. - * - * For range log level , the lower bound log level is returned. - * - * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the log level output parameter - * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is - * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a log level was not set prior to - * this call. - */ -extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_log_level( - const struct lttng_event_rule *rule, int *level); + const struct lttng_log_level_rule **log_level_rule); /* * Add an exclusion to the set of exclusion of an event rule. diff --git a/include/lttng/event-rule/uprobe-internal.h b/include/lttng/event-rule/userspace-probe-internal.h similarity index 65% rename from include/lttng/event-rule/uprobe-internal.h rename to include/lttng/event-rule/userspace-probe-internal.h index 6d4b10661..8ba5b2bf2 100644 --- a/include/lttng/event-rule/uprobe-internal.h +++ b/include/lttng/event-rule/userspace-probe-internal.h @@ -5,21 +5,21 @@ * */ -#ifndef LTTNG_EVENT_RULE_UPROBE_INTERNAL_H -#define LTTNG_EVENT_RULE_UPROBE_INTERNAL_H +#ifndef LTTNG_EVENT_RULE_USERSPACE_PROBE_INTERNAL_H +#define LTTNG_EVENT_RULE_USERSPACE_PROBE_INTERNAL_H #include #include #include -#include +#include -struct lttng_event_rule_uprobe { +struct lttng_event_rule_userspace_probe { struct lttng_event_rule parent; char *name; struct lttng_userspace_probe_location *location; }; -struct lttng_event_rule_uprobe_comm { +struct lttng_event_rule_userspace_probe_comm { /* Includes terminator `\0`. */ uint32_t name_len; /* Includes terminator `\0`. */ @@ -33,13 +33,13 @@ struct lttng_event_rule_uprobe_comm { } LTTNG_PACKED; LTTNG_HIDDEN -ssize_t lttng_event_rule_uprobe_create_from_payload( +ssize_t lttng_event_rule_userspace_probe_create_from_payload( struct lttng_payload_view *view, struct lttng_event_rule **rule); LTTNG_HIDDEN struct lttng_userspace_probe_location * -lttng_event_rule_uprobe_get_location_mutable( +lttng_event_rule_userspace_probe_get_location_mutable( const struct lttng_event_rule *rule); -#endif /* LTTNG_EVENT_RULE_UPROBE_INTERNAL_H */ +#endif /* LTTNG_EVENT_RULE_USERSPACE_PROBE_INTERNAL_H */ diff --git a/include/lttng/event-rule/uprobe.h b/include/lttng/event-rule/userspace-probe.h similarity index 74% rename from include/lttng/event-rule/uprobe.h rename to include/lttng/event-rule/userspace-probe.h index 12aa01318..88bc2f43a 100644 --- a/include/lttng/event-rule/uprobe.h +++ b/include/lttng/event-rule/userspace-probe.h @@ -5,8 +5,8 @@ * */ -#ifndef LTTNG_EVENT_RULE_UPROBE_H -#define LTTNG_EVENT_RULE_UPROBE_H +#ifndef LTTNG_EVENT_RULE_USERSPACE_PROBE_H +#define LTTNG_EVENT_RULE_USERSPACE_PROBE_H #include #include @@ -18,21 +18,12 @@ extern "C" { /* * Create a newly allocated uprobe event rule. * - * Returns a new event rule on success, NULL on failure. This event rule must be - * destroyed using lttng_event_rule_destroy(). - */ -extern struct lttng_event_rule *lttng_event_rule_uprobe_create(void); - -/* - * Set the location of a uprobe event rule. - * * The location is copied internally. * - * Returns LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID - * if invalid parameters are passed. + * Returns a new event rule on success, NULL on failure. This event rule must be + * destroyed using lttng_event_rule_destroy(). */ -extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_location( - struct lttng_event_rule *rule, +extern struct lttng_event_rule *lttng_event_rule_userspace_probe_create( const struct lttng_userspace_probe_location *location); /* @@ -47,7 +38,7 @@ extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_location( * 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( +extern enum lttng_event_rule_status lttng_event_rule_userspace_probe_get_location( const struct lttng_event_rule *rule, const struct lttng_userspace_probe_location **location); @@ -59,7 +50,7 @@ extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( * 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( +extern enum lttng_event_rule_status lttng_event_rule_userspace_probe_set_event_name( struct lttng_event_rule *rule, const char *name); /* @@ -73,11 +64,11 @@ extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( * 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( +extern enum lttng_event_rule_status lttng_event_rule_userspace_probe_get_event_name( const struct lttng_event_rule *rule, const char **name); #ifdef __cplusplus } #endif -#endif /* LTTNG_EVENT_RULE_UPROBE_H */ +#endif /* LTTNG_EVENT_RULE_USERSPACE_PROBE_H */ diff --git a/include/lttng/kernel-function-internal.h b/include/lttng/kernel-function-internal.h new file mode 100644 index 000000000..f0b412a59 --- /dev/null +++ b/include/lttng/kernel-function-internal.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_KERNEL_FUNCTION_INTERNAL_H +#define LTTNG_KERNEL_FUNCTION_INTERNAL_H + +#include +#include +#include + +#include +#include +#include + +struct lttng_payload; +struct lttng_payload_view; +struct lttng_dynamic_buffer; + +typedef bool (*kernel_function_location_equal_cb)( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); +typedef int (*kernel_function_location_serialize_cb)( + const struct lttng_kernel_function_location *kernel_function_location, + struct lttng_payload *payload); +typedef bool (*kernel_function_location_equal_cb)( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); +typedef ssize_t (*kernel_function_location_create_from_payload_cb)( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **kernel_function_location); +typedef unsigned long (*kernel_function_location_hash_cb)( + const struct lttng_kernel_function_location *location); + +struct lttng_kernel_function_location_comm { + /* enum lttng_kernel_function_location_type */ + int8_t type; + /* + * Payload is composed of, in that order, + * - type-specific payload + */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_kernel_function_location_symbol_comm { + /* Includes the trailing \0. */ + uint32_t symbol_len; + /* The offset from the symbol. */ + uint64_t offset; + /* + * Payload is composed of, in that order, + * - symbol name (with trailing \0). + */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_kernel_function_location_address_comm { + uint64_t address; +} LTTNG_PACKED; + +/* Common ancestor of all kernel function locations. */ +struct lttng_kernel_function_location { + enum lttng_kernel_function_location_type type; + kernel_function_location_equal_cb equal; + kernel_function_location_serialize_cb serialize; + kernel_function_location_hash_cb hash; +}; + +struct lttng_kernel_function_location_symbol { + struct lttng_kernel_function_location parent; + char *symbol_name; + uint64_t offset; +}; + +struct lttng_kernel_function_location_address { + struct lttng_kernel_function_location parent; + uint64_t address; +}; + +LTTNG_HIDDEN +int lttng_kernel_function_location_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload); + +LTTNG_HIDDEN +ssize_t lttng_kernel_function_location_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **function_location); + +LTTNG_HIDDEN +bool lttng_kernel_function_location_is_equal( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); + +LTTNG_HIDDEN +struct lttng_kernel_function_location *lttng_kernel_function_location_copy( + const struct lttng_kernel_function_location *location); + +LTTNG_HIDDEN +unsigned long lttng_kernel_function_location_hash( + const struct lttng_kernel_function_location *location); + +#endif /* LTTNG_KERNEL_FUNCTION_INTERNAL_H */ diff --git a/include/lttng/kernel-function.h b/include/lttng/kernel-function.h new file mode 100644 index 000000000..f05083f0b --- /dev/null +++ b/include/lttng/kernel-function.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_KERNEL_FUNCTION_H +#define LTTNG_KERNEL_FUNCTION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_kernel_function_location; + +enum lttng_kernel_function_location_status { + LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK = 0, + /* Invalid parameters provided. */ + LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_INVALID = -1, +}; + +enum lttng_kernel_function_location_type { + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_UNKNOWN = -1, + /* Location derived from a symbol and an offset. */ + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET = 0, + /* Location derived from an address. */ + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS = 1, +}; + +/* + * Get the type of the kernel function location. + */ +extern enum lttng_kernel_function_location_type +lttng_kernel_function_location_get_type( + const struct lttng_kernel_function_location *location); + +/* + * Destroy the kernel function location. + */ +extern void lttng_kernel_function_location_destroy( + struct lttng_kernel_function_location *location); + +/* + * Create a symbol derived function location. + * On failure, NULL is returned. + */ +extern struct lttng_kernel_function_location * +lttng_kernel_function_location_symbol_create(const char *symbol_name, + uint64_t offset); + +/* + * Get the symbol name of a symbol derived function location. + */ +extern const char *lttng_kernel_function_location_symbol_get_name( + const struct lttng_kernel_function_location *location); + +/* + * Get the offset of a symbol derived location. + */ +extern enum lttng_kernel_function_location_status +lttng_kernel_function_location_symbol_get_offset( + const struct lttng_kernel_function_location *location, + uint64_t *offset); + +/* + * Create an address derived function location. + * On failure, NULL is returned. + */ +extern struct lttng_kernel_function_location * +lttng_kernel_function_location_address_create(uint64_t address); + +/* + * Get the address of an address derived function location. + */ +extern enum lttng_kernel_function_location_status +lttng_kernel_function_location_address_get_address( + const struct lttng_kernel_function_location *location, + uint64_t *offset); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_KERNEL_FUNCTION_H */ diff --git a/include/lttng/log-level-rule-internal.h b/include/lttng/log-level-rule-internal.h new file mode 100644 index 000000000..9c5d1f250 --- /dev/null +++ b/include/lttng/log-level-rule-internal.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_LOG_LEVEL_RULE_INTERNAL_H +#define LTTNG_LOG_LEVEL_RULE_INTERNAL_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * For now only a single backing struct is used for both type of log level + * rule (exactly, as_severe) since both only have a "level" as property. + */ +struct lttng_log_level_rule { + enum lttng_log_level_rule_type type; + + /* Property */ + int level; +}; + +struct lttng_log_level_rule_comm { + /* enum lttng_log_level_rule_type */ + int8_t type; + int32_t level; +}; + +LTTNG_HIDDEN +ssize_t lttng_log_level_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_log_level_rule **rule); + +LTTNG_HIDDEN +int lttng_log_level_rule_serialize(const struct lttng_log_level_rule *rule, + struct lttng_payload *payload); + +LTTNG_HIDDEN +bool lttng_log_level_rule_is_equal(const struct lttng_log_level_rule *a, + const struct lttng_log_level_rule *b); + +LTTNG_HIDDEN +struct lttng_log_level_rule *lttng_log_level_rule_copy( + const struct lttng_log_level_rule *source); + +LTTNG_HIDDEN +void lttng_log_level_rule_to_loglevel( + const struct lttng_log_level_rule *log_level_rule, + enum lttng_loglevel_type *loglevel_type, + int *loglevel_value); +LTTNG_HIDDEN +unsigned long lttng_log_level_rule_hash( + const struct lttng_log_level_rule *log_level_rule); + +#endif /* LTTNG_LOG_LEVEL_RULE_INTERNAL_H */ diff --git a/include/lttng/log-level-rule.h b/include/lttng/log-level-rule.h new file mode 100644 index 000000000..d993163e9 --- /dev/null +++ b/include/lttng/log-level-rule.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_LOG_LEVEL_RULE_H +#define LTTNG_LOG_LEVEL_RULE_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_log_level_rule; + +enum lttng_log_level_rule_type { + LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN = -1, + LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY = 0, + LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS = 1, +}; + +enum lttng_log_level_rule_status { + LTTNG_LOG_LEVEL_RULE_STATUS_OK = 0, + LTTNG_LOG_LEVEL_RULE_STATUS_ERROR = -1, + LTTNG_LOG_LEVEL_RULE_STATUS_INVALID = -3, +}; + +/* + * Get the type of a log level rule. + * + * Returns the type of a log level rule on success, + * LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN on error. + */ +extern enum lttng_log_level_rule_type lttng_log_level_rule_get_type( + const struct lttng_log_level_rule *rule); + +/* + * Create a newly allocated log level rule where a log level must match exactly + * the rule to be considered. + * + * Returns a new log level rule on success, NULL on failure. This log level rule must be + * destroyed using lttng_log_level_rule_destroy(). + */ +extern struct lttng_log_level_rule *lttng_log_level_rule_exactly_create( + int level); + +/* + * Get the level property of a log level exactly rule. + * + * Returns LTTNG_LOG_LEVEL_RULE_STATUS and set the passed level pointer value + * on success, LTTNG_LOG_LEVEL_RULE_STATUS if an invalid + * parameter is passed. + */ +extern enum lttng_log_level_rule_status lttng_log_level_rule_exactly_get_level( + const struct lttng_log_level_rule *rule, int *level); + +/* + * Create a newly allocated log level rule where a log level must be at least as + * severe as the rule to be considered. + * + * Returns a new log level rule on success, NULL on failure. This log level rule + * must be destroyed using lttng_log_level_rule_destroy(). + */ +extern struct lttng_log_level_rule * +lttng_log_level_rule_at_least_as_severe_as_create(int level); + +/* + * Get the level property of a log level at least as severe rule. + * + * Returns LTTNG_LOG_LEVEL_RULE_STATUS and set the passed level pointer value + * on success, LTTNG_LOG_LEVEL_RULE_STATUS if an invalid + * parameter is passed. + */ +extern enum lttng_log_level_rule_status +lttng_log_level_rule_at_least_as_severe_as_get_level( + const struct lttng_log_level_rule *rule, int *level); + +/* + * Destroy (release) a log level rule object. + */ +extern void lttng_log_level_rule_destroy(struct lttng_log_level_rule *log_level_rule); + + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_LOG_LEVEL_RULE_H */ diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index 606d8b0b2..38b03550f 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -176,6 +176,20 @@ enum lttng_error_code { LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY = 163, /* Operation does not apply to the process attribute tracker's tracking policy */ LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD = 164, /* Error initializing event notifier group notification file descriptor */ LTTNG_ERR_INVALID_CAPTURE_EXPRESSION = 165, /* Invalid capture expression. */ + LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION = 166, /* Error registering event notifier to the tracer. */ + LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING = 167, /* Error initializing event notifier error accounting. */ + LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL = 168, /* Error event notifier error accounting full. */ + LTTNG_ERR_INVALID_MAP = 169, /* Invalid map provided. */ + LTTNG_ERR_MAP_NOT_FOUND = 170, /* Map by name not found. */ + LTTNG_ERR_UST_MAP_ENABLE_FAIL = 171, /* UST enable map failed */ + LTTNG_ERR_UST_MAP_DISABLE_FAIL = 172, /* UST disable map failed */ + LTTNG_ERR_UST_MAP_NOT_FOUND = 173, /* UST map not found */ + LTTNG_ERR_UST_MAP_EXIST = 174, /* UST map already exist */ + LTTNG_ERR_KERNEL_MAP_ENABLE_FAIL = 175, /* Kernel enable map failed */ + LTTNG_ERR_KERNEL_MAP_DISABLE_FAIL = 176, /* Kernel disable map failed */ + LTTNG_ERR_KERNEL_MAP_NOT_FOUND = 177, /* Kernel map not found */ + LTTNG_ERR_KERNEL_MAP_EXIST = 178, /* Kernel map already exist */ + LTTNG_ERR_MAP_VALUES_LIST_FAIL = 179, /* Listing map values failed */ /* MUST be last element of the manually-assigned section of the enum */ LTTNG_ERR_NR, diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index c665d3c9b..86db9945a 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -18,6 +18,7 @@ /* Include every LTTng ABI/API available. */ #include #include +#include #include #include #include @@ -29,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -40,16 +41,18 @@ #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/map-key-internal.h b/include/lttng/map-key-internal.h new file mode 100644 index 000000000..84c25805d --- /dev/null +++ b/include/lttng/map-key-internal.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ +#ifndef LTTNG_MAP_KEY_INTERNAL_H +#define LTTNG_MAP_KEY_INTERNAL_H + +#include +#include +#include +#include + +#include + +struct lttng_payload; +struct lttng_payload_view; +struct lttng_map_key_token; + +typedef bool (*map_key_token_equal_cb)(const struct lttng_map_key_token *a, + const struct lttng_map_key_token *b); + +enum lttng_map_key_token_type { + LTTNG_MAP_KEY_TOKEN_TYPE_STRING, + LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, +}; + +struct lttng_map_key_token { + enum lttng_map_key_token_type type; + map_key_token_equal_cb equal; +}; + +struct lttng_map_key_token_comm { + uint8_t type; +}; + +struct lttng_map_key_token_string { + struct lttng_map_key_token parent; + char *string; +}; + +struct lttng_map_key_token_string_comm { + uint8_t parent_type; + + /* Includes null terminator. */ + uint32_t string_len; + + char payload[]; +}; + +struct lttng_map_key_token_variable { + struct lttng_map_key_token parent; + enum lttng_map_key_token_variable_type type; +}; + +struct lttng_map_key_token_variable_comm { + uint8_t parent_type; + uint8_t var_type; +}; + +struct lttng_map_key { + /* Reference counting is only exposed to internal users*/ + struct urcu_ref ref; + /* Array of `struct lttng_map_key_token` */ + struct lttng_dynamic_pointer_array tokens; +}; + +struct lttng_map_key_comm { + uint32_t token_count; + /* Array of `struct lttng_map_key_token` */ + char payload[]; +}; + +LTTNG_HIDDEN +void lttng_map_key_get(struct lttng_map_key *key); + +LTTNG_HIDDEN +void lttng_map_key_put(struct lttng_map_key *key); + +LTTNG_HIDDEN +ssize_t lttng_map_key_create_from_payload(struct lttng_payload_view *view, + struct lttng_map_key **key); + +LTTNG_HIDDEN +int lttng_map_key_serialize(const struct lttng_map_key *key, + struct lttng_payload *payload); + +LTTNG_HIDDEN +enum lttng_map_key_status lttng_map_key_get_token_count( + const struct lttng_map_key *key, unsigned int *count); + +LTTNG_HIDDEN +const struct lttng_map_key_token *lttng_map_key_get_token_at_index( + const struct lttng_map_key *key, unsigned int index); + +LTTNG_HIDDEN +enum lttng_map_key_token_variable_type lttng_map_key_token_variable_get_type( + const struct lttng_map_key_token_variable *token); + +LTTNG_HIDDEN +const char *lttng_map_key_token_string_get_string( + const struct lttng_map_key_token_string *token); + +LTTNG_HIDDEN +bool lttng_map_key_is_equal( + const struct lttng_map_key *a, const struct lttng_map_key *b); + +LTTNG_HIDDEN +void lttng_map_key_get(struct lttng_map_key *key); + +LTTNG_HIDDEN +void lttng_map_key_put(struct lttng_map_key *key); + +LTTNG_HIDDEN +struct lttng_map_key *lttng_map_key_parse_from_string(const char *key_str); + +#endif /* LTTNG_MAP_KEY_INTERNAL_H */ diff --git a/include/lttng/map-key.h b/include/lttng/map-key.h new file mode 100644 index 000000000..be5694889 --- /dev/null +++ b/include/lttng/map-key.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_KEY_H +#define LTTNG_MAP_KEY_H + +struct lttng_map_key; + +enum lttng_map_key_status { + LTTNG_MAP_KEY_STATUS_ERROR = -2, + LTTNG_MAP_KEY_STATUS_INVALID = -1, + LTTNG_MAP_KEY_STATUS_OK = 0, +}; + +enum lttng_map_key_token_variable_type { + LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, + LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME, +}; + +struct lttng_map_key *lttng_map_key_create(void); + +enum lttng_map_key_status lttng_map_key_append_token_variable( + struct lttng_map_key *key, + enum lttng_map_key_token_variable_type var_type); + +enum lttng_map_key_status lttng_map_key_append_token_string( + struct lttng_map_key *key, const char *string); + +void lttng_map_key_destroy(struct lttng_map_key *key); + +#endif /* LTTNG_MAP_KEY_H */ diff --git a/include/lttng/map/map-internal.h b/include/lttng/map/map-internal.h new file mode 100644 index 000000000..45aae76fa --- /dev/null +++ b/include/lttng/map/map-internal.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_INTERNAL_H +#define LTTNG_MAP_INTERNAL_H + +#include +#include +#include +#include +#include + +#include "map.h" + +struct lttng_payload; +struct lttng_payload_view; + +struct lttng_map { + /* Reference counting is only exposed to internal users. */ + struct urcu_ref ref; + + char *name; + LTTNG_OPTIONAL(bool) is_enabled; + enum lttng_map_bitness bitness; + enum lttng_map_boundary_policy boundary_policy; + enum lttng_domain_type domain; + enum lttng_buffer_type buffer_type; + bool coalesce_hits; + unsigned int dimension_count; + uint64_t *dimension_sizes; +}; + +struct lttng_map_list { + struct lttng_dynamic_pointer_array array; +}; + +struct lttng_map_key_value_pair { + char *key; + int64_t value; + bool has_overflowed; + bool has_underflowed; +}; + +struct lttng_map_key_value_pair_list { + enum lttng_map_key_value_pair_list_type type; + uint64_t id; /* pid_t or uid_t */ + uint64_t cpu; + bool summed_all_cpus; + struct lttng_dynamic_pointer_array array; +}; + +struct lttng_map_content { + enum lttng_buffer_type type; + struct lttng_dynamic_pointer_array array; +}; + +struct lttng_map_comm { + uint32_t name_length /* Includes '\0' */; + uint32_t length; + uint8_t is_enabled; + uint8_t bitness; + uint8_t boundary_policy; + uint8_t domain; + uint8_t buffer_type; + uint8_t coalesce_hits;; + uint64_t dimension_count; + + /* length excludes its own length. */ + /* A name and dimension sizes follow. */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_map_list_comm { + uint32_t count; + /* Count * lttng_map_comm structure */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_map_key_value_pair_comm { + uint32_t key_length /* Includes '\0' */; + int64_t value; + uint8_t has_overflowed; + uint8_t has_underflowed; +} LTTNG_PACKED; + +struct lttng_map_key_value_pair_list_comm { + uint32_t count; + uint8_t type; /* enum lttng_map_key_value_pair_list_type */ + uint64_t id; /* pid_t or uid_t */ + uint64_t cpu; + uint8_t summed_all_cpus; + /* Count * lttng_map_key_value_pair_comm structure */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_map_content_comm { + uint32_t count; + uint8_t type; /* enum lttng_buffer_type */ + /* Count * lttng_map_key_value_pair_list structure */ + char payload[]; +}; + +LTTNG_HIDDEN +ssize_t lttng_map_create_from_payload(struct lttng_payload_view *view, + struct lttng_map **map); + +LTTNG_HIDDEN +int lttng_map_serialize(const struct lttng_map *map, + struct lttng_payload *payload); + +LTTNG_HIDDEN +void lttng_map_get(struct lttng_map *map); + +LTTNG_HIDDEN +void lttng_map_put(struct lttng_map *map); + +LTTNG_HIDDEN +void lttng_map_set_is_enabled(struct lttng_map *map, bool enabled); + +/* + * Allocate a new list of maps. + * The returned object must be freed via lttng_map_list_destroy. + */ +LTTNG_HIDDEN +struct lttng_map_list *lttng_map_list_create(void); + +/* + * Add a map to the maps set. + * + * A reference to the added map is acquired on behalf of the map set + * on success. + */ +LTTNG_HIDDEN +enum lttng_map_status lttng_map_list_add(struct lttng_map_list *map_list, + struct lttng_map *map); + +LTTNG_HIDDEN +ssize_t lttng_map_list_create_from_payload(struct lttng_payload_view *view, + struct lttng_map_list **map_list); + +/* + * Serialize a map list to an lttng_payload object. + * Return LTTNG_OK on success, negative lttng error code on error. + */ +LTTNG_HIDDEN +int lttng_map_list_serialize(const struct lttng_map_list *map_list, + struct lttng_payload *payload); + +LTTNG_HIDDEN +struct lttng_map_key_value_pair *lttng_map_key_value_pair_create( + const char *key, int64_t value); + +LTTNG_HIDDEN +void lttng_map_key_value_pair_set_has_overflowed( + struct lttng_map_key_value_pair *key_value); + +LTTNG_HIDDEN +void lttng_map_key_value_pair_set_has_underflowed( + struct lttng_map_key_value_pair *key_value); + +LTTNG_HIDDEN +ssize_t lttng_map_key_value_pair_create_from_payload( + struct lttng_payload_view *view, + struct lttng_map_key_value_pair **key_value); + +LTTNG_HIDDEN +int lttng_map_key_value_pair_serialize( + const struct lttng_map_key_value_pair *key_value, + struct lttng_payload *payload); + +LTTNG_HIDDEN +void lttng_map_key_value_pair_destroy( + struct lttng_map_key_value_pair *key_value); + +LTTNG_HIDDEN +struct lttng_map_key_value_pair_list *lttng_map_key_value_pair_list_create( + enum lttng_map_key_value_pair_list_type type, + bool summed_all_cpus); + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_set_identifier( + struct lttng_map_key_value_pair_list *kv_pair_list, + uint64_t identifier); + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_set_cpu( + struct lttng_map_key_value_pair_list *kv_pair_list, + uint64_t cpu); + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_append_key_value( + struct lttng_map_key_value_pair_list *key_values, + struct lttng_map_key_value_pair *key_value); + +LTTNG_HIDDEN +ssize_t lttng_map_key_value_pair_list_create_from_payload( + struct lttng_payload_view *view, + struct lttng_map_key_value_pair_list **key_values); + +LTTNG_HIDDEN +int lttng_map_key_value_pair_list_serialize( + const struct lttng_map_key_value_pair_list *key_values, + struct lttng_payload *payload); + +LTTNG_HIDDEN +struct lttng_map_content *lttng_map_content_create( + enum lttng_buffer_type type); + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_content_append_key_value_list( + struct lttng_map_content *map_content, + struct lttng_map_key_value_pair_list *kv_list); + +LTTNG_HIDDEN +ssize_t lttng_map_content_create_from_payload( + struct lttng_payload_view *view, + struct lttng_map_content **map_content); + +LTTNG_HIDDEN +int lttng_map_content_serialize( + const struct lttng_map_content *map_content, + struct lttng_payload *payload); + +#endif /* LTTNG_MAP_INTERNAL_H */ diff --git a/include/lttng/map/map-query-internal.h b/include/lttng/map/map-query-internal.h new file mode 100644 index 000000000..16d724112 --- /dev/null +++ b/include/lttng/map/map-query-internal.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_QUERY_INTERNAL_H +#define LTTNG_MAP_QUERY_INTERNAL_H + +#include + +#include +#include + +#include +#include + +struct lttng_map_query { + enum lttng_map_query_config_cpu config_cpu; + enum lttng_map_query_config_buffer config_buffer; + enum lttng_map_query_config_app_bitness config_bitness; + + /* + * Aggregate the values of all selected CPUs in a single table. + */ + bool sum_by_cpu; + + /* + * Aggregate the values of all selected bitness in a single table. + */ + bool sum_by_app_bitness; + + /* + * Aggregate the values of all selected uid or pid in a single table. + */ + bool sum_by_uid; + bool sum_by_pid; + + char *key_filter; + struct lttng_dynamic_array cpu_array; + struct lttng_dynamic_array uid_array; + struct lttng_dynamic_array pid_array; +}; + +struct lttng_map_query_comm { + uint32_t key_filter_length; /* Include '\0' */ + + uint8_t config_cpu; + uint8_t config_buffer; + uint8_t config_app_bitness; + + uint8_t sum_by_cpu; + uint8_t sum_by_app_bitness; + uint8_t sum_by_uid; + uint8_t sum_by_pid; + + uint32_t cpu_count; + uint32_t uid_count; + uint32_t pid_count; + /* + * key_filter + + * (cpu_count * int) + (uid_count * uid_t) + (pid_count * pid_t) + */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +enum lttng_map_query_config_cpu lttng_map_query_get_config_cpu( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +enum lttng_map_query_config_buffer lttng_map_query_get_config_buffer( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +enum lttng_map_query_config_app_bitness lttng_map_query_get_config_app_bitness( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +bool lttng_map_query_get_config_sum_by_cpu( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +bool lttng_map_query_get_config_sum_by_pid( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +bool lttng_map_query_get_config_sum_by_uid( + const struct lttng_map_query *query); + +// Not supported yet. +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_set_sum_by_app_bitness( + struct lttng_map_query *query, bool sum_by_app_bitness); + +// Not supported yet. +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_set_sum_by_uid( + struct lttng_map_query *query, bool sum_by_uid); + +LTTNG_HIDDEN +bool lttng_map_query_get_config_sum_by_app_bitness( + const struct lttng_map_query *query); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_cpu_count( + const struct lttng_map_query *query, unsigned int *count); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_uid_count( + const struct lttng_map_query *query, unsigned int *count); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_pid_count( + const struct lttng_map_query *query, unsigned int *count); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_cpu_at_index( + const struct lttng_map_query *query, unsigned int index, + int *cpu); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_uid_at_index( + const struct lttng_map_query *query, unsigned int index, + uid_t *uid); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_pid_at_index( + const struct lttng_map_query *query, unsigned int index, + pid_t *pid); + +LTTNG_HIDDEN +enum lttng_map_query_status lttng_map_query_get_key_filter( + const struct lttng_map_query *query, const char **key_filter); + +LTTNG_HIDDEN +ssize_t lttng_map_query_create_from_payload(struct lttng_payload_view *view, + struct lttng_map_query **query); + +LTTNG_HIDDEN +int lttng_map_query_serialize(const struct lttng_map_query *query, + struct lttng_payload *payload); + +#endif /* LTTNG_MAP_QUERY_INTERNAL_H */ diff --git a/include/lttng/map/map-query.h b/include/lttng/map/map-query.h new file mode 100644 index 000000000..b58aeee30 --- /dev/null +++ b/include/lttng/map/map-query.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_QUERY_H +#define LTTNG_MAP_QUERY_H + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum lttng_map_query_status { + LTTNG_MAP_QUERY_STATUS_OK = 0, + LTTNG_MAP_QUERY_STATUS_ERROR = -1, + LTTNG_MAP_QUERY_STATUS_INVALID = -2, + LTTNG_MAP_QUERY_STATUS_NONE = -3, +}; + +/* + * Query the values of all CPUs or just some. + */ +enum lttng_map_query_config_cpu { + LTTNG_MAP_QUERY_CONFIG_CPU_ALL, + LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET, +}; + +/* + * Query the values of all uid (or pid) or just some. + */ +enum lttng_map_query_config_buffer { + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL, + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET, + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL, + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET, + LTTNG_MAP_QUERY_CONFIG_BUFFER_KERNEL_GLOBAL, +}; + +/* + * Query the values of all bitness or just some. + */ +enum lttng_map_query_config_app_bitness { + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_32, /*Not supported yet*/ + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_64, /*Not supported yet*/ + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL, + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_KERNEL, +}; + +struct lttng_map_query; + +/* + * + */ +extern struct lttng_map_query *lttng_map_query_create( + enum lttng_map_query_config_cpu cpu, + enum lttng_map_query_config_buffer buffer, + enum lttng_map_query_config_app_bitness bitness); + +extern enum lttng_map_query_status lttng_map_query_set_sum_by_cpu( + struct lttng_map_query *query, bool sum_by_cpu); + +extern enum lttng_map_query_status lttng_map_query_set_sum_by_pid( + struct lttng_map_query *query, bool sum_by_pid); + +extern enum lttng_map_query_status lttng_map_query_add_cpu( + struct lttng_map_query *query, int cpu_id); + +extern enum lttng_map_query_status lttng_map_query_add_uid( + struct lttng_map_query *query, uid_t uid); + +extern enum lttng_map_query_status lttng_map_query_add_pid( + struct lttng_map_query *query, pid_t pid); + +extern enum lttng_map_query_status lttng_map_query_add_key_filter( + struct lttng_map_query *query, const char *key_filter); + +extern void lttng_map_query_destroy(struct lttng_map_query *query); + + + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_MAP_QUERY_H */ diff --git a/include/lttng/map/map.h b/include/lttng/map/map.h new file mode 100644 index 000000000..292e39e62 --- /dev/null +++ b/include/lttng/map/map.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_MAP_H +#define LTTNG_MAP_H + +#include +#include + +#include +#include + +#include + +struct lttng_map; +struct lttng_map_list; + +struct lttng_map_key_value_pair; +/* A list of key value pair. */ +struct lttng_map_key_value_pair_list; +/* A list of key value pair list. */ +struct lttng_map_content; + +#ifdef __cplusplus +extern "C" { +#endif + +enum lttng_map_status { + LTTNG_MAP_STATUS_OK = 0, + LTTNG_MAP_STATUS_ERROR = -1, + LTTNG_MAP_STATUS_INVALID = -2, + LTTNG_MAP_STATUS_UNSET = -3, +}; + +enum lttng_map_bitness { + LTTNG_MAP_BITNESS_32BITS = 32, + LTTNG_MAP_BITNESS_64BITS = 64, +}; + +enum lttng_map_boundary_policy { + LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW, +}; + +enum lttng_map_key_value_pair_list_type { + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED, +}; + +/* + * + * Return LTTNG_MAP_STATUS_OK on success, LTTNG_MAP_STATUS_INVALID if invalid + * parameters are passed. + */ +extern enum lttng_map_status lttng_map_create(const char *name, + unsigned int dimension_count, + uint64_t *dimension_sizes, + enum lttng_domain_type domain, + enum lttng_buffer_type buffer_type, + enum lttng_map_bitness bitness, + enum lttng_map_boundary_policy boundary_policy, + bool coalesce_hits, + struct lttng_map **map); + +extern enum lttng_map_status lttng_map_get_name( + const struct lttng_map *map, const char **name); + +extern enum lttng_map_status lttng_map_set_name( + struct lttng_map *map, const char *name); + +/* + * Get the number of dimensions. + * + */ +extern unsigned int lttng_map_get_dimension_count( + const struct lttng_map *map); + +/* + * Get the number of elements for the provided dimension. + * + * Return LTTNG_MAP_STATUS_OK on success, LTTNG_MAP_STATUS_INVALID if invalid + * parameters are passed. + * + */ +extern enum lttng_map_status lttng_map_get_dimension_length( + const struct lttng_map *map, unsigned int dimension, + uint64_t *dimension_length); + +extern int lttng_map_get_is_enabled(const struct lttng_map *map); + +extern enum lttng_map_bitness lttng_map_get_bitness( + const struct lttng_map *map); + +extern enum lttng_domain_type lttng_map_get_domain( + const struct lttng_map *map); + +extern enum lttng_buffer_type lttng_map_get_buffer_type( + const struct lttng_map *map); + +extern enum lttng_map_boundary_policy lttng_map_get_boundary_policy( + const struct lttng_map *map); + +extern bool lttng_map_get_coalesce_hits( + const struct lttng_map *map); + +extern void lttng_map_destroy(struct lttng_map *map); + +extern enum lttng_error_code lttng_add_map(struct lttng_handle *handle, + struct lttng_map *map); + +extern enum lttng_error_code lttng_enable_map(struct lttng_handle *handle, + const char *map_name); + +extern enum lttng_error_code lttng_disable_map(struct lttng_handle *handle, + const char *map_name); + + +/* + * Get a map from the list at a given index. + * + * Note that the map list maintains the ownership of the returned map. + * It must not be destroyed by the user, nor should a reference to it be held + * beyond the lifetime of the map list. + * + * Returns a map, or NULL on error. + */ +extern const struct lttng_map *lttng_map_list_get_at_index( + const struct lttng_map_list *map_list, unsigned int index); + +/* + * Get the number of map in a map list. + */ + +extern enum lttng_map_status lttng_map_list_get_count( + const struct lttng_map_list *map_list, unsigned int *count); + +/* + * Destroy a map list. + */ +extern void lttng_map_list_destroy(struct lttng_map_list *map_list); + +extern enum lttng_error_code lttng_list_maps(struct lttng_handle *handle, + struct lttng_map_list **map_list); + +/* + * FIXME: frdeso proper explanation + * lttng_map_content 1 to N lttng_map_key_value_pair_list + * lttng_map_key_value_pair_list 1 to N lttng_map_key_value_pair + */ + +/* + * Get the key of a key-value. + * + * The caller does not assume the ownership of the returned key. + * The key shall only be used for the duration of the key-value's lifetime. + * + * Returns LTTNG_MAP_STATUS_OK and a pointer to the key-value's key on success, + * LTTNG_MAP_STATUS_INVALID if an invalid parameter is passed, or + */ +extern enum lttng_map_status lttng_map_key_value_pair_get_key( + const struct lttng_map_key_value_pair *kv_pair, const char **key); + +extern bool lttng_map_key_value_pair_get_has_overflowed( + const struct lttng_map_key_value_pair *key_value); + +extern bool lttng_map_key_value_pair_get_has_underflowed( + const struct lttng_map_key_value_pair *key_value); +/* + * Get the value of a key-value. + * + * The caller does not assume the ownership of the returned value. + * The value shall only be used for the duration of the key-value's lifetime. + * + * Returns LTTNG_MAP_STATUS_OK and a pointer to the key-value's value on success, + * LTTNG_MAP_STATUS_INVALID if an invalid parameter is passed. + */ +extern enum lttng_map_status lttng_map_key_value_pair_get_value( + const struct lttng_map_key_value_pair *kv_pair, int64_t *value); + +extern enum lttng_map_status lttng_map_content_get_count( + const struct lttng_map_content *map_content, + unsigned int *count); + +extern const struct lttng_map_key_value_pair_list *lttng_map_content_get_at_index( + const struct lttng_map_content *map_content, + unsigned int index); +/* + * List all key-value pairs for the given session and map. + * + * On success, a newly-allocated key-value list is returned. + * + * The key-value list must be destroyed by the caller (see + * lttng_map_key_value_pair_list_destroy()). + * + * Returns LTTNG_OK on success, else a suitable LTTng error code. + */ +extern enum lttng_error_code lttng_list_map_content( + struct lttng_handle *handle, const struct lttng_map *map, + const struct lttng_map_query *map_query, + struct lttng_map_content **map_content); + +extern enum lttng_buffer_type lttng_map_content_get_buffer_type( + const struct lttng_map_content *map_content); + +extern void lttng_map_content_destroy( + struct lttng_map_content *map_content); +/* + * Get a key-value from the list at a given index. + * + * Note that the key value list maintains the ownership of the returned key + * value. + * It must not be destroyed by the user, nor should a reference to it be held + * beyond the lifetime of the key value list. + * + * Returns a key-value, or NULL on error. + */ +extern const struct lttng_map_key_value_pair *lttng_map_key_value_pair_list_get_at_index( + const struct lttng_map_key_value_pair_list *kv_pair_list, + unsigned int index); + +/* + * Get the number of key value pair in a key-value list. + * + * Return LTTNG_MAP_STATUS_OK on success, + * LTTNG_MAP_STATUS_INVALID when invalid parameters are passed. + */ +extern enum lttng_map_status lttng_map_key_value_pair_list_get_count( + const struct lttng_map_key_value_pair_list *kv_pair_list, + unsigned int *count); + +extern enum lttng_map_key_value_pair_list_type lttng_map_key_value_pair_list_get_type( + const struct lttng_map_key_value_pair_list *kv_pair_list); + +extern uint64_t lttng_map_key_value_pair_list_get_identifer( + const struct lttng_map_key_value_pair_list *kv_pair_list); + +extern uint64_t lttng_map_key_value_pair_list_get_cpu( + const struct lttng_map_key_value_pair_list *kv_pair_list); + +extern bool lttng_map_key_value_pair_list_get_summed_all_cpu( + const struct lttng_map_key_value_pair_list *kv_pair_list); + +/* + * Destroy a map_key_value set. + */ +extern void lttng_map_key_value_pair_list_destroy( + struct lttng_map_key_value_pair_list *kv_pair_list); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_MAP_H */ diff --git a/include/lttng/trigger/trigger-internal.h b/include/lttng/trigger/trigger-internal.h index 21c269bef..59fe771aa 100644 --- a/include/lttng/trigger/trigger-internal.h +++ b/include/lttng/trigger/trigger-internal.h @@ -196,4 +196,13 @@ enum lttng_error_code lttng_trigger_generate_bytecode( LTTNG_HIDDEN struct lttng_trigger *lttng_trigger_copy(const struct lttng_trigger *trigger); +/* + * A given trigger needs a tracer notifier if + * it has an event-rule condition, + * AND + * it has one or more sessiond-execution action. + */ +LTTNG_HIDDEN +bool lttng_trigger_needs_tracer_notifier(const struct lttng_trigger *trigger); + #endif /* LTTNG_TRIGGER_INTERNAL_H */ diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am index 1d3741c61..67b96b1dc 100644 --- a/src/bin/lttng-sessiond/Makefile.am +++ b/src/bin/lttng-sessiond/Makefile.am @@ -20,7 +20,7 @@ lttng_sessiond_SOURCES = utils.c utils.h \ context.c context.h \ channel.c channel.h \ event.c event.h \ - shm.c shm.h \ + map.c map.h \ consumer.c consumer.h \ session.c session.h \ modprobe.c modprobe.h kern-modules.h \ @@ -56,8 +56,11 @@ lttng_sessiond_SOURCES = utils.c utils.h \ manage-consumer.c manage-consumer.h \ clear.c clear.h \ tracker.c tracker.h \ + event-notifier-error-accounting.c event-notifier-error-accounting.h \ action-executor.c action-executor.h +lttng_sessiond_LDFLAGS = -rdynamic + if HAVE_LIBLTTNG_UST_CTL lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \ ust-consumer.c ust-consumer.h notify-apps.c \ diff --git a/src/bin/lttng-sessiond/action-executor.c b/src/bin/lttng-sessiond/action-executor.c index 8f8bae406..4e6de8907 100644 --- a/src/bin/lttng-sessiond/action-executor.c +++ b/src/bin/lttng-sessiond/action-executor.c @@ -16,13 +16,14 @@ #include #include #include +#include #include #include #include #include #include #include -#include +#include #include #include #include @@ -66,6 +67,9 @@ typedef int (*action_executor_handler)(struct action_executor *executor, static int action_executor_notify_handler(struct action_executor *executor, const struct action_work_item *, const struct lttng_action *); +static int action_executor_incr_value_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 *); @@ -87,6 +91,7 @@ static int action_executor_generic_handler(struct action_executor *executor, static const action_executor_handler action_executors[] = { [LTTNG_ACTION_TYPE_NOTIFY] = action_executor_notify_handler, + [LTTNG_ACTION_TYPE_INCREMENT_VALUE] = action_executor_incr_value_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, @@ -94,14 +99,6 @@ static const action_executor_handler action_executors[] = { [LTTNG_ACTION_TYPE_GROUP] = action_executor_group_handler, }; -static 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", -}; static const char *get_action_name(const struct lttng_action *action) { @@ -109,7 +106,7 @@ static const char *get_action_name(const struct lttng_action *action) assert(action_type != LTTNG_ACTION_TYPE_UNKNOWN); - return action_type_names[action_type]; + return lttng_action_type_string(action_type); } /* Check if this trigger allowed to interect with a given session. */ @@ -205,6 +202,14 @@ static int action_executor_notify_handler(struct action_executor *executor, client_handle_transmission_status, executor); } +static int action_executor_incr_value_handler(struct action_executor *executor, + const struct action_work_item *work_item, + const struct lttng_action *action) +{ + /* This action is executed by the tracer. */ + return 0; +} + static int action_executor_start_session_handler(struct action_executor *executor, const struct action_work_item *work_item, const struct lttng_action *action) @@ -237,7 +242,6 @@ static int action_executor_start_session_handler(struct action_executor *executo if (!is_trigger_allowed_for_session(work_item->trigger, session)) { goto error_dispose_session; } - cmd_ret = cmd_start_trace(session); switch (cmd_ret) { case LTTNG_OK: diff --git a/src/bin/lttng-sessiond/agent.c b/src/bin/lttng-sessiond/agent.c index 283d5b035..cbd81c576 100644 --- a/src/bin/lttng-sessiond/agent.c +++ b/src/bin/lttng-sessiond/agent.c @@ -15,8 +15,9 @@ #include #include #include -#include +#include #include +#include #include #include @@ -1243,6 +1244,7 @@ struct agent_event *agent_find_event_by_trigger( const struct lttng_event_rule *rule; const char *name; const char *filter_expression; + const struct lttng_log_level_rule *log_level_rule; /* Unused when loglevel_type is 'ALL'. */ int loglevel_value = 0; enum lttng_loglevel_type loglevel_type; @@ -1253,9 +1255,9 @@ struct agent_event *agent_find_event_by_trigger( condition = lttng_trigger_get_const_condition(trigger); assert(lttng_condition_get_type(condition) == - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + LTTNG_CONDITION_TYPE_ON_EVENT); - c_status = lttng_condition_event_rule_get_rule(condition, &rule); + c_status = lttng_condition_on_event_get_rule(condition, &rule); assert(c_status == LTTNG_CONDITION_STATUS_OK); assert(lttng_event_rule_get_type(rule) == @@ -1272,14 +1274,19 @@ struct agent_event *agent_find_event_by_trigger( /* Get the internal filter expression. */ filter_expression = lttng_event_rule_get_filter(rule); - er_status = lttng_event_rule_tracepoint_get_log_level_type( - rule, &loglevel_type); - assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); - - if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { - er_status = lttng_event_rule_tracepoint_get_log_level( - rule, &loglevel_value); - assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + /* Map log_level_rule to loglevel. + * TODO: There is a possibility of extracting this to the log_level_rule + * internal api since multiple callsite do the same. + */ + er_status = lttng_event_rule_tracepoint_get_log_level_rule( + rule, &log_level_rule); + if (er_status == LTTNG_EVENT_RULE_STATUS_UNSET) { + loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + loglevel_value = 0; + } else if (er_status == LTTNG_EVENT_RULE_STATUS_OK) { + lttng_log_level_rule_to_loglevel(log_level_rule, &loglevel_type, &loglevel_value); + } else { + abort(); } return agent_find_event(name, loglevel_type, loglevel_value, diff --git a/src/bin/lttng-sessiond/buffer-registry.c b/src/bin/lttng-sessiond/buffer-registry.c index 32fcbfb5f..34574e391 100644 --- a/src/bin/lttng-sessiond/buffer-registry.c +++ b/src/bin/lttng-sessiond/buffer-registry.c @@ -136,6 +136,14 @@ int buffer_reg_uid_create(uint64_t session_id, uint32_t bits_per_long, uid_t uid goto error_session; } + reg->registry->maps = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!reg->registry->maps) { + lttng_ht_destroy(reg->registry->channels); + ret = -ENOMEM; + goto error_session; + } + + cds_lfht_node_init(®->node.node); *regp = reg; @@ -262,6 +270,13 @@ int buffer_reg_pid_create(uint64_t session_id, struct buffer_reg_pid **regp, goto error_session; } + reg->registry->maps = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!reg->registry->maps) { + lttng_ht_destroy(reg->registry->channels); + ret = -ENOMEM; + goto error_session; + } + lttng_ht_node_init_u64(®->node, reg->session_id); *regp = reg; @@ -385,6 +400,36 @@ int buffer_reg_channel_create(uint64_t key, struct buffer_reg_channel **regp) return 0; } +/* + * Allocate and initialize a buffer registry map with the given key. Set + * regp with the object pointer. + * + * Return 0 on success or else a negative value keeping regp untouched. + */ +int buffer_reg_map_create(uint64_t key, struct buffer_reg_map **regp) +{ + struct buffer_reg_map *reg; + + assert(regp); + + DBG3("Buffer registry map create with key: %" PRIu64, key); + + reg = zmalloc(sizeof(*reg)); + if (!reg) { + PERROR("zmalloc buffer registry map"); + return -ENOMEM; + } + + reg->key = key; + CDS_INIT_LIST_HEAD(®->counters); + pthread_mutex_init(®->counter_list_lock, NULL); + + lttng_ht_node_init_u64(®->node, key); + *regp = reg; + + return 0; +} + /* * Allocate and initialize a buffer registry stream. Set regp with the object * pointer. @@ -410,6 +455,31 @@ int buffer_reg_stream_create(struct buffer_reg_stream **regp) return 0; } +/* + * Allocate and initialize a buffer registry map_counter. Set regp with the object + * pointer. + * + * Return 0 on success or else a negative value keeping regp untouched. + */ +int buffer_reg_map_counter_create(struct buffer_reg_map_counter **regp) +{ + struct buffer_reg_map_counter *reg; + + assert(regp); + + DBG3("Buffer registry creating map_counter"); + + reg = zmalloc(sizeof(*reg)); + if (!reg) { + PERROR("zmalloc buffer registry map_counter"); + return -ENOMEM; + } + + *regp = reg; + + return 0; +} + /* * Add stream to the list in the channel. */ @@ -425,6 +495,21 @@ void buffer_reg_stream_add(struct buffer_reg_stream *stream, pthread_mutex_unlock(&channel->stream_list_lock); } +/* + * Add map_counter to the list in the map. + */ +void buffer_reg_map_counter_add(struct buffer_reg_map_counter *map_counter, + struct buffer_reg_map *map) +{ + assert(map_counter); + assert(map); + + pthread_mutex_lock(&map->counter_list_lock); + cds_list_add_tail(&map_counter->lnode, &map->counters); + map->counter_count++; + pthread_mutex_unlock(&map->counter_list_lock); +} + /* * Add a buffer registry channel object to the given session. */ @@ -439,6 +524,20 @@ void buffer_reg_channel_add(struct buffer_reg_session *session, rcu_read_unlock(); } +/* + * Add a buffer registry map object to the given session. + */ +void buffer_reg_map_add(struct buffer_reg_session *session, + struct buffer_reg_map *map) +{ + assert(session); + assert(map); + + rcu_read_lock(); + lttng_ht_add_unique_u64(session->maps, &map->node); + rcu_read_unlock(); +} + /* * Find a buffer registry channel object with the given key. RCU read side lock * MUST be acquired and hold on until the object reference is not needed @@ -476,6 +575,43 @@ end: return chan; } +/* + * Find a buffer registry map object with the given key. RCU read side lock + * MUST be acquired and hold on until the object reference is not needed + * anymore. + * + * Return the object pointer or NULL on error. + */ +struct buffer_reg_map *buffer_reg_map_find(uint64_t key, + struct buffer_reg_uid *reg) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + struct buffer_reg_map *map = NULL; + struct lttng_ht *ht; + + assert(reg); + + switch (reg->domain) { + case LTTNG_DOMAIN_UST: + ht = reg->registry->maps; + break; + default: + assert(0); + goto end; + } + + lttng_ht_lookup(ht, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (!node) { + goto end; + } + map = caa_container_of(node, struct buffer_reg_map, node); + +end: + return map; +} + /* * Destroy a buffer registry stream with the given domain. */ @@ -511,6 +647,41 @@ void buffer_reg_stream_destroy(struct buffer_reg_stream *regp, return; } +/* + * Destroy a buffer registry map_counter with the given domain. + */ +void buffer_reg_map_counter_destroy(struct buffer_reg_map_counter *regp, + enum lttng_domain_type domain) +{ + if (!regp) { + return; + } + + DBG3("Buffer registry map counter destroy with handle %d", + regp->obj.ust->handle); + + switch (domain) { + case LTTNG_DOMAIN_UST: + { + int ret; + + ret = ust_app_release_object(NULL, regp->obj.ust); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Buffer reg map counter release obj handle %d failed with ret %d", + regp->obj.ust->handle, ret); + } + free(regp->obj.ust); + lttng_fd_put(LTTNG_FD_APPS, 2); + break; + } + default: + assert(0); + } + + free(regp); + return; +} + /* * Remove buffer registry channel object from the session hash table. RCU read * side lock MUST be acquired before calling this. @@ -529,6 +700,24 @@ void buffer_reg_channel_remove(struct buffer_reg_session *session, assert(!ret); } +/* + * Remove buffer registry map object from the session hash table. RCU read + * side lock MUST be acquired before calling this. + */ +void buffer_reg_map_remove(struct buffer_reg_session *session, + struct buffer_reg_map *regp) +{ + int ret; + struct lttng_ht_iter iter; + + assert(session); + assert(regp); + + iter.iter.node = ®p->node.node; + ret = lttng_ht_del(session->maps, &iter); + assert(!ret); +} + /* * Destroy a buffer registry channel with the given domain. */ @@ -572,6 +761,49 @@ void buffer_reg_channel_destroy(struct buffer_reg_channel *regp, return; } +/* + * Destroy a buffer registry map with the given domain. + */ +void buffer_reg_map_destroy(struct buffer_reg_map *regp, + enum lttng_domain_type domain) +{ + if (!regp) { + return; + } + + DBG3("Buffer registry map destroy with key %" PRIu32, regp->key); + + switch (domain) { + case LTTNG_DOMAIN_UST: + { + int ret; + struct buffer_reg_map_counter *map_counter_reg, *tmp; + /* Wipe counter */ + cds_list_for_each_entry_safe(map_counter_reg, tmp, ®p->counters, lnode) { + cds_list_del(&map_counter_reg->lnode); + regp->counter_count--; + buffer_reg_map_counter_destroy(map_counter_reg, domain); + } + + if (regp->obj.ust) { + ret = ust_app_release_object(NULL, regp->obj.ust); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Buffer reg map release obj handle %d failed with ret %d", + regp->obj.ust->handle, ret); + } + free(regp->obj.ust); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + break; + } + default: + assert(0); + } + + free(regp); + return; +} + /* * Destroy a buffer registry session with the given domain. * @@ -583,6 +815,7 @@ static void buffer_reg_session_destroy(struct buffer_reg_session *regp, int ret; struct lttng_ht_iter iter; struct buffer_reg_channel *reg_chan; + struct buffer_reg_map *reg_map; DBG3("Buffer registry session destroy"); @@ -594,9 +827,16 @@ static void buffer_reg_session_destroy(struct buffer_reg_session *regp, assert(!ret); buffer_reg_channel_destroy(reg_chan, domain); } + cds_lfht_for_each_entry(regp->maps->ht, &iter.iter, reg_map, + node.node) { + ret = lttng_ht_del(regp->maps, &iter); + assert(!ret); + buffer_reg_map_destroy(reg_map, domain); + } rcu_read_unlock(); ht_cleanup_push(regp->channels); + ht_cleanup_push(regp->maps); switch (domain) { case LTTNG_DOMAIN_UST: diff --git a/src/bin/lttng-sessiond/buffer-registry.h b/src/bin/lttng-sessiond/buffer-registry.h index 0812414e5..00be7b65e 100644 --- a/src/bin/lttng-sessiond/buffer-registry.h +++ b/src/bin/lttng-sessiond/buffer-registry.h @@ -26,6 +26,14 @@ struct buffer_reg_stream { } obj; }; +struct buffer_reg_map_counter { + struct cds_list_head lnode; + union { + /* Original object data that MUST be copied over. */ + struct lttng_ust_object_data *ust; + } obj; +}; + struct buffer_reg_channel { /* This key is the same as a tracing channel key. */ uint32_t key; @@ -49,6 +57,25 @@ struct buffer_reg_channel { } obj; }; +struct buffer_reg_map { + /* This key is the same as a tracing map key. */ + uint32_t key; + /* Per cpu counter registry object of this map registry. */ + struct cds_list_head counters; + /* Total number of stream in the list. */ + uint64_t counter_count; + /* Used to ensure mutual exclusion to the counter's list. */ + pthread_mutex_t counter_list_lock; + /* Node for hash table usage. */ + struct lttng_ht_node_u64 node; + union { + /* Original object data that MUST be copied over. */ + struct lttng_ust_object_data *ust; + } obj; + + struct ustctl_daemon_counter *daemon_counter; +}; + struct buffer_reg_session { /* Registry per domain. */ union { @@ -57,6 +84,8 @@ struct buffer_reg_session { /* Contains buffer registry channel indexed by tracing channel key. */ struct lttng_ht *channels; + /* Contains buffer registry map indexed by tracing map key. */ + struct lttng_ht *maps; }; /* @@ -130,6 +159,17 @@ void buffer_reg_channel_remove(struct buffer_reg_session *session, void buffer_reg_channel_destroy(struct buffer_reg_channel *regp, enum lttng_domain_type domain); +/* Map */ +int buffer_reg_map_create(uint64_t key, struct buffer_reg_map **regp); +void buffer_reg_map_add(struct buffer_reg_session *session, + struct buffer_reg_map *map); +struct buffer_reg_map *buffer_reg_map_find(uint64_t key, + struct buffer_reg_uid *reg); +void buffer_reg_map_remove(struct buffer_reg_session *session, + struct buffer_reg_map *regp); +void buffer_reg_map_destroy(struct buffer_reg_map *regp, + enum lttng_domain_type domain); + /* Stream */ int buffer_reg_stream_create(struct buffer_reg_stream **regp); void buffer_reg_stream_add(struct buffer_reg_stream *stream, @@ -137,6 +177,13 @@ void buffer_reg_stream_add(struct buffer_reg_stream *stream, void buffer_reg_stream_destroy(struct buffer_reg_stream *regp, enum lttng_domain_type domain); +/* Map counter */ +int buffer_reg_map_counter_create(struct buffer_reg_map_counter **regp); +void buffer_reg_map_counter_add(struct buffer_reg_map_counter *map_counter, + struct buffer_reg_map *map); +void buffer_reg_map_counter_destroy(struct buffer_reg_map_counter *regp, + enum lttng_domain_type domain); + /* Global registry. */ void buffer_reg_destroy_registries(void); diff --git a/src/bin/lttng-sessiond/clear.c b/src/bin/lttng-sessiond/clear.c index 3ae70ea2f..3a08a119a 100644 --- a/src/bin/lttng-sessiond/clear.c +++ b/src/bin/lttng-sessiond/clear.c @@ -188,7 +188,6 @@ int cmd_clear_session(struct ltt_session *session, int *sock_fd) /* Flag session that trace should start automatically */ if (usess) { int int_ret = ust_app_start_trace_all(usess); - if (int_ret < 0) { ret = LTTNG_ERR_UST_START_FAIL; goto end; diff --git a/src/bin/lttng-sessiond/client.c b/src/bin/lttng-sessiond/client.c index 770a2ee44..ba6d08cbd 100644 --- a/src/bin/lttng-sessiond/client.c +++ b/src/bin/lttng-sessiond/client.c @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -767,6 +770,106 @@ end: return ret_code; } +static enum lttng_error_code receive_lttng_map(int sock, + int *sock_error, + uint32_t map_len, + struct lttng_map **_map) +{ + int ret; + ssize_t sock_recv_len; + enum lttng_error_code ret_code; + struct lttng_payload map_payload; + struct lttng_map *map = NULL; + + lttng_payload_init(&map_payload); + ret = lttng_dynamic_buffer_set_size( + &map_payload.buffer, map_len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + sock_recv_len = lttcomm_recv_unix_sock( + sock, map_payload.buffer.data, map_len); + if (sock_recv_len < 0 || sock_recv_len != map_len) { + ERR("Failed to receive map in command payload"); + *sock_error = 1; + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto end; + } + + /* Deserialize map. */ + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &map_payload, 0, -1); + + if (lttng_map_create_from_payload(&view, &map) != + map_len) { + ERR("Invalid map received as part of command payload"); + ret_code = LTTNG_ERR_INVALID_TRIGGER; + lttng_map_put(map); + goto end; + } + } + + *_map = map; + ret_code = LTTNG_OK; + +end: + return ret_code; +} + +static enum lttng_error_code receive_lttng_map_query(int sock, + int *sock_error, + uint32_t map_query_len, + struct lttng_map_query **_map_query) +{ + int ret; + ssize_t sock_recv_len; + enum lttng_error_code ret_code; + struct lttng_payload map_query_payload; + struct lttng_map_query *map_query = NULL; + + lttng_payload_init(&map_query_payload); + ret = lttng_dynamic_buffer_set_size( + &map_query_payload.buffer, map_query_len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + sock_recv_len = lttcomm_recv_unix_sock( + sock, map_query_payload.buffer.data, map_query_len); + if (sock_recv_len < 0 || sock_recv_len != map_query_len) { + ERR("Failed to receive map query in command payload"); + *sock_error = 1; + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto end; + } + + /* Deserialize map query. */ + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &map_query_payload, 0, -1); + + if (lttng_map_query_create_from_payload(&view, &map_query) != + map_query_len) { + ERR("Invalid map query received as part of command payload"); + ret_code = LTTNG_ERR_INVALID_TRIGGER; + lttng_map_query_destroy(map_query); + goto end; + } + } + + *_map_query = map_query; + ret_code = LTTNG_OK; + +end: + return ret_code; +} + /* * Version of setup_lttng_msg() without command header. */ @@ -876,6 +979,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_SESSION_LIST_ROTATION_SCHEDULES: case LTTNG_CLEAR_SESSION: case LTTNG_LIST_TRIGGERS: + case LTTNG_LIST_MAP_VALUES: need_domain = false; break; default: @@ -886,6 +990,10 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, switch (cmd_ctx->lsm.cmd_type) { case LTTNG_REGISTER_TRIGGER: case LTTNG_UNREGISTER_TRIGGER: + case LTTNG_ADD_MAP: + case LTTNG_ENABLE_MAP: + case LTTNG_DISABLE_MAP: + case LTTNG_LIST_MAP_VALUES: need_consumerd = false; break; default: @@ -986,6 +1094,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, switch (cmd_ctx->lsm.cmd_type) { case LTTNG_DISABLE_CHANNEL: case LTTNG_DISABLE_EVENT: + case LTTNG_DISABLE_MAP: switch (cmd_ctx->lsm.domain.type) { case LTTNG_DOMAIN_KERNEL: if (!cmd_ctx->session->kernel_session) { @@ -1354,6 +1463,23 @@ error_add_context: kernel_poll_pipe[1]); break; } + case LTTNG_ADD_MAP: + { + ret = cmd_add_map(cmd_ctx, *sock); + + break; + } + case LTTNG_ENABLE_MAP: + ret = cmd_enable_map(cmd_ctx->session, cmd_ctx->lsm.domain.type, + cmd_ctx->lsm.u.enable_map.map_name); + break; + case LTTNG_DISABLE_MAP: + { + ret = cmd_disable_map(cmd_ctx->session, cmd_ctx->lsm.domain.type, + cmd_ctx->lsm.u.disable_map.map_name); + + break; + } case LTTNG_PROCESS_ATTR_TRACKER_ADD_INCLUDE_VALUE: case LTTNG_PROCESS_ATTR_TRACKER_REMOVE_INCLUDE_VALUE: { @@ -1892,6 +2018,45 @@ error_add_context: ret = LTTNG_OK; break; } + case LTTNG_LIST_MAPS: + { + struct lttng_map_list *return_map_list = NULL; + size_t original_payload_size; + size_t payload_size; + + ret = setup_empty_lttng_msg(cmd_ctx); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto setup_error; + } + + original_payload_size = cmd_ctx->reply_payload.buffer.size; + + ret = cmd_list_maps(cmd_ctx->lsm.domain.type, cmd_ctx->session, + &return_map_list); + if (ret != LTTNG_OK) { + goto error; + } + + assert(return_map_list); + ret = lttng_map_list_serialize(return_map_list, + &cmd_ctx->reply_payload); + lttng_map_list_destroy(return_map_list); + if (ret) { + ERR("Failed to serialize map_list in reply to `%s` command", + lttcomm_sessiond_command_str(cmd_ctx->lsm.cmd_type)); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + payload_size = cmd_ctx->reply_payload.buffer.size - + original_payload_size; + + update_lttng_msg(cmd_ctx, 0, payload_size); + + ret = LTTNG_OK; + break; + } case LTTNG_LIST_EVENTS: { ssize_t list_ret; @@ -2343,6 +2508,60 @@ error_add_context: ret = LTTNG_OK; break; } + case LTTNG_LIST_MAP_VALUES: + { + struct lttng_map_content *return_map_content = NULL; + struct lttng_map *payload_map = NULL; + struct lttng_map_query *payload_map_query = NULL; + size_t original_reply_payload_size; + size_t reply_payload_size; + + ret = setup_empty_lttng_msg(cmd_ctx); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto setup_error; + } + + original_reply_payload_size = cmd_ctx->reply_payload.buffer.size; + + ret = receive_lttng_map(*sock, sock_error, + cmd_ctx->lsm.u.list_map_values.map_length, + &payload_map); + if (ret != LTTNG_OK) { + goto error; + } + + ret = receive_lttng_map_query(*sock, sock_error, + cmd_ctx->lsm.u.list_map_values.query_length, + &payload_map_query); + if (ret != LTTNG_OK) { + goto error; + } + + ret = cmd_list_map_values(cmd_ctx->lsm.session.name, + payload_map, payload_map_query, + &return_map_content); + if (ret != LTTNG_OK) { + goto error; + } + assert(return_map_content); + ret = lttng_map_content_serialize(return_map_content, + &cmd_ctx->reply_payload); + lttng_map_content_destroy(return_map_content); + if (ret) { + ERR("Failed to serialize key-value pair list in reply to `list map values` command"); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + reply_payload_size = cmd_ctx->reply_payload.buffer.size - + original_reply_payload_size; + + update_lttng_msg(cmd_ctx, 0, reply_payload_size); + + 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 a2af8882f..67ac9fd7b 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -30,13 +31,19 @@ #include #include #include -#include -#include +#include +#include #include #include #include +#include +#include +#include #include #include +#include "lttng/domain.h" +#include "lttng/map/map.h" +#include #include #include #include @@ -57,12 +64,14 @@ #include "lttng-syscall.h" #include "agent.h" #include "buffer-registry.h" +#include "map.h" #include "notification-thread.h" #include "notification-thread-commands.h" #include "rotate.h" #include "rotation-thread.h" #include "timer.h" #include "agent-thread.h" +#include "session.h" #include "tracker.h" #include "cmd.h" @@ -288,6 +297,340 @@ end: return ret; } +enum tracer_executed_action_state { + TRACER_EXECUTED_ACTION_STATE_REGISTER, + TRACER_EXECUTED_ACTION_STATE_UNREGISTER +}; + +static +enum lttng_error_code sync_incr_value_action_ust( + struct ltt_session *session, + const struct lttng_condition *condition, + const char *map_name, + uint64_t tracer_token, + struct lttng_map_key *key, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret_code; + const struct lttng_event_rule *event_rule; + enum lttng_condition_status cond_status; + enum lttng_event_rule_status er_status; + struct ltt_ust_session *usess = session->ust_session; + const char *pattern; + struct ltt_ust_map *map; + char *filter_expression; + struct lttng_bytecode *filter; + struct lttng_event_exclusion *exclusion; + enum lttng_event_rule_generate_exclusions_status + generate_exclusion_status; + + DBG("Syncing UST incr-value action for session '%s', map '%s'", + session->name, map_name); + if (!usess) { + DBG("No UST session"); + ret_code = LTTNG_OK; + goto end; + } + + assert(usess->domain_global.maps); + + map = trace_ust_find_map_by_name(usess->domain_global.maps, map_name); + if (!map) { + DBG("UST map \"%s\" not found", map_name); + ret_code = LTTNG_OK; + goto end; + } + + cond_status = lttng_condition_on_event_get_rule(condition, &event_rule); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Error getting on-event condition event-rule"); + ret_code = LTTNG_ERR_INVALID_MAP; + goto end; + } + + er_status = lttng_event_rule_tracepoint_get_pattern(event_rule, &pattern); + if (er_status != LTTNG_EVENT_RULE_STATUS_OK) { + /* At this point, this is a fatal error. */ + abort(); + } + + /* + * FIXME: frdeso, reuse the event notifier functions and approach to + * create the event. + */ + filter_expression = (char *) lttng_event_rule_get_filter(event_rule); + filter = (struct lttng_bytecode *)lttng_event_rule_get_filter_bytecode( + event_rule); + generate_exclusion_status = lttng_event_rule_generate_exclusions( + event_rule, &exclusion); + if (generate_exclusion_status == LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_ERROR) { + ERR("Error generating the exclusion"); + ret_code = LTTNG_ERR_EXCLUSION_INVAL; + goto end; + } + + switch (state) { + case TRACER_EXECUTED_ACTION_STATE_REGISTER: + ret_code = map_event_ust_enable_tracepoint(usess, + map, tracer_token, (char *) pattern, key, + LTTNG_EVENT_TRACEPOINT, LTTNG_EVENT_LOGLEVEL_ALL, + 0, filter_expression, filter, exclusion, false); + if (ret_code == LTTNG_ERR_UST_EVENT_ENABLED) { + ret_code = LTTNG_OK; + } else if (ret_code != LTTNG_OK) { + ERR("Enabling UST map event"); + goto end; + } + break; + case TRACER_EXECUTED_ACTION_STATE_UNREGISTER: + ret_code = map_event_ust_disable_tracepoint(usess, + map, tracer_token, (char *) pattern, key, + LTTNG_EVENT_TRACEPOINT, LTTNG_EVENT_LOGLEVEL_ALL, + 0, filter_expression, filter, exclusion, false); + if (ret_code == LTTNG_ERR_UST_EVENT_ENABLED) { + ret_code = LTTNG_OK; + } else if (ret_code != LTTNG_OK) { + ERR("Enabling UST map event"); + goto end; + } + break; + default: + abort(); + } + + ret_code = LTTNG_OK; + +end: + return ret_code; +} + +static +enum lttng_error_code sync_incr_value_action_kernel( + struct ltt_session *session, + const struct lttng_credentials *creds, + const struct lttng_condition *condition, + const char *map_name, + uint64_t tracer_token, + struct lttng_map_key *key, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret_code; + const struct lttng_event_rule *event_rule; + enum lttng_condition_status cond_status; + struct ltt_kernel_map *kmap; + struct ltt_kernel_session *ksess = session->kernel_session; + + DBG("Syncing kernel incr-value action for session '%s', map '%s'", + session->name, map_name); + + if (!ksess) { + DBG("No kernel session"); + ret_code = LTTNG_OK; + goto end; + } + + kmap = trace_kernel_get_map_by_name(map_name, ksess); + if (!kmap) { + DBG("Kernel map \"%s\" not found", map_name); + ret_code = LTTNG_OK; + goto end; + } + + cond_status = lttng_condition_on_event_get_rule(condition, &event_rule); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + ret_code = LTTNG_ERR_INVALID_MAP; + ERR("Error getting on-event condition event-rule"); + goto end; + } + + switch (state) { + case TRACER_EXECUTED_ACTION_STATE_REGISTER: + ret_code = map_event_kernel_enable_event(kmap, creds, tracer_token, + event_rule, key); + if(ret_code != LTTNG_OK) { + ERR("Error enabling event counter to the kernel tracer"); + goto end; + } + break; + case TRACER_EXECUTED_ACTION_STATE_UNREGISTER: + ret_code = map_event_kernel_disable_event(kmap, tracer_token); + if(ret_code != LTTNG_OK) { + ERR("Error disabling event counter to the kernel tracer"); + goto end; + } + break; + } + +end: + return ret_code; +} + +static +enum lttng_error_code sync_incr_value_action( + const struct lttng_credentials *creds, + const struct lttng_condition *condition, + const struct lttng_action *action, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret; + const char *session_name, *map_name; + enum lttng_action_status action_status; + enum lttng_condition_status cond_status; + const struct lttng_event_rule *event_rule; + struct ltt_session *session = NULL; + struct lttng_map_key *key; + uint64_t action_tracer_token; + + action_status = lttng_action_incr_value_get_map_name(action, + &map_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Map name not set for incr-value action"); + ret = LTTNG_ERR_INVALID_MAP; + goto end; + } + + action_status = lttng_action_incr_value_get_session_name(action, + &session_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Session name not set for incr-value action"); + ret = LTTNG_ERR_INVALID_MAP; + goto end; + } + + action_status = lttng_action_incr_value_borrow_key_mutable(action, &key); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Key not set for incr-value action"); + ret = LTTNG_ERR_INVALID_MAP; + goto end; + } + + /* Returns a refcounted reference */ + session = session_find_by_name(session_name); + if(!session) { + DBG("Session not found for incr-value action: session-name=%s", + session_name); + ret = LTTNG_OK; + goto end; + } + + cond_status = lttng_condition_on_event_get_rule(condition, &event_rule); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Error getting on-event condition event-rule"); + ret = LTTNG_ERR_INVALID_MAP; + session_put(session); + goto end; + } + + action_tracer_token = lttng_action_incr_value_get_tracer_token(action); + + switch (lttng_event_rule_get_domain_type(event_rule)) { + case LTTNG_DOMAIN_UST: + ret = sync_incr_value_action_ust(session, condition, + map_name, action_tracer_token, key, state); + if (ret == LTTNG_ERR_UST_EVENT_EXIST) { + DBG("Incr-value action already registered"); + ret = LTTNG_OK; + } + break; + case LTTNG_DOMAIN_KERNEL: + ret = sync_incr_value_action_kernel(session, creds, condition, + map_name, action_tracer_token, key, state); + if (ret == LTTNG_ERR_KERN_EVENT_EXIST) { + DBG("Incr-value action already registered"); + ret = LTTNG_OK; + } + break; + default: + abort(); + } + + goto end; +end: + if (session) { + session_put(session); + } + return ret; +} + +static +enum lttng_error_code sync_one_tracer_executed_action( + const struct lttng_credentials *creds, + const struct lttng_condition *condition, + const struct lttng_action *action, + enum tracer_executed_action_state state) +{ + enum lttng_action_type action_type; + enum lttng_error_code ret; + + action_type = lttng_action_get_type(action); + assert(action_type != LTTNG_ACTION_TYPE_GROUP); + + switch (action_type) { + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + DBG("Action type \"%s\" is a tracer executed action.", + lttng_action_type_string(action_type)); + + ret = sync_incr_value_action(creds, condition, action, state); + if (ret != LTTNG_OK) { + ERR("Error syncing increment value action to the tracer"); + } + break; + default: + DBG("Action type \"%s\" is not a tracer executed action.", + lttng_action_type_string(action_type)); + ret = LTTNG_OK; + goto end; + } + +end: + return ret; +} + +static +enum lttng_error_code sync_all_tracer_executed_actions( + const struct lttng_trigger *trigger, + const struct lttng_credentials *cmd_creds, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret; + unsigned int i, count; + enum lttng_action_status action_status; + enum lttng_action_type action_type; + const struct lttng_action *action; + const struct lttng_condition *condition; + + condition = lttng_trigger_get_const_condition(trigger); + action = lttng_trigger_get_const_action(trigger); + + action_type = lttng_action_get_type(action); + + DBG("Iterating over all actions of trigger \"%s\" to sync any tracer executed actions", + trigger->name); + + if (action_type != LTTNG_ACTION_TYPE_GROUP) { + ret = sync_one_tracer_executed_action(cmd_creds, condition, action, + state); + } else { + 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 *inner_action = + lttng_action_group_get_at_index(action, i); + + ret = sync_one_tracer_executed_action(cmd_creds, condition, + inner_action, state); + if (ret != LTTNG_OK) { + ERR("Error syncing tracer executed action"); + goto end; + } + } + } + +end: + return ret; +} + /* * Fill lttng_channel array of all channels. */ @@ -1523,6 +1866,237 @@ end: return ret; } +enum lttng_error_code cmd_add_map(struct command_ctx *cmd_ctx, int sock) +{ + int ret; + enum lttng_error_code ret_code; + size_t map_len; + struct lttng_payload map_payload; + ssize_t sock_recv_len; + struct lttng_map *map = NULL; + const struct lttng_credentials cmd_creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid), + }; + + lttng_payload_init(&map_payload); + map_len = (size_t) cmd_ctx->lsm.u.add_map.length; + ret = lttng_dynamic_buffer_set_size( + &map_payload.buffer, map_len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + sock_recv_len = lttcomm_recv_unix_sock( + sock, map_payload.buffer.data, map_len); + if (sock_recv_len < 0 || sock_recv_len != map_len) { + ERR("Failed to receive \"register map\" command payload"); + ret_code = LTTNG_ERR_INVALID_MAP; + goto end; + } + + /* Deserialize map. */ + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &map_payload, 0, -1); + + if (lttng_map_create_from_payload(&view, &map) != map_len) { + ERR("Invalid map payload received in \"add map\" command"); + ret_code = LTTNG_ERR_INVALID_MAP; + goto end; + } + } + + switch (lttng_map_get_domain(map)) { + case LTTNG_DOMAIN_KERNEL: + ret_code = map_kernel_add(cmd_ctx->session->kernel_session, map); + if (ret_code != LTTNG_OK) { + ERR("Creating a new kernel map: %s", lttng_strerror(ret_code)); + goto end; + } + + ret_code = LTTNG_OK; + break; + case LTTNG_DOMAIN_UST: + ret = map_ust_add(cmd_ctx->session->ust_session, map); + if (ret) { + ERR("Creating a new UST map: %s", lttng_strerror(-ret)); + ret_code = ret; + goto end; + } + + ret_code = LTTNG_OK; + break; + default: + abort(); + } + + + { + struct lttng_triggers *triggers = NULL; + enum lttng_trigger_status t_status; + unsigned int count, i; + + /* + * FRDESO: beware of moving this code. This is currently not + * racy because this is executed by the client thread and the + * client thread is the thread registering new triggers. If + * this code is relocate special care must be taken. + */ + ret_code = notification_thread_command_list_triggers( + notification_thread_handle, 0, &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; + lttng_triggers_destroy(triggers); + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_trigger *trigger; + + trigger = lttng_triggers_get_at_index(triggers, i); + assert(trigger); + + ret_code = sync_all_tracer_executed_actions(trigger, + &cmd_creds, TRACER_EXECUTED_ACTION_STATE_REGISTER); + assert(ret_code == LTTNG_OK); + } + + lttng_triggers_destroy(triggers); + } + + lttng_map_put(map); + +end: + lttng_payload_reset(&map_payload); + return ret_code; +} + +enum lttng_error_code cmd_enable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name) +{ + struct ltt_ust_session *usess = session->ust_session; + enum lttng_error_code ret_code; + + DBG("Enabling map %s for session %s", map_name, session->name); + + rcu_read_lock(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_map *kmap; + struct ltt_kernel_session *ksess = session->kernel_session; + + kmap = trace_kernel_get_map_by_name(map_name, ksess); + if (kmap == NULL) { + ret_code = LTTNG_ERR_KERNEL_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_kernel_enable(ksess, kmap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_map *umap; + struct lttng_ht *map_ht; + + map_ht = usess->domain_global.maps; + + umap = trace_ust_find_map_by_name(map_ht, map_name); + if (umap == NULL) { + ret_code = LTTNG_ERR_UST_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_ust_enable(usess, umap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + default: + abort(); + } + + ret_code = LTTNG_OK; +error: + rcu_read_unlock(); + return ret_code; +} + +enum lttng_error_code cmd_disable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name) +{ + enum lttng_error_code ret_code; + + rcu_read_lock(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_map *kmap; + struct ltt_kernel_session *ksess = session->kernel_session; + + kmap = trace_kernel_get_map_by_name(map_name, ksess); + if (kmap == NULL) { + ret_code = LTTNG_ERR_KERNEL_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_kernel_disable(ksess, kmap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_map *umap; + struct lttng_ht *map_ht; + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + map_ht = usess->domain_global.maps; + + umap = trace_ust_find_map_by_name(map_ht, map_name); + if (umap == NULL) { + ret_code = LTTNG_ERR_UST_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_ust_disable(usess, umap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + default: + abort(); + } + + ret_code = LTTNG_OK; + +error: + rcu_read_unlock(); + return ret_code; +} + enum lttng_error_code cmd_process_attr_tracker_get_tracking_policy( struct ltt_session *session, enum lttng_domain_type domain, @@ -2594,7 +3168,7 @@ ssize_t cmd_list_syscalls(struct lttng_event **events) int cmd_start_trace(struct ltt_session *session) { enum lttng_error_code ret; - unsigned long nb_chan = 0; + unsigned long nb_chan = 0, nb_map = 0; struct ltt_kernel_session *ksession; struct ltt_ust_session *usess; const bool session_rotated_after_last_stop = @@ -2637,11 +3211,13 @@ int cmd_start_trace(struct ltt_session *session) */ if (usess && usess->domain_global.channels) { nb_chan += lttng_ht_get_count(usess->domain_global.channels); + nb_map += lttng_ht_get_count(usess->domain_global.maps); } if (ksession) { nb_chan += ksession->channel_count; + nb_map += ksession->map_count; } - if (!nb_chan) { + if (!nb_chan && !nb_map) { ret = LTTNG_ERR_NO_CHANNEL; goto error; } @@ -3653,6 +4229,64 @@ end: return ret; } +enum lttng_error_code cmd_list_maps(enum lttng_domain_type domain, + struct ltt_session *session, + struct lttng_map_list **return_map_list) +{ + enum lttng_error_code ret_code; + struct lttng_map_list *map_list = NULL; + + map_list = lttng_map_list_create(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + if (session->kernel_session != NULL) { + struct ltt_kernel_map *kmap; + cds_list_for_each_entry(kmap, + &session->kernel_session->map_list.head, list) { + enum lttng_map_status map_status; + map_status = lttng_map_list_add(map_list, kmap->map); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error appending kernel map to list"); + ret_code = LTTNG_ERR_FATAL; + break; + } + } + + } + break; + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_map *umap; + struct lttng_ht_iter iter; + + rcu_read_lock(); + cds_lfht_for_each_entry(session->ust_session->domain_global.maps->ht, + &iter.iter, umap, node.node) { + enum lttng_map_status map_status; + map_status = lttng_map_list_add(map_list, umap->map); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error appending UST map to list"); + ret_code = LTTNG_ERR_FATAL; + break; + } + } + rcu_read_unlock(); + break; + } + default: + ret_code = LTTNG_ERR_UND; + goto end; + } + + *return_map_list = map_list; + map_list = NULL; + ret_code = LTTNG_OK; +end: + lttng_map_list_destroy(map_list); + return ret_code; +} + /* * Command LTTNG_LIST_EVENTS processed by the client thread. */ @@ -4293,21 +4927,82 @@ end: return ret; } -static enum lttng_error_code trigger_modifies_event_notifier( - const struct lttng_trigger *trigger, bool *adds_event_notifier) +static +enum lttng_error_code synchronize_tracer_notifier_register( + struct notification_thread_handle *notification_thread, + struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds) { - enum lttng_error_code ret_code = LTTNG_OK; - const struct lttng_condition *condition = NULL; + enum lttng_error_code ret_code; + struct lttng_condition *condition = lttng_trigger_get_condition(trigger); + const char *trigger_name; + uid_t trigger_owner; + enum lttng_trigger_status trigger_status; + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction( + trigger); - condition = lttng_trigger_get_const_condition(trigger); - if (!condition) { - ret_code = LTTNG_ERR_INVALID_TRIGGER; - goto end; + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_owner); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? trigger_name : "(unnamed)"; + + session_lock_list(); + switch (trigger_domain) { + case LTTNG_DOMAIN_KERNEL: + { + ret_code = kernel_register_event_notifier(trigger, cmd_creds); + if (ret_code != LTTNG_OK) { + enum lttng_error_code notif_thread_unregister_ret; + + notif_thread_unregister_ret = + notification_thread_command_unregister_trigger( + notification_thread, trigger); + + if (notif_thread_unregister_ret != LTTNG_OK) { + /* Return the original error code. */ + ERR("Failed to unregister trigger from notification thread during error recovery: trigger name = '%s', trigger owner uid = %d, error code = %d", + trigger_name, + (int) trigger_owner, + ret_code); + } + } + break; + } + case LTTNG_DOMAIN_UST: + ust_app_global_update_all_event_notifier_rules(); + break; + case LTTNG_DOMAIN_NONE: + abort(); + default: + { + /* Agent domains. */ + struct agent *agt = agent_find_by_event_notifier_domain( + trigger_domain); + if (!agt) { + agt = agent_create(trigger_domain); + if (!agt) { + ret_code = LTTNG_ERR_NOMEM; + goto end_unlock_session_list; + } + agent_add(agt, trigger_agents_ht_by_domain); + } + + ret_code = trigger_agent_enable(trigger, agt); + if (ret_code != LTTNG_OK) { + goto end_unlock_session_list; + } + + break; + } } - *adds_event_notifier = lttng_condition_get_type(condition) == - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; -end: + ret_code = LTTNG_OK; +end_unlock_session_list: + session_unlock_list(); return ret_code; } @@ -4317,8 +5012,8 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c struct lttng_trigger **return_trigger) { enum lttng_error_code ret_code; - bool must_update_event_notifiers; const char *trigger_name; + const struct lttng_condition *condition; uid_t trigger_owner; enum lttng_trigger_status trigger_status; @@ -4334,7 +5029,7 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c trigger_name, (int) trigger_owner, (int) lttng_credentials_get_uid(cmd_creds)); - /* +/* * Validate the trigger credentials against the command credentials. * Only the root user can register a trigger with non-matching * credentials. @@ -4384,72 +5079,30 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? trigger_name : "(unnamed)"; - ret_code = trigger_modifies_event_notifier(trigger, &must_update_event_notifiers); - if (ret_code != LTTNG_OK) { - ERR("Failed to determine if event modifies event notifiers: trigger name = '%s', trigger owner uid = %d, error code = %d", - trigger_name, (int) trigger_owner, ret_code); - goto end; - } - /* * Synchronize tracers if the trigger adds an event notifier. */ - if (must_update_event_notifiers) { - const enum lttng_domain_type trigger_domain = - lttng_trigger_get_underlying_domain_type_restriction(trigger); - - session_lock_list(); - switch (trigger_domain) { - case LTTNG_DOMAIN_KERNEL: - { - ret_code = kernel_register_event_notifier( - trigger, cmd_creds); - if (ret_code != LTTNG_OK) { - const enum lttng_error_code notif_thread_unregister_ret = - notification_thread_command_unregister_trigger( - notification_thread, - trigger); - - if (notif_thread_unregister_ret != LTTNG_OK) { - /* Return the original error code. */ - ERR("Failed to unregister trigger from notification thread during error recovery: trigger name = '%s', trigger owner uid = %d, error code = %d", - trigger_name, - (int) trigger_owner, - ret_code); - } - } - break; + if (lttng_trigger_needs_tracer_notifier(trigger)) { + ret_code = synchronize_tracer_notifier_register(notification_thread, + trigger, cmd_creds); + if (ret_code != LTTNG_OK) { + ERR("Error registering tracer notifier"); + goto end; } - case LTTNG_DOMAIN_UST: - ust_app_global_update_all_event_notifier_rules(); - break; - case LTTNG_DOMAIN_NONE: - abort(); - default: - { - /* Agent domains. */ - struct agent *agt = agent_find_by_event_notifier_domain( - trigger_domain); - - if (!agt) { - agt = agent_create(trigger_domain); - if (!agt) { - ret_code = LTTNG_ERR_NOMEM; - goto end_unlock_session_list; - } - agent_add(agt, trigger_agents_ht_by_domain); - } - - ret_code = trigger_agent_enable(trigger, agt); - if (ret_code != LTTNG_OK) { - goto end_unlock_session_list; - } + } - break; - } - } + condition = lttng_trigger_get_const_condition(trigger); + /* TODO: Extract condition below to lttng_trigger internal function */ + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT) { + session_lock_list(); + ret_code = sync_all_tracer_executed_actions(trigger, + cmd_creds, TRACER_EXECUTED_ACTION_STATE_REGISTER); session_unlock_list(); + if (ret_code != LTTNG_OK) { + ERR("Error registering tracer executed actions"); + goto end; + } } /* @@ -4467,6 +5120,57 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c } end: return ret_code; +} + +static +enum lttng_error_code synchronize_tracer_notifier_unregister( + const struct lttng_trigger *trigger) +{ + enum lttng_error_code ret_code; + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction( + trigger); + + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); + + session_lock_list(); + switch (trigger_domain) { + case LTTNG_DOMAIN_KERNEL: + ret_code = kernel_unregister_event_notifier(trigger); + break; + case LTTNG_DOMAIN_UST: + ust_app_global_update_all_event_notifier_rules(); + break; + case LTTNG_DOMAIN_NONE: + abort(); + default: + { + /* Agent domains. */ + struct agent *agt = agent_find_by_event_notifier_domain( + trigger_domain); + if (!agt) { + agt = agent_create(trigger_domain); + if (!agt) { + ret_code = LTTNG_ERR_NOMEM; + goto end_unlock_session_list; + } + agent_add(agt, trigger_agents_ht_by_domain); + } + + ret_code = trigger_agent_disable(trigger, agt); + if (ret_code != LTTNG_OK) { + goto end_unlock_session_list; + } + + break; + } + } + + ret_code = LTTNG_OK; + end_unlock_session_list: session_unlock_list(); return ret_code; @@ -4477,15 +5181,14 @@ enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd struct notification_thread_handle *notification_thread) { enum lttng_error_code ret_code; - bool must_update_event_notifiers; const char *trigger_name; + struct lttng_trigger *real_trigger; uid_t trigger_owner; enum lttng_trigger_status trigger_status; trigger_status = lttng_trigger_get_name(trigger, &trigger_name); trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? trigger_name : "(unnamed)"; - trigger_status = lttng_trigger_get_owner_uid( - trigger, &trigger_owner); + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_owner); assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); DBG("Running unregister trigger command: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", @@ -4509,18 +5212,20 @@ enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd } } - ret_code = trigger_modifies_event_notifier(trigger, &must_update_event_notifiers); + ret_code = notification_thread_command_get_trigger( + notification_thread, trigger, &real_trigger); if (ret_code != LTTNG_OK) { - ERR("Failed to determine if event modifies event notifiers: trigger name = '%s', trigger owner uid = %d, error code = %d", + DBG("Failed to get the real trigger from notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", trigger_name, (int) trigger_owner, ret_code); goto end; } - ret_code = notification_thread_command_unregister_trigger(notification_thread, - trigger); + ret_code = notification_thread_command_unregister_trigger( + notification_thread, real_trigger); if (ret_code != LTTNG_OK) { DBG("Failed to unregister trigger from notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", trigger_name, (int) trigger_owner, ret_code); + goto end; } /* @@ -4529,56 +5234,28 @@ enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd * the tracers from producing notifications associated with this * event notifier. */ - if (must_update_event_notifiers) { - const enum lttng_domain_type trigger_domain = - lttng_trigger_get_underlying_domain_type_restriction( - trigger); - - session_lock_list(); - switch (trigger_domain) { - case LTTNG_DOMAIN_KERNEL: - { - ret_code = kernel_unregister_event_notifier( - trigger); - break; + if (lttng_trigger_needs_tracer_notifier(real_trigger)) { + ret_code = synchronize_tracer_notifier_unregister(real_trigger); + if (ret_code != LTTNG_OK) { + ERR("Error unregistering trigger to tracer."); + goto end; } - case LTTNG_DOMAIN_UST: - ust_app_global_update_all_event_notifier_rules(); - break; - case LTTNG_DOMAIN_NONE: - abort(); - default: - { - /* Agent domains. */ - struct agent *agt = agent_find_by_event_notifier_domain( - trigger_domain); - - if (!agt) { - agt = agent_create(trigger_domain); - if (!agt) { - ret_code = LTTNG_ERR_NOMEM; - goto end_unlock_session_list; - } - agent_add(agt, trigger_agents_ht_by_domain); - } - - ret_code = trigger_agent_disable(trigger, agt); - if (ret_code != LTTNG_OK) { - goto end_unlock_session_list; - } - break; - } - } + } - session_unlock_list(); + session_lock_list(); + ret_code = sync_all_tracer_executed_actions(real_trigger, + cmd_creds, TRACER_EXECUTED_ACTION_STATE_UNREGISTER); + session_unlock_list(); + if (ret_code != LTTNG_OK) { + ERR("Error registering tracer executed actions"); + goto end; } + lttng_trigger_put(real_trigger); end: return ret_code; -end_unlock_session_list: - session_unlock_list(); - return ret_code;} +} int cmd_list_triggers(struct command_ctx *cmd_ctx, struct notification_thread_handle *notification_thread, @@ -4603,6 +5280,76 @@ end: lttng_triggers_destroy(triggers); return ret; } + +int cmd_list_map_values(const char *session_name, + const struct lttng_map *map, + const struct lttng_map_query *map_query, + struct lttng_map_content **return_map_content) +{ + enum lttng_error_code ret; + struct ltt_session *session; + enum lttng_domain_type domain; + const char *map_name; + enum lttng_map_status map_status; + + /* Returns a refcounted reference */ + session = session_find_by_name(session_name); + if (!session) { + DBG("Session '%s' not found", session_name); + ret = LTTNG_ERR_SESS_NOT_FOUND; + goto end; + } + + domain = lttng_map_get_domain(map); + + map_status = lttng_map_get_name(map, &map_name); + assert(map_status == LTTNG_MAP_STATUS_OK); + + if (domain == LTTNG_DOMAIN_KERNEL) { + if (session->kernel_session) { + struct ltt_kernel_map *kmap; + + kmap = trace_kernel_get_map_by_name(map_name, + session->kernel_session); + if (kmap) { + ret = kernel_list_map_values(kmap, map_query, + return_map_content); + if (ret != LTTNG_OK) { + ERR("Error listing kernel map '%s' values", map_name); + goto end; + } + } else { + DBG("No kernel map '%s' in session '%s'", map_name, session_name); + } + } + } else if (domain == LTTNG_DOMAIN_UST) { + if (session->ust_session) { + struct ltt_ust_map *umap; + struct ltt_ust_session *usess = session->ust_session; + + umap = trace_ust_find_map_by_name( + usess->domain_global.maps, map_name); + if (umap) { + ret = ust_app_map_list_values(usess, umap, + map_query, return_map_content); + if (ret) { + ret = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + ERR("Error listing UST map '%s' values", map_name); + goto end; + } + } else { + DBG("No UST map '%s' in session '%s'", map_name, session_name); + } + } + } + + + ret = LTTNG_OK; +end: + session_put(session); + 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 dcda2365d..898eba2ed 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -10,10 +10,12 @@ #include "context.h" #include "lttng-sessiond.h" +#include "lttng/map/map.h" #include "lttng/tracker.h" #include "session.h" #include + struct notification_thread_handle; /* @@ -49,6 +51,13 @@ int cmd_disable_channel(struct ltt_session *session, int cmd_enable_channel(struct ltt_session *session, const struct lttng_domain *domain, const struct lttng_channel *attr, int wpipe); +enum lttng_error_code cmd_add_map(struct command_ctx *cmd_ctx, int sock); + +enum lttng_error_code cmd_enable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name); + +enum lttng_error_code cmd_disable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name); /* Process attribute tracker commands */ enum lttng_error_code cmd_process_attr_tracker_get_tracking_policy( @@ -114,6 +123,9 @@ ssize_t cmd_list_events(enum lttng_domain_type domain, struct lttng_payload *payload); ssize_t cmd_list_channels(enum lttng_domain_type domain, struct ltt_session *session, struct lttng_channel **channels); +enum lttng_error_code cmd_list_maps(enum lttng_domain_type domain, + struct ltt_session *session, + struct lttng_map_list **return_map_list); ssize_t cmd_list_domains(struct ltt_session *session, struct lttng_domain **domains); void cmd_list_lttng_sessions(struct lttng_session *sessions, @@ -167,6 +179,10 @@ int cmd_rotation_set_schedule(struct ltt_session *session, uint64_t value, struct notification_thread_handle *notification_thread_handle); +int cmd_list_map_values(const char *session_name, const struct lttng_map *map, + const struct lttng_map_query *map_query, + struct lttng_map_content **return_map_content); + const struct cmd_completion_handler *cmd_pop_completion_handler(void); int start_kernel_session(struct ltt_kernel_session *ksess); int stop_kernel_session(struct ltt_kernel_session *ksess); diff --git a/src/bin/lttng-sessiond/condition-internal.c b/src/bin/lttng-sessiond/condition-internal.c index dc21ca3af..bde25ea73 100644 --- a/src/bin/lttng-sessiond/condition-internal.c +++ b/src/bin/lttng-sessiond/condition-internal.c @@ -13,9 +13,10 @@ #include #include #include -#include -#include +#include +#include #include +#include #include "condition-internal.h" static @@ -95,7 +96,7 @@ unsigned long lttng_condition_session_rotation_hash( } static -unsigned long lttng_condition_event_rule_hash( +unsigned long lttng_condition_on_event_hash( const struct lttng_condition *condition) { unsigned long hash, condition_type; @@ -103,8 +104,7 @@ unsigned long lttng_condition_event_rule_hash( const struct lttng_event_rule *event_rule; condition_type = (unsigned long) condition->type; - - condition_status = lttng_condition_event_rule_get_rule(condition, + condition_status = lttng_condition_on_event_get_rule(condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); @@ -128,10 +128,41 @@ 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); + case LTTNG_CONDITION_TYPE_ON_EVENT: + return lttng_condition_on_event_hash(condition); default: //ERR("[notification-thread] Unexpected condition type caught"); abort(); } } + +LTTNG_HIDDEN +struct lttng_condition *lttng_condition_copy(const struct lttng_condition *condition) +{ + int ret; + struct lttng_payload copy_buffer; + struct lttng_condition *copy = NULL; + + lttng_payload_init(©_buffer); + + ret = lttng_condition_serialize(condition, ©_buffer); + if (ret < 0) { + goto end; + } + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + ©_buffer, 0, -1); + ret = lttng_condition_create_from_payload( + &view, ©); + if (ret < 0) { + copy = NULL; + goto end; + } + } + +end: + lttng_payload_reset(©_buffer); + return copy; +} diff --git a/src/bin/lttng-sessiond/condition-internal.h b/src/bin/lttng-sessiond/condition-internal.h index 2863f7bd8..270a8b1af 100644 --- a/src/bin/lttng-sessiond/condition-internal.h +++ b/src/bin/lttng-sessiond/condition-internal.h @@ -17,4 +17,6 @@ */ unsigned long lttng_condition_hash(const struct lttng_condition *condition); +struct lttng_condition *lttng_condition_copy( + const struct lttng_condition *condition); #endif /* LTTNG_SESSIOND_CONDITION_INTERNAL_H */ diff --git a/src/bin/lttng-sessiond/dispatch.c b/src/bin/lttng-sessiond/dispatch.c index d33a3cba8..5748223d7 100644 --- a/src/bin/lttng-sessiond/dispatch.c +++ b/src/bin/lttng-sessiond/dispatch.c @@ -63,10 +63,13 @@ static void update_ust_app(int app_sock) /* For all tracing session(s) */ cds_list_for_each_entry_safe(sess, stmp, &session_list->head, list) { + if (!session_get(sess)) { continue; } session_lock(sess); + + if (!sess->active || !sess->ust_session) { goto unlock_session; } diff --git a/src/bin/lttng-sessiond/event-notifier-error-accounting.c b/src/bin/lttng-sessiond/event-notifier-error-accounting.c new file mode 100644 index 000000000..aea054866 --- /dev/null +++ b/src/bin/lttng-sessiond/event-notifier-error-accounting.c @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "event-notifier-error-accounting.h" +#include "lttng-ust-error.h" +#include "ust-app.h" + +struct index_ht_entry { + struct lttng_ht_node_u64 node; + uint64_t error_counter_index; + struct rcu_head rcu_head; +}; + +struct error_account_entry { + struct lttng_ht_node_u64 node; + struct rcu_head rcu_head; + struct ustctl_daemon_counter *daemon_counter; + /* + * Those `lttng_ust_object_data` are anonymous handles to the counters + * objects. + * They are only used to be duplicated for each new applications of the + * user. To destroy them, call with the `sock` parameter set to -1. + * e.g. `ustctl_release_object(-1, data)`; + */ + struct lttng_ust_object_data *counter; + struct lttng_ust_object_data **cpu_counters; + int nr_counter_cpu_fds; +}; + +struct kernel_error_account_entry { + int kernel_event_notifier_error_counter_fd; +}; + +static struct kernel_error_account_entry kernel_error_accountant = { 0 }; + +/* Hashtable mapping event notifier token to index_ht_entry */ +static struct lttng_ht *error_counter_indexes_ht; + +/* Hashtable mapping uid to error_account_entry */ +static struct lttng_ht *error_counter_uid_ht; + +static uint64_t error_counter_size = 0; +struct lttng_index_allocator *index_allocator; + +static inline +const char *error_accounting_status_str( + enum event_notifier_error_accounting_status status) +{ + switch (status) { + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK: + return "OK"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR: + return "ERROR"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND: + return "NOT_FOUND"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM: + return "NOMEM"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE: + return "NO_INDEX_AVAILABLE"; + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_APP_DEAD: + return "APP_DEAD"; + default: + abort(); + } +} + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_init(uint64_t nb_bucket) +{ + enum event_notifier_error_accounting_status status; + + index_allocator = lttng_index_allocator_create(nb_bucket); + if (!index_allocator) { + ERR("Failed to allocate event notifier error counter index"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM; + goto error_index_allocator; + } + + error_counter_indexes_ht = lttng_ht_new(16, LTTNG_HT_TYPE_U64); + error_counter_uid_ht = lttng_ht_new(16, LTTNG_HT_TYPE_U64); + error_counter_size = nb_bucket; + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + +error_index_allocator: + return status; +} + +static +enum event_notifier_error_accounting_status get_error_counter_index_for_token( + uint64_t tracer_token, uint64_t *error_counter_index) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + struct index_ht_entry *index_entry;; + enum event_notifier_error_accounting_status status; + + lttng_ht_lookup(error_counter_indexes_ht, &tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (node) { + index_entry = caa_container_of(node, struct index_ht_entry, node); + *error_counter_index = index_entry->error_counter_index; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + } else { + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND; + } + + return status; +} + +#ifdef HAVE_LIBLTTNG_UST_CTL +static +struct error_account_entry *get_uid_accounting_entry(const struct ust_app *app) +{ + struct error_account_entry *entry; + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + uint64_t key = app->uid; + + lttng_ht_lookup(error_counter_uid_ht, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if(node == NULL) { + entry = NULL; + } else { + entry = caa_container_of(node, struct error_account_entry, node); + } + + return entry; +} + +static +struct error_account_entry *create_uid_accounting_entry( + const struct ust_app *app) +{ + int i, ret; + struct ustctl_counter_dimension dimension[1] = {0}; + struct ustctl_daemon_counter *daemon_counter; + struct lttng_ust_object_data *counter, **counter_cpus; + int *counter_cpu_fds; + struct error_account_entry *entry = NULL; + + entry = zmalloc(sizeof(struct error_account_entry)); + if (!entry) { + PERROR("Allocating event notifier error acounting entry") + goto error; + } + + entry->nr_counter_cpu_fds = ustctl_get_nr_cpu_per_counter(); + counter_cpu_fds = zmalloc(entry->nr_counter_cpu_fds * sizeof(*counter_cpu_fds)); + if (!counter_cpu_fds) { + ret = -1; + goto error_counter_cpu_fds_alloc; + } + + counter_cpus = zmalloc(entry->nr_counter_cpu_fds * sizeof(**counter_cpus)); + if (!counter_cpus) { + ret = -1; + goto error_counter_cpus_alloc; + } + + for (i = 0; i < entry->nr_counter_cpu_fds; i++) { + counter_cpu_fds[i] = shm_create_anonymous("event-notifier-error-accounting"); + //FIXME error handling + } + + + dimension[0].size = error_counter_size; + dimension[0].has_underflow = false; + dimension[0].has_overflow = false; + + daemon_counter = ustctl_create_counter(1, dimension, 0, -1, + entry->nr_counter_cpu_fds, counter_cpu_fds, + USTCTL_COUNTER_BITNESS_32, + USTCTL_COUNTER_ARITHMETIC_MODULAR, + USTCTL_COUNTER_ALLOC_PER_CPU, + false); + assert(daemon_counter); + + ret = ustctl_create_counter_data(daemon_counter, &counter); + assert(ret == 0); + + for (i = 0; i < entry->nr_counter_cpu_fds; i++) { + ret = ustctl_create_counter_cpu_data(daemon_counter, i, + &counter_cpus[i]); + assert(ret == 0); + } + + entry->daemon_counter = daemon_counter; + entry->counter = counter; + entry->cpu_counters = counter_cpus; + + lttng_ht_node_init_u64(&entry->node, app->uid); + lttng_ht_add_unique_u64(error_counter_uid_ht, &entry->node); + + free(counter_cpu_fds); + + goto end; + +error_counter_cpus_alloc: + free(counter_cpu_fds); +error_counter_cpu_fds_alloc: + free(entry); +error: + entry = NULL; +end: + return entry; +} + +static +enum event_notifier_error_accounting_status send_counter_data_to_ust( + struct ust_app *app, + struct lttng_ust_object_data *new_counter) +{ + int ret; + enum event_notifier_error_accounting_status status; + + /* Attach counter to trigger group */ + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_data_to_ust(app->sock, + app->event_notifier_group.object->handle, new_counter); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Error ustctl send counter data to app pid: %d with ret %d", + app->pid, ret); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + } else { + DBG3("UST app send counter data to ust failed. Application is dead."); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_APP_DEAD; + } + goto end; + } + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +static +enum event_notifier_error_accounting_status send_counter_cpu_data_to_ust( + struct ust_app *app, + struct lttng_ust_object_data *counter, + struct lttng_ust_object_data *counter_cpu) +{ + int ret; + enum event_notifier_error_accounting_status status; + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_cpu_data_to_ust(app->sock, + counter, counter_cpu); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("Error ustctl send counter cpu data to app pid: %d with ret %d", + app->pid, ret); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + } else { + DBG3("UST app send counter cpu data to ust failed. Application is dead."); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_APP_DEAD; + } + goto end; + } + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +enum event_notifier_error_accounting_status event_notifier_error_accounting_register_app( + struct ust_app *app) +{ + int ret; + uint64_t i; + struct lttng_ust_object_data *new_counter; + struct error_account_entry *entry; + enum event_notifier_error_accounting_status status; + + /* + * Check if we already have a error counter for the user id of this + * app. If not, create one. + */ + rcu_read_lock(); + entry = get_uid_accounting_entry(app); + if (entry == NULL) { + entry = create_uid_accounting_entry(app); + } + + /* Duplicate counter object data*/ + ret = ustctl_duplicate_ust_object_data(&new_counter, + entry->counter); + assert(ret == 0); + + status = send_counter_data_to_ust(app, new_counter); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error sending counter data to UST tracer: status=%s", + error_accounting_status_str(status)); + goto end; + } + + + app->event_notifier_group.counter = new_counter; + app->event_notifier_group.nr_counter_cpu = entry->nr_counter_cpu_fds; + app->event_notifier_group.counter_cpu = + zmalloc(entry->nr_counter_cpu_fds * sizeof(struct lttng_ust_object_data)); + + for (i = 0; i < entry->nr_counter_cpu_fds; i++) { + struct lttng_ust_object_data *new_counter_cpu = NULL; + + ret = ustctl_duplicate_ust_object_data(&new_counter_cpu, + entry->cpu_counters[i]); + assert(ret == 0); + + status = send_counter_cpu_data_to_ust(app, new_counter, + new_counter_cpu); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error sending counter cpu data to UST tracer: status=%s", + error_accounting_status_str(status)); + goto end; + } + app->event_notifier_group.counter_cpu[i] = new_counter_cpu; + } + +end: + rcu_read_unlock(); + return status; +} + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_unregister_app(struct ust_app *app) +{ + enum event_notifier_error_accounting_status status; + struct error_account_entry *entry; + int i; + + rcu_read_lock(); + entry = get_uid_accounting_entry(app); + if (entry == NULL) { + ERR("Event notitifier error accounting entry not found on app teardown"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + for (i = 0; i < app->event_notifier_group.nr_counter_cpu; i++) { + ustctl_release_object(app->sock, + app->event_notifier_group.counter_cpu[i]); + free(app->event_notifier_group.counter_cpu[i]); + } + + free(app->event_notifier_group.counter_cpu); + + ustctl_release_object(app->sock, app->event_notifier_group.counter); + free(app->event_notifier_group.counter); + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + rcu_read_unlock(); + return status; +} + +static +enum event_notifier_error_accounting_status +event_notifier_error_accounting_ust_get_count( + const struct lttng_trigger *trigger, uint64_t *count) +{ + struct lttng_ht_iter iter; + struct error_account_entry *uid_entry; + uint64_t error_counter_index, global_sum = 0; + enum event_notifier_error_accounting_status status; + size_t dimension_indexes[1]; + + /* + * Go over all error counters (ignoring uid) as a trigger (and trigger + * errors) can be generated from any applications that this session + * daemon is managing. + */ + + rcu_read_lock(); + + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), &error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + + dimension_indexes[0] = error_counter_index; + + cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter, + uid_entry, node.node) { + int ret; + int64_t local_value = 0; + bool overflow = 0, underflow = 0; + ret = ustctl_counter_aggregate(uid_entry->daemon_counter, + dimension_indexes, &local_value, &overflow, + &underflow); + assert(ret == 0); + + /* should always be zero or above. */ + assert(local_value >= 0); + global_sum += (uint64_t) local_value; + + } + + + *count = global_sum; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + +end: + rcu_read_unlock(); + return status; +} + +static +enum event_notifier_error_accounting_status event_notifier_error_accounting_ust_clear( + const struct lttng_trigger *trigger) +{ + struct lttng_ht_iter iter; + struct error_account_entry *uid_entry; + uint64_t error_counter_index; + enum event_notifier_error_accounting_status status; + size_t dimension_indexes[1]; + + /* + * Go over all error counters (ignoring uid) as a trigger (and trigger + * errors) can be generated from any applications that this session + * daemon is managing. + */ + + rcu_read_lock(); + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + + dimension_indexes[0] = error_counter_index; + + cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter, + uid_entry, node.node) { + int ret; + ret = ustctl_counter_clear(uid_entry->daemon_counter, + dimension_indexes); + assert(ret == 0); + } + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + rcu_read_unlock(); + return status; +} +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +static +enum event_notifier_error_accounting_status event_notifier_error_accounting_kernel_clear( + const struct lttng_trigger *trigger) +{ + int ret; + uint64_t error_counter_index; + enum event_notifier_error_accounting_status status; + struct lttng_kernel_counter_clear counter_clear = {0}; + + rcu_read_lock(); + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + + counter_clear.index.number_dimensions = 1; + counter_clear.index.dimension_indexes[0] = error_counter_index; + + ret = kernctl_counter_clear( + kernel_error_accountant.kernel_event_notifier_error_counter_fd, + &counter_clear); + if (ret) { + ERR("Error clearing event notifier error counter"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + rcu_read_unlock(); + return status; +} + +enum event_notifier_error_accounting_status event_notifier_error_accounting_register_kernel( + int kernel_event_notifier_group_fd) +{ + int local_fd = -1, ret; + enum event_notifier_error_accounting_status status; + struct lttng_kernel_counter_conf error_counter_conf = {0}; + + error_counter_conf.arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR; + error_counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_64; + error_counter_conf.global_sum_step = 0; + error_counter_conf.number_dimensions = 1; + error_counter_conf.dimensions[0].size = error_counter_size; + error_counter_conf.dimensions[0].has_underflow = false; + error_counter_conf.dimensions[0].has_overflow = false; + + ret = kernctl_create_event_notifier_group_error_counter( + kernel_event_notifier_group_fd, &error_counter_conf); + if (ret < 0) { + PERROR("ioctl kernel create event notifier group error counter"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + 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 event notifier error counter fd"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto error; + } + + DBG("Kernel event notifier group error counter (fd: %d)", local_fd); + + kernel_error_accountant.kernel_event_notifier_error_counter_fd = local_fd; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + +error: + return status; +} + +static +enum event_notifier_error_accounting_status create_error_counter_index_for_token( + uint64_t tracer_token, uint64_t *error_counter_index) +{ + struct index_ht_entry *index_entry;; + enum lttng_index_allocator_status index_alloc_status; + uint64_t local_error_counter_index; + enum event_notifier_error_accounting_status status; + + /* Allocate a new index for that counter. */ + index_alloc_status = lttng_index_allocator_alloc(index_allocator, + &local_error_counter_index); + switch (index_alloc_status) { + case LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY: + DBG("No more index available in the configured event notifier error counter:" + "number-of-indices=%"PRIu64, + lttng_index_allocator_get_index_count( + index_allocator)); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE; + goto end; + case LTTNG_INDEX_ALLOCATOR_STATUS_OK: + break; + default: + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + index_entry = zmalloc(sizeof(*index_entry)); + if (index_entry == NULL) { + PERROR("Event notifier error counter hashtable entry zmalloc"); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM; + goto end; + } + + index_entry->error_counter_index = local_error_counter_index; + lttng_ht_node_init_u64(&index_entry->node, tracer_token); + + lttng_ht_add_unique_u64(error_counter_indexes_ht, &index_entry->node); + + *error_counter_index = local_error_counter_index; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +end: + return status; +} + +enum event_notifier_error_accounting_status event_notifier_error_accounting_register_event_notifier( + const struct lttng_trigger *trigger, + uint64_t *error_counter_index) +{ + enum event_notifier_error_accounting_status status; + uint64_t local_error_counter_index; + + rcu_read_lock(); + /* + * Check if this event notifier already has a error counter index + * assigned. + */ + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &local_error_counter_index); + switch (status) { + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND: + DBG("Event notifier error counter index for this tracer token not found. Allocating a new one."); + status = create_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), + &local_error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error creating index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + case EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK: + *error_counter_index = local_error_counter_index; + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + break; + default: + break; + } + +end: + rcu_read_unlock(); + return status; +} + +static +enum event_notifier_error_accounting_status event_notifier_error_accounting_kernel_get_count( + const struct lttng_trigger *trigger, uint64_t *count) +{ + struct lttng_kernel_counter_aggregate counter_aggregate = {0}; + enum event_notifier_error_accounting_status status; + uint64_t error_counter_index; + int ret; + + rcu_read_lock(); + status = get_error_counter_index_for_token( + lttng_trigger_get_tracer_token(trigger), &error_counter_index); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting index for token: status=%s", + error_accounting_status_str(status)); + goto end; + } + + counter_aggregate.index.number_dimensions = 1; + counter_aggregate.index.dimension_indexes[0] = error_counter_index; + + assert(kernel_error_accountant.kernel_event_notifier_error_counter_fd); + + ret = kernctl_counter_get_aggregate_value( + kernel_error_accountant.kernel_event_notifier_error_counter_fd, + &counter_aggregate); + if (ret) { + ERR("Error getting event notifier error count."); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + if (counter_aggregate.value.value < 0) { + ERR("Event notifier error counter less than zero."); + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR; + goto end; + } + + /* Error count can't be negative. */ + assert(counter_aggregate.value.value >= 0); + *count = (uint64_t) counter_aggregate.value.value; + + status = EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; + +end: + rcu_read_unlock(); + return status; +} + +enum event_notifier_error_accounting_status event_notifier_error_accounting_get_count( + const struct lttng_trigger *trigger, uint64_t *count) +{ + switch (lttng_trigger_get_underlying_domain_type_restriction(trigger)) { + case LTTNG_DOMAIN_KERNEL: + return event_notifier_error_accounting_kernel_get_count(trigger, count); + case LTTNG_DOMAIN_UST: +#ifdef HAVE_LIBLTTNG_UST_CTL + return event_notifier_error_accounting_ust_get_count(trigger, count); +#else + return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +#endif /* HAVE_LIBLTTNG_UST_CTL */ + default: + abort(); + } +} + +static +enum event_notifier_error_accounting_status event_notifier_error_accounting_clear( + const struct lttng_trigger *trigger) +{ + switch (lttng_trigger_get_underlying_domain_type_restriction(trigger)) { + case LTTNG_DOMAIN_KERNEL: + return event_notifier_error_accounting_kernel_clear(trigger); + case LTTNG_DOMAIN_UST: +#ifdef HAVE_LIBLTTNG_UST_CTL + return event_notifier_error_accounting_ust_clear(trigger); +#else + return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +#endif /* HAVE_LIBLTTNG_UST_CTL */ + default: + abort(); + } +} + +static void free_index_ht_entry(struct rcu_head *head) +{ + struct index_ht_entry *entry = caa_container_of(head, + struct index_ht_entry, rcu_head); + free(entry); +} + +void event_notifier_error_accounting_unregister_event_notifier( + const struct lttng_trigger *trigger) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_u64 *node; + struct index_ht_entry *index_entry; + enum event_notifier_error_accounting_status status; + enum lttng_index_allocator_status index_alloc_status; + uint64_t tracer_token = lttng_trigger_get_tracer_token(trigger); + + status = event_notifier_error_accounting_clear(trigger); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error clearing event notifier error counter index: status=%s", + error_accounting_status_str(status)); + } + + rcu_read_lock(); + lttng_ht_lookup(error_counter_indexes_ht, &tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if(node) { + index_entry = caa_container_of(node, struct index_ht_entry, node); + index_alloc_status = lttng_index_allocator_release( + index_allocator, + index_entry->error_counter_index); + if (index_alloc_status != LTTNG_INDEX_ALLOCATOR_STATUS_OK) { + ERR("Error releasing event notifier error counter index: status=%s", + error_accounting_status_str(status)); + } + + lttng_ht_del(error_counter_indexes_ht, &iter); + call_rcu(&index_entry->rcu_head, free_index_ht_entry); + } + rcu_read_unlock(); +} + +static void free_error_account_entry(struct rcu_head *head) +{ + struct error_account_entry *entry = caa_container_of(head, + struct error_account_entry, rcu_head); +#ifdef HAVE_LIBLTTNG_UST_CTL + int i; + for (i = 0; i < entry->nr_counter_cpu_fds; i++) { + ustctl_release_object(-1, entry->cpu_counters[i]); + free(entry->cpu_counters[i]); + } + + free(entry->cpu_counters); + + ustctl_release_object(-1, entry->counter); + free(entry->counter); + + ustctl_destroy_counter(entry->daemon_counter); +#endif /* HAVE_LIBLTTNG_UST_CTL */ + + free(entry); +} + +void event_notifier_error_accounting_fini(void) +{ + struct lttng_ht_iter iter; + struct index_ht_entry *index_entry; + struct error_account_entry *uid_entry; + + lttng_index_allocator_destroy(index_allocator); + + if (kernel_error_accountant.kernel_event_notifier_error_counter_fd) { + int ret = close(kernel_error_accountant.kernel_event_notifier_error_counter_fd); + if (ret) { + PERROR("Closing kernel event notifier error counter"); + } + } + + rcu_read_lock(); + + cds_lfht_for_each_entry(error_counter_uid_ht->ht, &iter.iter, + uid_entry, node.node) { + cds_lfht_del(error_counter_uid_ht->ht, &uid_entry->node.node); + call_rcu(&uid_entry->rcu_head, free_error_account_entry); + } + + cds_lfht_for_each_entry(error_counter_indexes_ht->ht, &iter.iter, + index_entry, node.node) { + cds_lfht_del(error_counter_indexes_ht->ht, &index_entry->node.node); + call_rcu(&index_entry->rcu_head, free_index_ht_entry); + } + + rcu_read_unlock(); + + lttng_ht_destroy(error_counter_uid_ht); + lttng_ht_destroy(error_counter_indexes_ht); +} diff --git a/src/bin/lttng-sessiond/event-notifier-error-accounting.h b/src/bin/lttng-sessiond/event-notifier-error-accounting.h new file mode 100644 index 000000000..889efffa3 --- /dev/null +++ b/src/bin/lttng-sessiond/event-notifier-error-accounting.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef _EVENT_NOTIFIER_ERROR_ACCOUNTING_H +#define _EVENT_NOTIFIER_ERROR_ACCOUNTING_H + +#include + +#include + +#include "ust-app.h" + +enum event_notifier_error_accounting_status { + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_ERR, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOT_FOUND, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NOMEM, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE, + EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_APP_DEAD, +}; + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_init(uint64_t nb_bucket); + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_register_kernel( + int kernel_event_notifier_group_fd); + +#ifdef HAVE_LIBLTTNG_UST_CTL +enum event_notifier_error_accounting_status +event_notifier_error_accounting_register_app(struct ust_app *app); + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_unregister_app(struct ust_app *app); +#else /* HAVE_LIBLTTNG_UST_CTL */ +static inline +enum event_notifier_error_accounting_status +event_notifier_error_accounting_register_app(struct ust_app *app) +{ + return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +} + +static inline +enum event_notifier_error_accounting_status +event_notifier_error_accounting_unregister_app(struct ust_app *app) +{ + return EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK; +} +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_register_event_notifier( + const struct lttng_trigger *trigger, + uint64_t *error_counter_index); + +enum event_notifier_error_accounting_status +event_notifier_error_accounting_get_count( + const struct lttng_trigger *trigger, + uint64_t *count); + +void event_notifier_error_accounting_unregister_event_notifier( + const struct lttng_trigger *trigger); + +void event_notifier_error_accounting_fini(void); + +#endif /* _EVENT_NOTIFIER_ERROR_ACCOUNTING_H */ diff --git a/src/bin/lttng-sessiond/event.c b/src/bin/lttng-sessiond/event.c index 125adb9a0..dd2e4b382 100644 --- a/src/bin/lttng-sessiond/event.c +++ b/src/bin/lttng-sessiond/event.c @@ -13,13 +13,14 @@ #include #include #include -#include +#include #include #include #include #include #include #include +#include #include #include "channel.h" @@ -38,7 +39,7 @@ * Add unique UST event based on the event name, filter bytecode and loglevel. */ static void add_unique_ust_event(struct lttng_ht *ht, - struct ltt_ust_event *event) + struct ltt_ust_event *event, struct lttng_map_key *map_key) { struct cds_lfht_node *node_ptr; struct ltt_ust_ht_key key; @@ -52,6 +53,7 @@ static void add_unique_ust_event(struct lttng_ht *ht, key.loglevel_type = event->attr.loglevel_type; key.loglevel_value = event->attr.loglevel; key.exclusion = event->exclusion; + key.key = map_key; node_ptr = cds_lfht_add_unique(ht->ht, ht->hash_fct(event->node.key, lttng_ht_seed), @@ -113,8 +115,8 @@ int event_kernel_enable_event(struct ltt_kernel_channel *kchan, assert(kchan); assert(event); - kevent = trace_kernel_find_event(event->name, kchan, - event->type, filter); + kevent = trace_kernel_find_event(&kchan->events_list, + 0, event->name, event->type, filter); if (kevent == NULL) { ret = kernel_create_event(event, kchan, filter_expression, filter); /* We have passed ownership */ @@ -142,6 +144,104 @@ end: return ret; } +/* + * Disable kernel tracepoint events for a map from the kernel session of + * a specified event_name and event type. + * On type LTTNG_EVENT_ALL all events with event_name are disabled. + * If event_name is NULL all events of the specified type are disabled. + */ +int map_event_kernel_disable_event(struct ltt_kernel_map *kmap, + uint64_t action_tracer_token) +{ + struct ltt_kernel_event_counter *kevent_counter; + struct lttng_ht_iter iter; + const struct lttng_ht_node_u64 *node; + enum lttng_error_code ret_code; + int ret; + + assert(kmap); + + lttng_ht_lookup(kmap->event_counters_ht, (void *) &action_tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (node){ + kevent_counter = caa_container_of(node, + struct ltt_kernel_event_counter, ht_node); + ret = kernctl_disable(kevent_counter->fd); + if (ret < 0) { + ret_code = LTTNG_ERR_KERN_DISABLE_FAIL; + goto end; + } + kevent_counter->enabled = false; + DBG("Disable kernel event counter"); + } else { + ret_code = LTTNG_ERR_NO_EVENT; + goto end; + } + + ret_code = LTTNG_OK; +end: + return ret_code; +} + +/* + * Enable kernel tracepoint event for a map from the kernel session. + * We own filter_expression and filter. + */ +int map_event_kernel_enable_event(struct ltt_kernel_map *kmap, + const struct lttng_credentials *creds, + uint64_t action_tracer_token, + const struct lttng_event_rule *event_rule, + struct lttng_map_key *key) +{ + int err; + enum lttng_error_code ret_code; + struct ltt_kernel_event_counter *kevent_counter; + struct lttng_ht_iter iter; + const struct lttng_ht_node_u64 *node; + + assert(kmap); + assert(event_rule); + assert(key); + + lttng_ht_lookup(kmap->event_counters_ht, (void *) &action_tracer_token, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (node){ + kevent_counter = caa_container_of(node, + struct ltt_kernel_event_counter, ht_node); + if (kevent_counter->enabled) { + /* At this point, the event is considered enabled */ + ret_code = LTTNG_ERR_KERN_EVENT_EXIST; + goto end; + } + + err = kernctl_enable(kevent_counter->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + ret_code = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel event counter"); + ret_code = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto end; + } + + } else { + + ret_code = kernel_create_event_counter(kmap, creds, + action_tracer_token, event_rule, key); + if (ret_code != LTTNG_OK) { + goto end; + } + } + + ret_code = LTTNG_OK; +end: + return ret_code; +} + /* * ============================ * UST : The Ultimate Frontier! @@ -162,18 +262,26 @@ int event_ust_enable_tracepoint(struct ltt_ust_session *usess, int ret = LTTNG_OK, to_create = 0; struct ltt_ust_event *uevent; + /* + * FIXME: Frdeso. The tracer token should probably me set for regular + * events too. + */ + uint64_t tracer_token = 0; + assert(usess); assert(uchan); assert(event); rcu_read_lock(); - uevent = trace_ust_find_event(uchan->events, event->name, filter, + uevent = trace_ust_find_event(uchan->events, 0, event->name, filter, (enum lttng_ust_loglevel_type) event->loglevel_type, - event->loglevel, exclusion); + event->loglevel, exclusion, NULL); if (!uevent) { - ret = trace_ust_create_event(event, filter_expression, - filter, exclusion, internal_event, &uevent); + ret = trace_ust_create_event(tracer_token, event->name, NULL, event->type, + event->loglevel_type, event->loglevel, + filter_expression, filter, exclusion, + internal_event, &uevent); /* We have passed ownership */ filter_expression = NULL; filter = NULL; @@ -196,7 +304,7 @@ int event_ust_enable_tracepoint(struct ltt_ust_session *usess, uevent->enabled = 1; if (to_create) { /* Add ltt ust event to channel */ - add_unique_ust_event(uchan->events, uevent); + add_unique_ust_event(uchan->events, uevent, NULL); } if (!usess->active) { @@ -205,10 +313,10 @@ int event_ust_enable_tracepoint(struct ltt_ust_session *usess, if (to_create) { /* Create event on all UST registered apps for session */ - ret = ust_app_create_event_glb(usess, uchan, uevent); + ret = ust_app_create_channel_event_glb(usess, uchan, uevent); } else { /* Enable event on all UST registered apps for session */ - ret = ust_app_enable_event_glb(usess, uchan, uevent); + ret = ust_app_enable_channel_event_glb(usess, uchan, uevent); } if (ret < 0) { @@ -252,6 +360,140 @@ error: return ret; } +/* + * Enable UST tracepoint event for a map from a UST session. + */ +enum lttng_error_code map_event_ust_enable_tracepoint( + struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + uint64_t tracer_token, + char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + int ev_loglevel_value, + char *_filter_expression, + struct lttng_bytecode *_filter, + struct lttng_event_exclusion *exclusion, + bool internal_event) +{ + enum lttng_error_code ret_code = LTTNG_OK; + int ret, to_create = 0; + struct ltt_ust_event *uevent; + struct lttng_bytecode *filter = NULL; + char *filter_expression = NULL; + + + assert(usess); + assert(umap); + + /* + * FIXME: FRDESO: this function was copied from ust-app.c + */ + if (_filter_expression) { + filter_expression = strdup(_filter_expression); + } + + if (_filter) { + filter = zmalloc(sizeof(*filter) + _filter->len); + if (!filter) { + PERROR("Failed to allocate lttng_ust_filter_bytecode: bytecode len = %" PRIu32 " bytes", _filter->len); + goto error; + } + + assert(sizeof(struct lttng_bytecode) == + sizeof(struct lttng_ust_filter_bytecode)); + memcpy(filter, _filter, sizeof(*filter) + _filter->len); + } + + rcu_read_lock(); + + uevent = trace_ust_find_event(umap->events, tracer_token, ev_name, filter, + (enum lttng_ust_loglevel_type) ev_loglevel_type, + ev_loglevel_value, exclusion, key); + if (!uevent) { + ret_code = trace_ust_create_event(tracer_token, ev_name, key, ev_type, + ev_loglevel_type, ev_loglevel_value, + filter_expression, filter, exclusion, + internal_event, &uevent); + /* We have passed ownership */ + filter_expression = NULL; + filter = NULL; + exclusion = NULL; + if (ret_code != LTTNG_OK) { + goto error; + } + + /* Valid to set it after the goto error since uevent is still NULL */ + to_create = 1; + } + + if (uevent->enabled) { + /* It's already enabled so everything is OK */ + assert(!to_create); + ret_code = LTTNG_ERR_UST_EVENT_ENABLED; + goto end; + } + + uevent->enabled = 1; + if (to_create) { + /* Add ltt ust event to map */ + add_unique_ust_event(umap->events, uevent, key); + } + + if (!usess->active) { + goto end; + } + + if (to_create) { + /* Create event on all UST registered apps for session */ + ret = ust_app_create_map_event_glb(usess, umap, uevent); + } else { + /* Enable event on all UST registered apps for session */ + ret = ust_app_enable_map_event_glb(usess, umap, uevent); + } + + if (ret < 0) { + if (ret == -LTTNG_UST_ERR_EXIST) { + ret_code = LTTNG_ERR_UST_EVENT_EXIST; + goto end; + } else { + ret_code = LTTNG_ERR_UST_ENABLE_FAIL; + goto error; + } + } + + DBG("Event UST %s %s in map %s", uevent->attr.name, + to_create ? "created" : "enabled", umap->name); + + ret_code = LTTNG_OK; + +end: + rcu_read_unlock(); + free(filter_expression); + free(filter); + free(exclusion); + return ret_code; + +error: + /* + * Only destroy event on creation time (not enabling time) because if the + * event is found in the map (to_create == 0), it means that at some + * point the enable_event worked and it's thus valid to keep it alive. + * Destroying it also implies that we also destroy it's shadow copy to sync + * everyone up. + */ + if (to_create) { + /* In this code path, the uevent was not added to the hash table */ + trace_ust_destroy_event(uevent); + } + rcu_read_unlock(); + free(filter_expression); + free(filter); + free(exclusion); + return ret_code; +} + /* * Disable UST tracepoint of a channel from a UST session. */ @@ -300,7 +542,7 @@ int event_ust_disable_tracepoint(struct ltt_ust_session *usess, if (!usess->active) { goto next; } - ret = ust_app_disable_event_glb(usess, uchan, uevent); + ret = ust_app_disable_channel_event_glb(usess, uchan, uevent); if (ret < 0 && ret != -LTTNG_UST_ERR_EXIST) { ret = LTTNG_ERR_UST_DISABLE_FAIL; goto error; @@ -319,6 +561,69 @@ error: return ret; } +/* + * Disable UST tracepoint of a map from a UST session. + */ +enum lttng_error_code map_event_ust_disable_tracepoint( + struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + uint64_t tracer_token, + char *event_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + int ev_loglevel_value, + char *filter_expression, + struct lttng_bytecode *filter, + struct lttng_event_exclusion *exclusion, + bool internal_event) +{ + int ret; + enum lttng_error_code ret_code; + struct ltt_ust_event *uevent; + + assert(usess); + assert(umap); + assert(event_name); + + rcu_read_lock(); + + /* + * FIXME: frdeso: We need to pass all the parameters to find the right + * event. + */ + uevent = trace_ust_find_event(umap->events, tracer_token, event_name, filter, + (enum lttng_ust_loglevel_type) ev_loglevel_type, + ev_loglevel_value, exclusion, key); + assert(uevent); + + if (uevent->enabled == 0) { + ret_code = LTTNG_OK; + goto end; + } + + uevent->enabled = 0; + DBG2("Event UST %s disabled in map %s", uevent->attr.name, + umap->name); + + if (!usess->active) { + ret_code = LTTNG_OK; + goto end; + } + + ret = ust_app_disable_map_event_glb(usess, umap, uevent); + if (ret < 0 && ret != -LTTNG_UST_ERR_EXIST) { + ret_code = LTTNG_ERR_UST_DISABLE_FAIL; + goto end; + } + + ret_code = LTTNG_OK; + +end: + rcu_read_unlock(); + return ret_code; +} + /* * Disable all UST tracepoints for a channel from a UST session. */ @@ -372,6 +677,56 @@ error: return ret; } +/* + * Disable all UST tracepoints for a map from a UST session. + */ +int map_event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + int ret, error = 0; + struct lttng_ht_iter iter; + struct ltt_ust_event *uevent = NULL; + struct lttng_event *events = NULL; + + assert(usess); + assert(umap); + + rcu_read_lock(); + + /* Disabling existing events */ + cds_lfht_for_each_entry(umap->events->ht, &iter.iter, uevent, + node.node) { + if (uevent->enabled == 1) { + ret = map_event_ust_disable_tracepoint(usess, umap, + uevent->attr.token, + uevent->attr.name, + uevent->key, + uevent->attr.instrumentation, + (enum lttng_loglevel_type) uevent->attr.loglevel_type, + uevent->attr.loglevel, + uevent->filter_expression, + uevent->filter, + uevent->exclusion, + false); + if (ret < 0) { + error = LTTNG_ERR_UST_DISABLE_FAIL; + continue; + } + } + } + + /* + * FIXME: FRDESO: in the equivalent function + * event_ust_disable_all_tracepoints() (above ^) we also iterator over + * all lttng_event. Do we need to do this here too? + */ + + ret = error ? error : LTTNG_OK; + rcu_read_unlock(); + free(events); + return ret; +} + static void agent_enable_all(struct agent *agt) { struct agent_event *aevent; @@ -601,9 +956,9 @@ int trigger_agent_enable(const struct lttng_trigger *trigger, struct agent *agt) condition = lttng_trigger_get_const_condition(trigger); assert(lttng_condition_get_type(condition) == - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + LTTNG_CONDITION_TYPE_ON_EVENT); - c_status = lttng_condition_event_rule_get_rule(condition, &rule); + c_status = lttng_condition_on_event_get_rule(condition, &rule); assert(c_status == LTTNG_CONDITION_STATUS_OK); assert(lttng_event_rule_get_type(rule) == @@ -779,13 +1134,15 @@ static int event_agent_disable_one(struct ltt_ust_session *usess, * happens thanks to an UST filter. The following -1 is actually * ignored since the type is LTTNG_UST_LOGLEVEL_ALL. */ - uevent = trace_ust_find_event(uchan->events, (char *) ust_event_name, - aevent->filter, LTTNG_UST_LOGLEVEL_ALL, -1, NULL); + /* TODO: JORAJ FRDESO: hmmm what to do with tracer token here? + */ + uevent = trace_ust_find_event(uchan->events, 0, (char *) ust_event_name, + aevent->filter, LTTNG_UST_LOGLEVEL_ALL, -1, NULL, NULL); /* If the agent event exists, it must be available on the UST side. */ assert(uevent); if (usess->active) { - ret = ust_app_disable_event_glb(usess, uchan, uevent); + ret = ust_app_disable_channel_event_glb(usess, uchan, uevent); if (ret < 0 && ret != -LTTNG_UST_ERR_EXIST) { ret = LTTNG_ERR_UST_DISABLE_FAIL; goto error; diff --git a/src/bin/lttng-sessiond/event.h b/src/bin/lttng-sessiond/event.h index 8849ee7cc..42b8fb7c2 100644 --- a/src/bin/lttng-sessiond/event.h +++ b/src/bin/lttng-sessiond/event.h @@ -9,6 +9,7 @@ #define _LTT_EVENT_H #include "trace-kernel.h" +#include "trace-ust.h" struct agent; @@ -19,18 +20,59 @@ int event_kernel_enable_event(struct ltt_kernel_channel *kchan, struct lttng_event *event, char *filter_expression, struct lttng_bytecode *filter); +int map_event_kernel_disable_event(struct ltt_kernel_map *kmap, + uint64_t action_tracer_token); + +int map_event_kernel_enable_event(struct ltt_kernel_map *kmap, + const struct lttng_credentials *creds, + uint64_t tracer_token, + const struct lttng_event_rule *event_rule, + struct lttng_map_key *key); + int event_ust_enable_tracepoint(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct lttng_event *event, char *filter_expression, struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, bool internal_event); + +enum lttng_error_code map_event_ust_enable_tracepoint( + struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + uint64_t tracer_token, + char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + int ev_loglevel_value, + char *filter_expression, + struct lttng_bytecode *filter, + struct lttng_event_exclusion *exclusion, + bool internal_event); + int event_ust_disable_tracepoint(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, const char *event_name); +enum lttng_error_code map_event_ust_disable_tracepoint( + struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + uint64_t tracer_token, + char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + int ev_loglevel_value, + char *filter_expression, + struct lttng_bytecode *filter, + struct lttng_event_exclusion *exclusion, + bool internal_event); + int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan); +int map_event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); + int event_agent_enable(struct ltt_ust_session *usess, struct agent *agt, struct lttng_event *event, struct lttng_bytecode *filter, char *filter_expression); diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 23e8eac2a..25d744d11 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -28,12 +28,18 @@ #include #include -#include -#include +#include +#include #include #include -#include - +#include +#include +#include +#include +#include +#include + +#include "event-notifier-error-accounting.h" #include "lttng-sessiond.h" #include "lttng-syscall.h" #include "condition-internal.h" @@ -41,6 +47,8 @@ #include "kernel.h" #include "kernel-consumer.h" #include "kern-modules.h" +#include "map.h" +#include "sessiond-config.h" #include "utils.h" #include "rotate.h" #include "modprobe.h" @@ -540,9 +548,9 @@ static int userspace_probe_event_rule_add_callsites( assert(creds); event_rule_type = lttng_event_rule_get_type(rule); - assert(event_rule_type == LTTNG_EVENT_RULE_TYPE_UPROBE); + assert(event_rule_type == LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE); - status = lttng_event_rule_uprobe_get_location(rule, &location); + status = lttng_event_rule_userspace_probe_get_location(rule, &location); if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) { ret = -1; goto end; @@ -1989,7 +1997,8 @@ int init_kernel_tracer(void) WARN("Failed to create kernel event notifier group"); kernel_tracer_event_notifier_group_fd = -1; } else { - const enum lttng_error_code error_code_ret = + enum event_notifier_error_accounting_status error_accounting_status; + enum lttng_error_code error_code_ret = kernel_create_event_notifier_group_notification_fd( &kernel_tracer_event_notifier_group_notification_fd); @@ -1997,6 +2006,14 @@ int init_kernel_tracer(void) goto error_modules; } + error_accounting_status = event_notifier_error_accounting_register_kernel( + kernel_tracer_event_notifier_group_fd); + if (error_accounting_status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error initializing event notifier error accounting for kernel tracer."); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING; + goto error_modules; + } + kernel_token_to_event_notifier_rule_ht = cds_lfht_new( DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, @@ -2113,8 +2130,6 @@ void cleanup_kernel_tracer(void) kernel_tracer_fd = -1; } - DBG("Unloading kernel modules"); - modprobe_remove_lttng_all(); free(syscall_table); } @@ -2123,13 +2138,8 @@ bool kernel_tracer_is_initialized(void) { return kernel_tracer_fd >= 0; } - -/* - * Clear a kernel session. - * - * Return LTTNG_OK on success or else an LTTng error code. - */ -enum lttng_error_code kernel_clear_session(struct ltt_session *session) +static +enum lttng_error_code kernel_clear_session_channels(struct ltt_session *session) { int ret; enum lttng_error_code status = LTTNG_OK; @@ -2140,9 +2150,6 @@ enum lttng_error_code kernel_clear_session(struct ltt_session *session) assert(ksess); assert(ksess->consumer); - DBG("Clear kernel session %s (session %" PRIu64 ")", - session->name, session->id); - rcu_read_lock(); if (ksess->active) { @@ -2189,6 +2196,7 @@ enum lttng_error_code kernel_clear_session(struct ltt_session *session) } } + goto end; error: switch (-ret) { @@ -2204,6 +2212,116 @@ end: return status; } +static +enum lttng_error_code kernel_map_clear_all(struct ltt_kernel_map *map) +{ + enum lttng_error_code status; + uint64_t descr_count, i; + int ret; + + assert(map); + + ret = kernctl_counter_map_descriptor_count(map->fd, &descr_count); + if (ret) { + ERR("Error getting map descriptor count"); + status = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + for(i = 0; i < descr_count; i++) { + struct lttng_kernel_counter_map_descriptor descriptor = {0}; + struct lttng_kernel_counter_clear counter_clear = {0}; + + descriptor.descriptor_index = i; + + ret = kernctl_counter_map_descriptor(map->fd, &descriptor); + if (ret) { + ERR("Error getting map descriptor %"PRIu64, i); + status = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + counter_clear.index.number_dimensions = 1; + counter_clear.index.dimension_indexes[0] = descriptor.array_index; + + ret = kernctl_counter_clear(map->fd, &counter_clear); + if (ret) { + ERR("Error clearing value of map descriptor %"PRIu64, i); + status = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + } + + status = LTTNG_OK; +end: + return status; +} + +static +enum lttng_error_code kernel_clear_session_maps(struct ltt_session *session) +{ + enum lttng_error_code status = LTTNG_OK; + struct ltt_kernel_map *map; + struct ltt_kernel_session *ksess = session->kernel_session; + + assert(ksess); + + cds_list_for_each_entry(map, &ksess->map_list.head, list) { + DBG("Clear kernel map %" PRIu64 ", session %s", + map->key, session->name); + status = kernel_map_clear_all(map); + if (status != LTTNG_OK) { + ERR("Clearing all values of map"); + goto end; + } + } + +end: + return status; +} + +/* + * Clear a kernel session. + * + * Return LTTNG_OK on success or else an LTTng error code. + */ +enum lttng_error_code kernel_clear_session(struct ltt_session *session) +{ + enum lttng_error_code status = LTTNG_OK; + struct ltt_kernel_session *ksess = session->kernel_session; + + assert(ksess); + assert(ksess->consumer); + + DBG("Clear kernel session %s (session %" PRIu64 ")", + session->name, session->id); + + rcu_read_lock(); + + if (ksess->active) { + ERR("Expecting inactive session %s (%" PRIu64 ")", session->name, session->id); + status = LTTNG_ERR_FATAL; + goto end; + } + + status = kernel_clear_session_channels(session); + if (status != LTTNG_OK) { + goto end; + } + /* + * Iterate and clear all kernel maps. + */ + status = kernel_clear_session_maps(session); + if (status != LTTNG_OK) { + goto end; + } + + +end: + rcu_read_unlock(); + return status; +} + enum lttng_error_code kernel_create_event_notifier_group_notification_fd( int *event_notifier_group_notification_fd) { @@ -2288,6 +2406,193 @@ int match_trigger(struct cds_lfht_node *node, const void *key) return lttng_trigger_is_equal(trigger, event_notifier_rule->trigger); } +static +int add_key_token(struct lttng_kernel_key_token *kernel_key_token, + const struct lttng_map_key_token *key_token) +{ + int ret; + switch (key_token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + const struct lttng_map_key_token_string *str_token; + str_token = (typeof(str_token)) key_token; + + kernel_key_token->type = LTTNG_KERNEL_KEY_TOKEN_STRING; + kernel_key_token->arg.string_ptr = (uint64_t) str_token->string; + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + const struct lttng_map_key_token_variable *var_token; + var_token = (typeof(var_token)) key_token; + switch (var_token->type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + kernel_key_token->type = LTTNG_KERNEL_KEY_TOKEN_EVENT_NAME; + break; + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + /* The kernel events don't have providers */ + ERR("Provider variable token type not supported for kernel tracer"); + ret = -1; + goto end; + default: + abort(); + } + + break; + } + default: + abort(); + } + ret = 0; +end: + return ret; +} + +enum lttng_error_code kernel_create_event_counter( + struct ltt_kernel_map *kmap, + const struct lttng_credentials *creds, + uint64_t action_tracer_token, + const struct lttng_event_rule *event_rule, + struct lttng_map_key *key) +{ + int err, fd, ret = 0; + unsigned int i, key_token_count; + enum lttng_error_code error_code_ret; + enum lttng_map_key_status status; + struct ltt_kernel_event_counter *event_counter; + struct lttng_kernel_counter_event k_counter_event = {}; + + + event_counter = zmalloc(sizeof(*event_counter)); + if (!event_counter) { + error_code_ret = LTTNG_ERR_NOMEM; + goto error; + } + + trace_kernel_init_event_counter_from_event_rule(event_rule, + &k_counter_event); + event_counter->fd = -1; + event_counter->enabled = 1; + event_counter->action_tracer_token = action_tracer_token; + event_counter->filter = lttng_event_rule_get_filter_bytecode(event_rule); + + k_counter_event.event.token = action_tracer_token; + + /* Set the key pattern for this event counter. */ + k_counter_event.key.nr_dimensions = 1; + + status = lttng_map_key_get_token_count(key, &key_token_count); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + error_code_ret = LTTNG_ERR_UNK; + goto error; + } + + assert(key_token_count > 0); + + k_counter_event.key.key_dimensions[0].nr_key_tokens = key_token_count; + + for (i = 0; i < key_token_count; i++) { + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + + ret = add_key_token(&k_counter_event.key.key_dimensions[0].key_tokens[i], + token); + if (ret) { + ERR("Error appending map key token"); + error_code_ret = LTTNG_ERR_INVALID; + goto error; + } + } + + fd = kernctl_create_counter_event(kmap->fd, &k_counter_event); + if (fd < 0) { + switch (-fd) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Event counter type not implemented"); + error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Event counter %s not found!", k_counter_event.event.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + PERROR("create event counter ioctl"); + } + } + + event_counter->fd = fd; + event_counter->enabled = true; + + /* Prevent fd duplication after execlp() */ + err = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("fcntl session fd"); + } + + if (event_counter->filter) { + err = kernctl_filter(event_counter->fd, event_counter->filter); + if (err < 0) { + switch (-err) { + case ENOMEM: + error_code_ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + error_code_ret = LTTNG_ERR_FILTER_INVAL; + break; + } + goto filter_error; + } + } + if (lttng_event_rule_get_type(event_rule) == + LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE) { + ret = userspace_probe_event_rule_add_callsites( + event_rule, creds, event_counter->fd); + if (ret) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto add_callsite_error; + } + } + + err = kernctl_enable(event_counter->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel counter event"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add event to event list */ + rcu_read_lock(); + lttng_ht_node_init_u64(&event_counter->ht_node, + event_counter->action_tracer_token); + lttng_ht_add_unique_u64(kmap->event_counters_ht, + &event_counter->ht_node); + rcu_read_unlock(); + kmap->event_count++; + + DBG("Kernel event counter %s created (fd: %d)", + event_counter->event->name, + event_counter->fd); + error_code_ret = LTTNG_OK; + +add_callsite_error: +filter_error: +enable_error: +error: + return error_code_ret; +} + static enum lttng_error_code kernel_create_event_notifier_rule( struct lttng_trigger *trigger, const struct lttng_credentials *creds, uint64_t token) @@ -2299,8 +2604,10 @@ static enum lttng_error_code kernel_create_event_notifier_rule( enum lttng_event_rule_type event_rule_type; struct ltt_kernel_event_notifier_rule *event_notifier_rule; struct lttng_kernel_event_notifier kernel_event_notifier = {}; + unsigned int capture_bytecode_count = 0, i; const struct lttng_condition *condition = NULL; const struct lttng_event_rule *event_rule = NULL; + enum lttng_condition_status cond_status; assert(trigger); @@ -2308,10 +2615,10 @@ static enum lttng_error_code kernel_create_event_notifier_rule( assert(condition); condition_type = lttng_condition_get_type(condition); - assert(condition_type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + assert(condition_type == LTTNG_CONDITION_TYPE_ON_EVENT); /* Does not acquire a reference. */ - condition_status = lttng_condition_event_rule_get_rule( + condition_status = lttng_condition_on_event_get_rule( condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); assert(event_rule); @@ -2320,6 +2627,7 @@ static enum lttng_error_code kernel_create_event_notifier_rule( assert(event_rule_type != LTTNG_EVENT_RULE_TYPE_UNKNOWN); error_code_ret = trace_kernel_create_event_notifier_rule(trigger, token, + lttng_condition_on_event_get_error_counter_index(condition), &event_notifier_rule); if (error_code_ret != LTTNG_OK) { goto error; @@ -2332,6 +2640,8 @@ static enum lttng_error_code kernel_create_event_notifier_rule( } kernel_event_notifier.event.token = event_notifier_rule->token; + kernel_event_notifier.error_counter_idx = + lttng_condition_on_event_get_error_counter_index(condition); fd = kernctl_create_event_notifier( kernel_tracer_event_notifier_group_fd, @@ -2384,7 +2694,7 @@ static enum lttng_error_code kernel_create_event_notifier_rule( } if (lttng_event_rule_get_type(event_rule) == - LTTNG_EVENT_RULE_TYPE_UPROBE) { + LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE) { ret = userspace_probe_event_rule_add_callsites( event_rule, creds, event_notifier_rule->fd); if (ret) { @@ -2393,6 +2703,25 @@ static enum lttng_error_code kernel_create_event_notifier_rule( } } + /* Set the capture bytecode if any */ + cond_status = lttng_condition_on_event_get_capture_descriptor_count(condition, &capture_bytecode_count); + assert(cond_status == LTTNG_CONDITION_STATUS_OK); + for (i = 0; i < capture_bytecode_count; i++) { + const struct lttng_bytecode *capture_bytecode = + lttng_condition_on_event_get_capture_bytecode_at_index( + condition, i); + if (capture_bytecode == NULL) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto error; + } + + ret = kernctl_capture(event_notifier_rule->fd, capture_bytecode); + if (ret < 0) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto error; + } + } + err = kernctl_enable(event_notifier_rule->fd); if (err < 0) { switch (-err) { @@ -2452,7 +2781,7 @@ enum lttng_error_code kernel_register_event_notifier( assert(condition); /* Does not acquire a reference to the event rule. */ - status = lttng_condition_event_rule_get_rule( + status = lttng_condition_on_event_get_rule( condition, &event_rule); assert(status == LTTNG_CONDITION_STATUS_OK); @@ -2505,6 +2834,210 @@ error: return error_code_ret; } +struct key_ht_entry { + char *key; + struct lttng_ht_node_str node; +}; + +enum lttng_error_code kernel_list_map_values(const struct ltt_kernel_map *map, + const struct lttng_map_query *query, + struct lttng_map_content **map_content) +{ + enum lttng_map_status map_status; + enum lttng_error_code ret_code; + const char *map_name = NULL; + uint64_t descr_count, descr_idx, cpu_idx; + struct lttng_map_content *local_map_content; + struct lttng_ht *key_ht; + struct lttng_ht *values = NULL; + struct lttng_ht_node_str *node; + struct key_ht_entry *ht_entry; + struct lttng_ht_iter iter; + enum lttng_map_query_status map_query_status; + const char *key_filter; + bool sum_cpus = lttng_map_query_get_config_sum_by_cpu(query); + enum lttng_map_query_config_cpu config_cpu; + int ret; + int selected_cpu; + + + local_map_content = lttng_map_content_create(LTTNG_BUFFER_GLOBAL); + if (!local_map_content) { + ERR("Error creating map content"); + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + map_query_status = lttng_map_query_get_key_filter(query, &key_filter); + if (map_query_status == LTTNG_MAP_QUERY_STATUS_NONE) { + key_filter = NULL; + } else if (map_query_status != LTTNG_MAP_QUERY_STATUS_OK) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + config_cpu = lttng_map_query_get_config_cpu(query); + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int count; + map_query_status = lttng_map_query_get_cpu_count(query, &count); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + assert(count == 1); + + map_query_status = lttng_map_query_get_cpu_at_index(query, 0, + &selected_cpu); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + map_status = lttng_map_get_name(map->map, &map_name); + assert(map_status == LTTNG_MAP_STATUS_OK); + + DBG("Listing kernel map values: map-name = '%s'", map_name); + + ret = kernctl_counter_map_descriptor_count(map->fd, &descr_count); + if (ret) { + ERR("Error getting map descriptor count"); + ret_code = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + /* + * The kernel tracer sends us descriptors that may be identical aside + * from their user token field. This ABI was design this way to cover a + * potential use case where the user wants to know what enabler might + * have contributed to a specific bucket. + * + * We use this hashtable to de-duplicate keys. + */ + if (sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!values) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + } + + DBG("Querying kernel for all map values: " + "map-name = '%s', key-value count = %"PRIu64, + map_name, descr_count); + for (cpu_idx = 0; cpu_idx < utils_get_number_of_possible_cpus(); cpu_idx++) { + struct lttng_kernel_counter_read value = {0}; + + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + if (selected_cpu != cpu_idx) { + continue; + } + } + + if (!sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + assert(values); + } + + key_ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!key_ht) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + for(descr_idx = 0; descr_idx < descr_count; descr_idx++) { + struct lttng_kernel_counter_map_descriptor descriptor = {0}; + + DBG("Querying kernel for map key-value descriptor: " + "map-name = '%s', descriptor = %"PRIu64, + map_name, descr_idx); + descriptor.descriptor_index = descr_idx; + + ret = kernctl_counter_map_descriptor(map->fd, &descriptor); + if (ret) { + ERR("Error getting map descriptor %"PRIu64, descr_idx); + ret_code = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + if (key_filter && strcmp(key_filter, descriptor.key) != 0) { + continue; + } + + lttng_ht_lookup(key_ht, descriptor.key, &iter); + node = lttng_ht_iter_get_node_str(&iter); + if (node) { + /* This key was already appended to the list. */ + continue; + } + + + value.index.number_dimensions = 1; + value.index.dimension_indexes[0] = descriptor.array_index; + value.cpu = cpu_idx; + + DBG("Querying kernel for map descriptor value: " + "map-name = '%s', counter-index = %"PRIu64, + map_name, descriptor.array_index); + ret = kernctl_counter_read_value(map->fd, &value); + if (ret) { + ERR("Error getting value of map descriptor %"PRIu64, descr_idx); + ret_code = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + map_add_or_increment_map_values(values, descriptor.key, + value.value.value, value.value.underflow, + value.value.overflow); + + ht_entry = zmalloc(sizeof(*ht_entry)); + assert(ht_entry); + ht_entry->key = strdup(descriptor.key); + lttng_ht_node_init_str(&ht_entry->node, ht_entry->key); + lttng_ht_add_unique_str(key_ht, &ht_entry->node); + } + + if (!sum_cpus) { + ret = map_new_content_section(local_map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL, + sum_cpus, 0, + cpu_idx, values); + if (ret) { + abort(); + } + + lttng_ht_destroy(values); + } + + /* + * Remove all the keys before destroying the hashtable. + */ + cds_lfht_for_each_entry(key_ht->ht, &iter.iter, ht_entry, node.node) { + struct lttng_ht_iter entry_iter; + + entry_iter.iter.node = &ht_entry->node.node; + lttng_ht_del(key_ht, &entry_iter); + + free(ht_entry); + } + + lttng_ht_destroy(key_ht); + } + + if (sum_cpus) { + ret = map_new_content_section(local_map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL, + sum_cpus, 0, 0, values); + if (ret) { + abort(); + } + lttng_ht_destroy(values); + } + + + *map_content = local_map_content; + local_map_content = NULL; + ret_code = LTTNG_OK; + +end: + lttng_map_content_destroy(local_map_content); + return ret_code; +} + int kernel_get_notification_fd(void) { return kernel_tracer_event_notifier_group_notification_fd; diff --git a/src/bin/lttng-sessiond/kernel.h b/src/bin/lttng-sessiond/kernel.h index a2dd7f060..16d809930 100644 --- a/src/bin/lttng-sessiond/kernel.h +++ b/src/bin/lttng-sessiond/kernel.h @@ -87,12 +87,32 @@ enum lttng_error_code kernel_create_event_notifier_group_notification_fd( enum lttng_error_code kernel_destroy_event_notifier_group_notification_fd( int event_notifier_group_notification_fd); +enum lttng_error_code kernel_create_event_counter( + struct ltt_kernel_map *kmap, + const struct lttng_credentials *creds, + uint64_t action_tracer_token, + const struct lttng_event_rule *event_rule, + struct lttng_map_key *key); + +enum lttng_error_code kernel_register_incr_value_action( + struct ltt_session *session, + const struct lttng_condition *condition, + const char *map_name, + uint64_t tracer_token, + struct lttng_map_key *key); + enum lttng_error_code kernel_register_event_notifier( struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds); enum lttng_error_code kernel_unregister_event_notifier( const struct lttng_trigger *trigger); +enum lttng_error_code kernel_synchronize_tracer_executed_action(void); + +enum lttng_error_code kernel_list_map_values(const struct ltt_kernel_map *map, + const struct lttng_map_query *query, + struct lttng_map_content **map_content); + int kernel_get_notification_fd(void); #endif /* _LTT_KERNEL_CTL_H */ diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index a344cd476..6e4950e54 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -49,9 +49,9 @@ #include "consumer.h" #include "context.h" #include "event.h" +#include "event-notifier-error-accounting.h" #include "kernel.h" #include "kernel-consumer.h" -#include "shm.h" #include "lttng-ust-ctl.h" #include "ust-consumer.h" #include "utils.h" @@ -74,6 +74,7 @@ #include "register.h" #include "manage-apps.h" #include "manage-kernel.h" +#include "modprobe.h" static const char *help_msg = #ifdef LTTNG_EMBED_HELP @@ -83,6 +84,8 @@ NULL #endif ; +#define EVENT_NOTIFIER_ERROR_COUNTER_NUMBER_OF_BUCKET_MAX 65535 + const char *progname; static int lockfile_fd = -1; static int opt_print_version; @@ -120,6 +123,7 @@ static const struct option long_options[] = { { "load", required_argument, 0, 'l' }, { "kmod-probes", required_argument, 0, '\0' }, { "extra-kmod-probes", required_argument, 0, '\0' }, + { "event-notifier-error-number-of-bucket", required_argument, 0, '\0' }, { NULL, 0, 0, 0 } }; @@ -697,6 +701,23 @@ static int set_option(int opt, const char *arg, const char *optname) ret = -ENOMEM; } } + } else if (string_match(optname, "event-notifier-error-number-of-bucket")) { + unsigned long v; + + errno = 0; + v = strtoul(arg, NULL, 0); + if (errno != 0 || !isdigit(arg[0])) { + ERR("Wrong value in --event-notifier-error-number-of-bucket parameter: %s", arg); + return -1; + } + if (v == 0 || v >= EVENT_NOTIFIER_ERROR_COUNTER_NUMBER_OF_BUCKET_MAX) { + ERR("Value out of range for --event-notifier-error-number-of-bucket parameter: %s", arg); + return -1; + } + config.event_notifier_error_counter_bucket = (int) v; + DBG3("Number of event notifier error counter set to non default: %i", + config.event_notifier_error_counter_bucket); + goto end; } else if (string_match(optname, "config") || opt == 'f') { /* This is handled in set_options() thus silent skip. */ goto end; @@ -1243,6 +1264,7 @@ static void destroy_all_sessions_and_wait(void) goto unlock_session; } (void) cmd_stop_trace(session); + (void) cmd_destroy_session(session, notification_thread_handle, NULL); unlock_session: @@ -1589,6 +1611,8 @@ int main(int argc, char **argv) goto stop_threads; } + event_notifier_error_accounting_init(config.event_notifier_error_counter_bucket); + /* * Initialize agent app hash table. We allocate the hash table here * since cleanup() can get called after this point. @@ -1826,6 +1850,7 @@ int main(int argc, char **argv) sessiond_wait_for_quit_pipe(-1); stop_threads: + /* * Ensure that the client thread is no longer accepting new commands, * which could cause new sessions to be created. @@ -1864,7 +1889,7 @@ stop_threads: sessiond_cleanup(); /* - * Wait for all pending call_rcu work to complete tearing shutting down + * Wait for all pending call_rcu work to complete before shutting down * the notification thread. This call_rcu work includes shutting down * UST apps and event notifier pipes. */ @@ -1875,6 +1900,27 @@ stop_threads: lttng_thread_put(notification_thread); } + /* + * Error accounting teardown has to be done after the teardown of all + * event notifier pipes to ensure that no tracer may try to use the + * error accounting facilities. + */ + event_notifier_error_accounting_fini(); + + /* + * Unloading the kernel modules needs to be done after all kernel + * ressources have been released. In our case, this includes the + * notification fd, the event notifier group fd, error accounting fd, + * all event and event notifier fds, etc. + * + * In short, at this point, we need to have called close() on all fds + * received from the kernel tracer. + */ + if (is_root && !config.no_kernel) { + DBG("Unloading kernel modules"); + modprobe_remove_lttng_all(); + } + /* * Ensure all prior call_rcu are done. call_rcu callbacks may push * hash tables to the ht_cleanup thread. Therefore, we ensure that diff --git a/src/bin/lttng-sessiond/map.c b/src/bin/lttng-sessiond/map.c new file mode 100644 index 000000000..6e63504f1 --- /dev/null +++ b/src/bin/lttng-sessiond/map.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + + +#include "lttng/domain.h" +#include +#include +#include + +#include "lttng-sessiond.h" +#include "lttng-ust-error.h" +#include "notification-thread-commands.h" +#include "trace-kernel.h" +#include "trace-ust.h" + +#include "map.h" + +enum lttng_error_code map_kernel_add(struct ltt_kernel_session *ksession, + struct lttng_map *map) +{ + enum lttng_error_code ret; + struct ltt_kernel_map *kmap; + enum lttng_map_status map_status; + const char *map_name; + + assert(lttng_map_get_domain(map) == LTTNG_DOMAIN_KERNEL); + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Can't get map name"); + ret = -1; + goto error; + } + + kmap = trace_kernel_get_map_by_name(map_name, ksession); + if (kmap) { + DBG("Kernel map named \"%s\" already present", map_name); + ret = -1; + goto error; + } + + kmap = trace_kernel_create_map(map); + assert(kmap); + + ret = kernctl_create_session_counter(ksession->fd, + &kmap->counter_conf); + if (ret < 0) { + PERROR("ioctl kernel create session counter"); + goto error; + } + + kmap->fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(kmap->fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session counter fd"); + goto error; + } + + kmap->map = map; + lttng_map_get(map); + cds_list_add(&kmap->list, &ksession->map_list.head); + ksession->map_count++; + + DBG("Kernel session counter created (fd: %d)", kmap->fd); + + ret = kernctl_enable(kmap->fd); + if (ret < 0) { + PERROR("Enable kernel map"); + } + + ret = LTTNG_OK; +error: + return ret; +} + +enum lttng_error_code map_kernel_enable(struct ltt_kernel_session *ksess, + struct ltt_kernel_map *kmap) +{ + enum lttng_error_code ret = LTTNG_OK; + const char *map_name; + enum lttng_map_status map_status; + + + assert(ksess); + assert(kmap); + + map_status = lttng_map_get_name(kmap->map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting kernel map name"); + ret = -1; + goto end; + } + + /* If already enabled, everything is OK */ + if (kmap->enabled) { + DBG3("Map %s already enabled. Skipping", map_name); + ret = LTTNG_ERR_UST_MAP_EXIST; + goto end; + } else { + kmap->enabled = 1; + lttng_map_set_is_enabled(kmap->map, true); + DBG2("Map %s enabled successfully", map_name); + } + + DBG2("Map %s being enabled in kernel domain", map_name); + + /* + * Enable map for UST global domain on all applications. Ignore return + * value here since whatever error we got, it means that the map was + * not created on one or many registered applications and we can not report + * this to the user yet. However, at this stage, the map was + * successfully created on the session daemon side so the enable-map + * command is a success. + */ + + ret = kernctl_enable(kmap->fd); + if (ret < 0) { + PERROR("Enable kernel map"); + } + + ret = LTTNG_OK; +end: + return ret; +} + +enum lttng_error_code map_kernel_disable(struct ltt_kernel_session *usess, + struct ltt_kernel_map *kmap) +{ + enum lttng_error_code ret = LTTNG_OK; + enum lttng_map_status map_status; + const char *map_name = NULL; + + assert(usess); + assert(kmap); + + map_status = lttng_map_get_name(kmap->map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting kernel map name"); + ret = -1; + goto end; + } + + /* Already disabled */ + if (kmap->enabled == 0) { + DBG2("Map kernel %s already disabled", map_name); + ret = LTTNG_ERR_KERNEL_MAP_EXIST; + goto end; + } + + kmap->enabled = 0; + lttng_map_set_is_enabled(kmap->map, false); + + DBG2("Map %s being disabled in kernel global domain", map_name); + + /* Disable map for global domain */ + ret = kernctl_disable(kmap->fd); + if (ret < 0) { + ret = LTTNG_ERR_KERNEL_MAP_DISABLE_FAIL; + goto error; + } + + + DBG2("Map %s disabled successfully", map_name); + + return LTTNG_OK; + +end: +error: + return ret; +} + +int map_ust_add(struct ltt_ust_session *usession, struct lttng_map *map) +{ + int ret = 0; + struct ltt_ust_map *umap; + enum lttng_map_status map_status; + const char *map_name; + enum lttng_buffer_type buffer_type; + + assert(lttng_map_get_domain(map) == LTTNG_DOMAIN_UST); + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Can't get map name"); + ret = -1; + goto end; + } + + umap = trace_ust_find_map_by_name(usession->domain_global.maps, + map_name); + if (umap) { + DBG("UST map named \"%s\" already present", map_name); + ret = -1; + goto end; + } + + buffer_type = lttng_map_get_buffer_type(map); + + umap = trace_ust_create_map(map); + assert(umap); + + umap->enabled = 1; + umap->id = trace_ust_get_next_chan_id(usession); + umap->map = map; + lttng_map_get(map); + + lttng_map_set_is_enabled(umap->map, true); + + DBG2("Map %s is being created for UST with buffer type %d and id %" PRIu64, + umap->name, buffer_type, umap->id); + + /* Flag session buffer type. */ + if (!usession->buffer_type_changed) { + usession->buffer_type = buffer_type; + usession->buffer_type_changed = 1; + } else if (usession->buffer_type != buffer_type) { + /* Buffer type was already set. Refuse to create channel. */ + ret = LTTNG_ERR_BUFFER_TYPE_MISMATCH; + goto error_free_map; + } + + rcu_read_lock(); + + /* Adding the map to the map hash table. */ + lttng_ht_add_unique_str(usession->domain_global.maps, &umap->node); + + rcu_read_unlock(); + + DBG2("Map %s created successfully", umap->name); + + ret = 0; + goto end; + +error_free_map: + trace_ust_destroy_map(umap); +end: + return ret; +} + +/* + * Enable UST map for session and domain. + */ +int map_ust_enable(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + int ret = LTTNG_OK; + + assert(usess); + assert(umap); + + /* If already enabled, everything is OK */ + if (umap->enabled) { + DBG3("Map %s already enabled. Skipping", umap->name); + ret = LTTNG_ERR_UST_MAP_EXIST; + goto end; + } else { + umap->enabled = 1; + lttng_map_set_is_enabled(umap->map, true); + DBG2("Map %s enabled successfully", umap->name); + } + + if (!usess->active) { + /* + * The map will be activated against the apps + * when the session is started as part of the + * application map "synchronize" operation. + */ + goto end; + } + + DBG2("Map %s being enabled in UST domain", umap->name); + + /* + * Enable map for UST global domain on all applications. Ignore return + * value here since whatever error we got, it means that the map was + * not created on one or many registered applications and we can not report + * this to the user yet. However, at this stage, the map was + * successfully created on the session daemon side so the enable-map + * command is a success. + */ + (void) ust_app_enable_map_glb(usess, umap); + + +end: + return ret; +} + +int map_ust_disable(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + int ret = LTTNG_OK; + + assert(usess); + assert(umap); + + /* Already disabled */ + if (umap->enabled == 0) { + DBG2("Map UST %s already disabled", umap->name); + ret = LTTNG_ERR_UST_MAP_EXIST; + goto end; + } + + umap->enabled = 0; + lttng_map_set_is_enabled(umap->map, false); + + /* + * If session is inactive we don't notify the tracer right away. We + * wait for the next synchronization. + */ + if (!usess->active) { + goto end; + } + + DBG2("Map %s being disabled in UST global domain", umap->name); + + /* Disable map for global domain */ + ret = ust_app_disable_map_glb(usess, umap); + if (ret < 0 && ret != -LTTNG_UST_ERR_EXIST) { + ret = LTTNG_ERR_UST_MAP_DISABLE_FAIL; + goto error; + } + + + DBG2("Map %s disabled successfully", umap->name); + + return LTTNG_OK; + +end: +error: + return ret; +} + +void map_add_or_increment_map_values(struct lttng_ht *map_values, const char *key, + int64_t value, bool has_underflowed, bool has_overflowed) +{ + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_node_str *node; + struct lttng_ht_iter ht_iter; + + lttng_ht_lookup(map_values, (void *) key, &ht_iter); + node = lttng_ht_iter_get_node_str(&ht_iter); + if (node == NULL) { + /* + * If the key is absent, the key value mapping. + */ + kv_entry = zmalloc(sizeof(*kv_entry)); + if (!kv_entry) { + abort(); + } + + kv_entry->key = strdup(key); + kv_entry->value = value; + kv_entry->has_underflowed = has_underflowed; + kv_entry->has_overflowed = has_overflowed; + + lttng_ht_node_init_str(&kv_entry->node, (char *) kv_entry->key); + lttng_ht_add_unique_str(map_values, &kv_entry->node); + + } else { + /* + * If the key is already present, increment the current value with the + * new value. + */ + kv_entry = caa_container_of(node, typeof(*kv_entry), node); + kv_entry->value += value; + kv_entry->has_underflowed |= has_underflowed; + kv_entry->has_overflowed |= has_overflowed; + } +} + +int map_new_content_section(struct lttng_map_content *map_content, + enum lttng_map_key_value_pair_list_type list_type, + bool summed_all_cpus, unsigned int identifier, + int cpu, struct lttng_ht *values) +{ + int ret; + struct lttng_map_key_value_pair_list *kv_pair_list; + enum lttng_map_status map_status; + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_iter key_iter; + + kv_pair_list = lttng_map_key_value_pair_list_create(list_type, + summed_all_cpus); + switch (list_type) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + map_status = lttng_map_key_value_pair_list_set_identifier( + kv_pair_list, identifier); + assert(map_status == LTTNG_MAP_STATUS_OK); + break; + default: + break; + } + + if (!summed_all_cpus) { + map_status = lttng_map_key_value_pair_list_set_cpu(kv_pair_list, + cpu); + } + + cds_lfht_for_each_entry(values->ht, &key_iter.iter, kv_entry, node.node) { + struct lttng_ht_iter entry_iter; + + struct lttng_map_key_value_pair *pair = + lttng_map_key_value_pair_create(kv_entry->key, + kv_entry->value); + if (kv_entry->has_overflowed) { + lttng_map_key_value_pair_set_has_overflowed(pair); + } + + if (kv_entry->has_underflowed) { + lttng_map_key_value_pair_set_has_underflowed(pair); + } + + map_status = lttng_map_key_value_pair_list_append_key_value( + kv_pair_list, pair); + + entry_iter.iter.node = &kv_entry->node.node; + lttng_ht_del(values, &entry_iter); + + free(kv_entry->key); + free(kv_entry); + } + + map_status = lttng_map_content_append_key_value_list(map_content, + kv_pair_list); + if (map_status != LTTNG_MAP_STATUS_OK) { + lttng_map_key_value_pair_list_destroy(kv_pair_list); + ret = -1; + ERR("Error appending key-value pair list to map content object"); + goto end; + } + ret = 0; +end: + return ret; +} + diff --git a/src/bin/lttng-sessiond/map.h b/src/bin/lttng-sessiond/map.h new file mode 100644 index 000000000..1c84fe667 --- /dev/null +++ b/src/bin/lttng-sessiond/map.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ +#ifndef _LTT_MAP_H +#define _LTT_MAP_H + +#include +#include +#include "trace-kernel.h" +#include "trace-ust.h" + +struct map_kv_ht_entry { + struct lttng_ht_node_str node; + char *key; + int64_t value; + bool has_overflowed; + bool has_underflowed; +}; + +enum lttng_error_code map_kernel_add(struct ltt_kernel_session *ksession, + struct lttng_map *map); +enum lttng_error_code map_kernel_enable(struct ltt_kernel_session *ksession, + struct ltt_kernel_map *kmap); +enum lttng_error_code map_kernel_disable(struct ltt_kernel_session *ksession, + struct ltt_kernel_map *kmap); + +int map_ust_add(struct ltt_ust_session *usession, + struct lttng_map *map); +int map_ust_enable(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); +int map_ust_disable(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); + +void map_add_or_increment_map_values(struct lttng_ht *map_values, const char *key, + int64_t value, bool has_underflowed, bool has_overflowed); + +int map_new_content_section(struct lttng_map_content *map_content, + enum lttng_map_key_value_pair_list_type list_type, + bool summed_all_cpus, unsigned int identifier, + int cpu, struct lttng_ht *values); + +#endif /* _LTT_MAP_H */ diff --git a/src/bin/lttng-sessiond/modprobe.c b/src/bin/lttng-sessiond/modprobe.c index 28385ea39..8d162857f 100644 --- a/src/bin/lttng-sessiond/modprobe.c +++ b/src/bin/lttng-sessiond/modprobe.c @@ -56,6 +56,14 @@ struct kern_modules_param kern_modules_control_core[] = { .name = (char *) "lttng-ring-buffer-event_notifier-client", .load_policy = KERNEL_MODULE_PROPERTY_LOAD_POLICY_OPTIONAL, }, + { + .name = (char *) "lttng-counter-client-percpu-64-modular", + .load_policy = KERNEL_MODULE_PROPERTY_LOAD_POLICY_OPTIONAL, + }, + { + .name = (char *) "lttng-counter-client-percpu-32-modular", + .load_policy = KERNEL_MODULE_PROPERTY_LOAD_POLICY_OPTIONAL, + }, }; /* LTTng kerneltracer probe modules list */ @@ -506,14 +514,23 @@ static void modprobe_remove_lttng(const struct kern_modules_param *modules, modprobe[sizeof(modprobe) - 1] = '\0'; ret = system(modprobe); if (ret == -1) { - ERR("Unable to launch modprobe -r for module %s", - modules[i].name); - } else if (modules[i].load_policy == KERNEL_MODULE_PROPERTY_LOAD_POLICY_REQUIRED && WEXITSTATUS(ret) != 0) { - ERR("Unable to remove module %s", - modules[i].name); + if (modules[i].load_policy == KERNEL_MODULE_PROPERTY_LOAD_POLICY_REQUIRED) { + ERR("Unable to launch modprobe -r for required module %s", + modules[i].name); + } else { + DBG("Unable to launch modprobe -r for optional module %s", + modules[i].name); + } + } else if (WEXITSTATUS(ret) != 0) { + if (modules[i].load_policy == KERNEL_MODULE_PROPERTY_LOAD_POLICY_REQUIRED) { + ERR("Unable to remove required module %s", + modules[i].name); + } else { + DBG("Unable to remove optional module %s", + modules[i].name); + } } else { - DBG("Modprobe removal successful %s", - modules[i].name); + DBG("Modprobe removal successful %s", modules[i].name); } } } diff --git a/src/bin/lttng-sessiond/notification-thread-commands.c b/src/bin/lttng-sessiond/notification-thread-commands.c index 44bee3d3b..bc8db39d7 100644 --- a/src/bin/lttng-sessiond/notification-thread-commands.c +++ b/src/bin/lttng-sessiond/notification-thread-commands.c @@ -382,17 +382,48 @@ int notification_thread_client_communication_update( return run_command_no_wait(handle, &cmd); } +enum lttng_error_code notification_thread_command_get_trigger( + struct notification_thread_handle *handle, + const struct lttng_trigger *trigger, + struct lttng_trigger **real_trigger) +{ + int ret; + enum lttng_error_code ret_code; + struct notification_thread_command cmd = {}; + + init_notification_thread_command(&cmd); + + cmd.type = NOTIFICATION_COMMAND_TYPE_GET_TRIGGER; + cmd.parameters.get_trigger.trigger= trigger; + ret = run_command_wait(handle, &cmd); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + + ret_code = cmd.reply_code; + *real_trigger = cmd.reply.get_trigger.trigger; + +end: + return ret_code; +} + +/* + * Takes ownership of the payload if present. + */ LTTNG_HIDDEN -struct lttng_event_notifier_notification * -lttng_event_notifier_notification_create(uint64_t tracer_token, - enum lttng_domain_type domain) +struct lttng_event_notifier_notification *lttng_event_notifier_notification_create( + uint64_t tracer_token, + enum lttng_domain_type domain, + char *payload, + size_t payload_size) { struct lttng_event_notifier_notification *notification = NULL; assert(domain != LTTNG_DOMAIN_NONE); + assert((payload && payload_size) || (!payload && !payload_size)); - notification = zmalloc( - sizeof(struct lttng_event_notifier_notification)); + notification = zmalloc(sizeof(struct lttng_event_notifier_notification)); if (notification == NULL) { ERR("[notification-thread] Error allocating notification"); goto end; @@ -400,6 +431,8 @@ lttng_event_notifier_notification_create(uint64_t tracer_token, notification->tracer_token = tracer_token; notification->type = domain; + notification->capture_buffer = payload; + notification->capture_buf_size = payload_size; end: return notification; @@ -413,5 +446,6 @@ void lttng_event_notifier_notification_destroy( return; } + free(notification->capture_buffer); free(notification); } diff --git a/src/bin/lttng-sessiond/notification-thread-commands.h b/src/bin/lttng-sessiond/notification-thread-commands.h index 50751a94d..882959a29 100644 --- a/src/bin/lttng-sessiond/notification-thread-commands.h +++ b/src/bin/lttng-sessiond/notification-thread-commands.h @@ -32,6 +32,7 @@ enum notification_thread_command_type { NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS, NOTIFICATION_COMMAND_TYPE_QUIT, NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE, + NOTIFICATION_COMMAND_TYPE_GET_TRIGGER, }; struct notification_thread_command { @@ -89,12 +90,19 @@ struct notification_thread_command { enum client_transmission_status status; } client_communication_update; + struct { + const struct lttng_trigger *trigger; + } get_trigger; + } parameters; union { struct { struct lttng_triggers *triggers; } list_triggers; + struct { + struct lttng_trigger *trigger; + } get_trigger; } reply; /* lttng_waiter on which to wait for command reply (optional). */ struct lttng_waiter reply_waiter; @@ -166,4 +174,9 @@ enum lttng_error_code notification_thread_command_remove_tracer_event_source( void notification_thread_command_quit( struct notification_thread_handle *handle); +enum lttng_error_code notification_thread_command_get_trigger( + struct notification_thread_handle *handle, + const struct lttng_trigger *trigger, + struct lttng_trigger **real_trigger); + #endif /* NOTIFICATION_THREAD_COMMANDS_H */ diff --git a/src/bin/lttng-sessiond/notification-thread-events.c b/src/bin/lttng-sessiond/notification-thread-events.c index 747902612..0498aac35 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.c +++ b/src/bin/lttng-sessiond/notification-thread-events.c @@ -21,12 +21,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -39,15 +42,20 @@ #include #include "condition-internal.h" +#include "event-notifier-error-accounting.h" #include "notification-thread.h" #include "notification-thread-events.h" #include "notification-thread-commands.h" #include "lttng-sessiond.h" #include "kernel.h" +#include "event.h" #define CLIENT_POLL_MASK_IN (LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP) #define CLIENT_POLL_MASK_IN_OUT (CLIENT_POLL_MASK_IN | LPOLLOUT) +/* The tracers currently limit the capture size to PIPE_BUF (4kb on linux). */ +#define MAX_CAPTURE_SIZE (PIPE_BUF) + enum lttng_object_type { LTTNG_OBJECT_TYPE_UNKNOWN, LTTNG_OBJECT_TYPE_NONE, @@ -116,6 +124,7 @@ struct lttng_trigger_ht_element { struct lttng_trigger *trigger; struct cds_lfht_node node; struct cds_lfht_node node_by_name_uid; + struct cds_list_head client_list_trigger_node; /* call_rcu delayed reclaim. */ struct rcu_head rcu_node; }; @@ -308,7 +317,7 @@ int match_client_list_condition(struct cds_lfht_node *node, const void *key) client_list = caa_container_of(node, struct notification_client_list, notification_trigger_clients_ht_node); - condition = lttng_trigger_get_const_condition(client_list->trigger); + condition = client_list->condition; return !!lttng_condition_is_equal(condition_key, condition); } @@ -346,6 +355,8 @@ const char *notification_command_type_str( return "REMOVE_TRACER_EVENT_SOURCE"; case NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS: return "LIST_TRIGGERS"; + case NOTIFICATION_COMMAND_TYPE_GET_TRIGGER: + return "GET_TRIGGER"; case NOTIFICATION_COMMAND_TYPE_QUIT: return "QUIT"; case NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE: @@ -462,7 +473,7 @@ 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: + case LTTNG_CONDITION_TYPE_ON_EVENT: return LTTNG_OBJECT_TYPE_NONE; default: return LTTNG_OBJECT_TYPE_UNKNOWN; @@ -655,62 +666,119 @@ void notification_client_list_release(struct urcu_ref *list_ref) container_of(list_ref, typeof(*list), ref); struct notification_client_list_element *client_list_element, *tmp; + lttng_condition_put(list->condition); + 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) { + &list->clients_list, node) { free(client_list_element); } + + assert(cds_list_empty(&list->triggers_list)); + pthread_mutex_destroy(&list->lock); call_rcu(&list->rcu_node, free_notification_client_list_rcu); } +static +bool condition_applies_to_client(const struct lttng_condition *condition, + struct notification_client *client) +{ + bool applies = false; + struct lttng_condition_list_element *condition_list_element; + + cds_list_for_each_entry(condition_list_element, &client->condition_list, + node) { + applies = lttng_condition_is_equal( + condition_list_element->condition, + condition); + if (applies) { + break; + } + } + return applies; +} + static struct notification_client_list *notification_client_list_create( - const struct lttng_trigger *trigger) + struct notification_thread_state *state, + const struct lttng_condition *condition) { - struct notification_client_list *client_list = - zmalloc(sizeof(*client_list)); + struct notification_client *client; + struct cds_lfht_iter iter; + struct notification_client_list *client_list; + client_list = zmalloc(sizeof(*client_list)); if (!client_list) { - goto error; + goto end; } + pthread_mutex_init(&client_list->lock, NULL); + /* + * The trigger that owns the condition has the first reference to this + * client list. + */ 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; -} + CDS_INIT_LIST_HEAD(&client_list->clients_list); + CDS_INIT_LIST_HEAD(&client_list->triggers_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); + /* + * Create a copy of the condition so that it's independent of any + * trigger. The client list may outlive the trigger object (which owns + * the condition) that is used to create it. + */ + client_list->condition = lttng_condition_copy(condition); + + /* Build a list of clients to which this new condition applies. */ + cds_lfht_for_each_entry (state->client_socket_ht, &iter, client, + client_socket_ht_node) { + struct notification_client_list_element *client_list_element; - assert(!list->notification_trigger_clients_ht); - notification_client_list_get(list); + if (!condition_applies_to_client(condition, client)) { + continue; + } - list->notification_trigger_clients_ht = + client_list_element = zmalloc(sizeof(*client_list_element)); + if (!client_list_element) { + 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->clients_list); + } + + client_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); + /* + * Add the client list to the global list of client list. + */ + cds_lfht_add_unique(state->notification_trigger_clients_ht, + lttng_condition_hash(client_list->condition), + match_client_list_condition, + client_list->condition, + &client_list->notification_trigger_clients_ht_node); rcu_read_unlock(); + goto end; + +error_put_client_list: + notification_client_list_put(client_list); + client_list = NULL; + +end: + return client_list; } -LTTNG_HIDDEN void notification_client_list_put(struct notification_client_list *list) { if (!list) { @@ -998,12 +1066,11 @@ int evaluate_condition_for_client(const struct lttng_trigger *trigger, * subscribing. */ cds_lfht_node_init(&client_list.notification_trigger_clients_ht_node); - CDS_INIT_LIST_HEAD(&client_list.list); - client_list.trigger = trigger; + CDS_INIT_LIST_HEAD(&client_list.clients_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_list_add(&client_list_element.node, &client_list.clients_list); /* Send evaluation result to the newly-subscribed client. */ DBG("[notification-thread] Newly subscribed-to condition evaluated to true, notifying client"); @@ -1077,13 +1144,19 @@ int notification_thread_client_subscribe(struct notification_client *client, * 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; + struct lttng_trigger_ht_element *trigger_ht_element; + pthread_mutex_lock(&client_list->lock); + cds_list_for_each_entry(trigger_ht_element, + &client_list->triggers_list, client_list_trigger_node) { + if (evaluate_condition_for_client(trigger_ht_element->trigger, condition, + client, state)) { + WARN("[notification-thread] Evaluation of a condition on client subscription failed, aborting."); + ret = -1; + free(client_list_element); + goto end; + } } + pthread_mutex_unlock(&client_list->lock); /* * Add the client to the list of clients interested in a given trigger @@ -1094,7 +1167,7 @@ int notification_thread_client_subscribe(struct notification_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); + cds_list_add(&client_list_element->node, &client_list->clients_list); pthread_mutex_unlock(&client_list->lock); end: if (_status) { @@ -1165,7 +1238,7 @@ int notification_thread_client_unsubscribe( pthread_mutex_lock(&client_list->lock); cds_list_for_each_entry_safe(client_list_element, client_tmp, - &client_list->list, node) { + &client_list->clients_list, node) { if (client_list_element->client->id != client->id) { continue; } @@ -1358,25 +1431,6 @@ fail: return false; } -static -bool trigger_applies_to_client(struct lttng_trigger *trigger, - struct notification_client *client) -{ - bool applies = false; - struct lttng_condition_list_element *condition_list_element; - - cds_list_for_each_entry(condition_list_element, &client->condition_list, - node) { - applies = lttng_condition_is_equal( - condition_list_element->condition, - lttng_trigger_get_condition(trigger)); - if (applies) { - break; - } - } - return applies; -} - /* Must be called with RCU read lock held. */ static struct lttng_session_trigger_list *get_session_trigger_list( @@ -1718,7 +1772,6 @@ error: session_info_put(session_info); return 1; } - static void free_channel_trigger_list_rcu(struct rcu_head *node) { @@ -2118,6 +2171,29 @@ end: return ret; } +static +int condition_on_event_update_error_count(struct lttng_trigger *trigger) +{ + int ret = 0; + uint64_t error_count = 0; + struct lttng_condition *condition; + enum event_notifier_error_accounting_status status; + + condition = lttng_trigger_get_condition(trigger); + + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); + + status = event_notifier_error_accounting_get_count(trigger, &error_count); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting event notifier error count."); + ret = -1; + } + + lttng_condition_on_event_set_error_count(condition, error_count); + + return ret; +} + int handle_notification_thread_remove_tracer_event_source_no_result( struct notification_thread_state *state, int tracer_event_source_fd) @@ -2165,6 +2241,12 @@ static int handle_notification_thread_command_list_triggers( continue; } + if (lttng_trigger_needs_tracer_notifier(trigger_ht_element->trigger)) { + ret = condition_on_event_update_error_count( + trigger_ht_element->trigger); + assert(!ret); + } + ret = lttng_triggers_add(local_triggers, trigger_ht_element->trigger); if (ret < 0) { @@ -2186,6 +2268,42 @@ end: return ret; } +static +int handle_notification_thread_command_get_trigger( + struct notification_thread_state *state, + const struct lttng_trigger *trigger, + struct lttng_trigger **real_trigger, + enum lttng_error_code *_cmd_result) +{ + int ret = -1; + struct cds_lfht_iter iter; + struct lttng_trigger_ht_element *trigger_ht_element; + enum lttng_error_code cmd_result = LTTNG_ERR_TRIGGER_NOT_FOUND; + + rcu_read_lock(); + + cds_lfht_for_each_entry(state->triggers_ht, &iter, + trigger_ht_element, node) { + + if (lttng_trigger_is_equal(trigger, trigger_ht_element->trigger)) { + /* + * Take one reference on the return trigger. + */ + *real_trigger = trigger_ht_element->trigger; + lttng_trigger_get(*real_trigger); + ret = 0; + cmd_result = LTTNG_OK; + goto end; + } + } + + +end: + rcu_read_unlock(); + *_cmd_result = cmd_result; + return ret; +} + static bool condition_is_supported(struct lttng_condition *condition) { @@ -2217,12 +2335,12 @@ bool condition_is_supported(struct lttng_condition *condition) is_supported = kernel_supports_ring_buffer_snapshot_sample_positions() == 1; break; } - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: { const struct lttng_event_rule *event_rule; enum lttng_domain_type domain; const enum lttng_condition_status status = - lttng_condition_event_rule_get_rule( + lttng_condition_on_event_get_rule( condition, &event_rule); assert(status == LTTNG_CONDITION_STATUS_OK); @@ -2424,6 +2542,114 @@ enum lttng_error_code generate_trigger_name( return ret_code; } +static inline +void notif_thread_state_remove_trigger_ht_elem( + struct notification_thread_state *state, + struct lttng_trigger_ht_element *trigger_ht_element) +{ + assert(state); + assert(trigger_ht_element); + + cds_lfht_del(state->triggers_ht, &trigger_ht_element->node); + cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid); +} + +static +enum lttng_error_code setup_tracer_notifier( + struct notification_thread_state *state, + struct lttng_trigger *trigger) +{ + enum lttng_error_code ret; + enum event_notifier_error_accounting_status error_accounting_status; + struct cds_lfht_node *node; + uint64_t error_counter_index = 0; + struct lttng_condition *condition = lttng_trigger_get_condition(trigger); + struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL; + + trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element)); + if (!trigger_tokens_ht_element) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Add trigger token to the trigger_tokens_ht. */ + cds_lfht_node_init(&trigger_tokens_ht_element->node); + trigger_tokens_ht_element->token = LTTNG_OPTIONAL_GET(trigger->tracer_token); + trigger_tokens_ht_element->trigger = trigger; + + node = cds_lfht_add_unique(state->trigger_tokens_ht, + hash_key_u64(&trigger_tokens_ht_element->token, lttng_ht_seed), + match_trigger_token, + &trigger_tokens_ht_element->token, + &trigger_tokens_ht_element->node); + if (node != &trigger_tokens_ht_element->node) { + ret = LTTNG_ERR_TRIGGER_EXISTS; + goto error_free_ht_element; + } + + error_accounting_status = event_notifier_error_accounting_register_event_notifier( + trigger, &error_counter_index); + if (error_accounting_status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + if (error_accounting_status == EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_NO_INDEX_AVAILABLE) { + DBG("Trigger group error accounting counter full."); + ret = LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL; + } else { + ERR("Error registering trigger for error accounting"); + ret = LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION; + } + + goto error_remove_ht_element; + } + + lttng_condition_on_event_set_error_counter_index( + condition, error_counter_index); + + ret = LTTNG_OK; + goto end; + +error_remove_ht_element: + cds_lfht_del(state->trigger_tokens_ht, &trigger_tokens_ht_element->node); +error_free_ht_element: + free(trigger_tokens_ht_element); +end: + return ret; +} + +static +void set_action_incr_value_tracer_token(struct notification_thread_state *state, + struct lttng_action *action) +{ + lttng_action_incr_value_set_tracer_token(action, + state->trigger_id.next_tracer_token++); +} + +static +void set_all_action_incr_value_tracer_token(struct notification_thread_state *state, + struct lttng_action *action) +{ + unsigned int i, count; + enum lttng_action_status action_status; + enum lttng_action_type action_type; + action_type = lttng_action_get_type(action); + + if (action_type != LTTNG_ACTION_TYPE_GROUP) { + if (action_type == LTTNG_ACTION_TYPE_INCREMENT_VALUE) { + set_action_incr_value_tracer_token(state, action); + } + } else { + action_status = lttng_action_group_get_count(action, &count); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + for (i = 0; i < count; i++) { + struct lttng_action *inner_action = + lttng_action_group_get_mutable_at_index( + action, i); + set_all_action_incr_value_tracer_token(state, + inner_action); + } + } +} + /* * FIXME A client's credentials are not checked when registering a trigger. * @@ -2447,13 +2673,9 @@ int handle_notification_thread_command_register_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; - struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL; struct cds_lfht_node *node; - struct cds_lfht_iter iter; const char* trigger_name; bool free_trigger = true; struct lttng_evaluation *evaluation = NULL; @@ -2469,6 +2691,8 @@ int handle_notification_thread_command_register_trigger( /* Set the trigger's tracer token. */ lttng_trigger_set_tracer_token(trigger, trigger_tracer_token); + set_all_action_incr_value_tracer_token(state, lttng_trigger_get_action(trigger)); + if (lttng_trigger_get_name(trigger, &trigger_name) == LTTNG_TRIGGER_STATUS_UNSET) { const enum lttng_error_code ret_code = generate_trigger_name( @@ -2530,95 +2754,72 @@ int handle_notification_thread_command_register_trigger( 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) { - /* Fatal error. */ - ret = -1; - cds_lfht_del(state->triggers_ht, - &trigger_ht_element->node); - cds_lfht_del(state->triggers_by_name_uid_ht, - &trigger_ht_element->node_by_name_uid); - goto error_free_ht_element; - } + /* + * Some triggers might need a tracer notifier depending on its + * condition and actions. + */ + if (lttng_trigger_needs_tracer_notifier(trigger)) { + enum lttng_error_code error_code; + + error_code = setup_tracer_notifier(state, trigger); + if (error_code != LTTNG_OK) { + notif_thread_state_remove_trigger_ht_elem(state, + trigger_ht_element); + if (error_code == LTTNG_ERR_NOMEM) { + ret = -1; + } else { + *cmd_result = error_code; + ret = 0; + } - /* Add trigger token to the trigger_tokens_ht. */ - cds_lfht_node_init(&trigger_tokens_ht_element->node); - trigger_tokens_ht_element->token = - LTTNG_OPTIONAL_GET(trigger->tracer_token); - trigger_tokens_ht_element->trigger = trigger; - - node = cds_lfht_add_unique(state->trigger_tokens_ht, - hash_key_u64(&trigger_tokens_ht_element->token, - lttng_ht_seed), - match_trigger_token, - &trigger_tokens_ht_element->token, - &trigger_tokens_ht_element->node); - if (node != &trigger_tokens_ht_element->node) { - /* Internal corruption, fatal error. */ - ret = -1; - *cmd_result = LTTNG_ERR_TRIGGER_EXISTS; - cds_lfht_del(state->triggers_ht, - &trigger_ht_element->node); - cds_lfht_del(state->triggers_by_name_uid_ht, - &trigger_ht_element->node_by_name_uid); 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; - /* * The rest only applies to triggers that have a "notify" action. * It is not skipped as this is the only action type currently * supported. */ if (is_trigger_action_notify(trigger)) { - client_list = notification_client_list_create(trigger); + /* + * Find or create the client list of this condition. It may + * already be present if another trigger is already registered + * with the same condition. + */ + client_list = get_client_list_from_condition(state, condition); if (!client_list) { - ret = -1; - goto error_free_ht_element; - } - - /* Build a list of clients to which this new trigger applies. */ - cds_lfht_for_each_entry (state->client_socket_ht, &iter, client, - client_socket_ht_node) { - if (!trigger_applies_to_client(trigger, client)) { - continue; - } - - client_list_element = - zmalloc(sizeof(*client_list_element)); - if (!client_list_element) { - ret = -1; - goto error_put_client_list; + /* + * No client list for this condition yet. We create new + * one and build it up. + */ + client_list = notification_client_list_create(state, condition); + if (!client_list) { + ERR("Error creating notification client list for trigger %s", trigger->name); + goto error_free_ht_element; } - - CDS_INIT_LIST_HEAD(&client_list_element->node); - client_list_element->client = client; - cds_list_add(&client_list_element->node, - &client_list->list); } - /* - * Client list ownership transferred to the - * notification_trigger_clients_ht. - */ - publish_notification_client_list(state, client_list); + CDS_INIT_LIST_HEAD(&trigger_ht_element->client_list_trigger_node); + + pthread_mutex_lock(&client_list->lock); + cds_list_add(&trigger_ht_element->client_list_trigger_node, &client_list->triggers_list); + pthread_mutex_unlock(&client_list->lock); } + /* + * Ownership of the trigger and of its wrapper was transfered to + * the triggers_ht. Same for token ht element if necessary. + */ + trigger_ht_element = NULL; + free_trigger = false; + 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_put_client_list; + goto error_free_ht_element; } break; case LTTNG_OBJECT_TYPE_CHANNEL: @@ -2628,7 +2829,7 @@ int handle_notification_thread_command_register_trigger( */ ret = bind_trigger_to_matching_channels(trigger, state); if (ret) { - goto error_put_client_list; + goto error_free_ht_element; } break; case LTTNG_OBJECT_TYPE_NONE: @@ -2636,7 +2837,7 @@ int handle_notification_thread_command_register_trigger( default: ERR("Unknown object type on which to bind a newly registered trigger was encountered"); ret = -1; - goto error_put_client_list; + goto error_free_ht_element; } /* @@ -2694,7 +2895,7 @@ int handle_notification_thread_command_register_trigger( if (ret) { /* Fatal error. */ - goto error_put_client_list; + goto error_free_ht_element; } DBG("Newly registered trigger's condition evaluated to %s", @@ -2702,7 +2903,7 @@ int handle_notification_thread_command_register_trigger( if (!evaluation) { /* Evaluation yielded nothing. Normal exit. */ ret = 0; - goto end; + goto success; } /* @@ -2723,7 +2924,7 @@ int handle_notification_thread_command_register_trigger( */ ERR("Fatal error occurred while enqueuing action associated to newly registered trigger"); ret = -1; - goto error_put_client_list; + goto error_free_ht_element; case ACTION_EXECUTOR_STATUS_OVERFLOW: /* * TODO Add trigger identification (name/id) when @@ -2733,18 +2934,16 @@ int handle_notification_thread_command_register_trigger( */ WARN("No space left when enqueuing action associated to newly registered trigger"); ret = 0; - goto end; + goto success; default: abort(); } -end: +success: *cmd_result = LTTNG_OK; DBG("Registered trigger: name = `%s`, tracer token = %" PRIu64, trigger_name, trigger_tracer_token); - -error_put_client_list: - notification_client_list_put(client_list); + goto end; error_free_ht_element: if (trigger_ht_element) { @@ -2752,12 +2951,11 @@ error_free_ht_element: call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu); } - - free(trigger_tokens_ht_element); error: if (free_trigger) { lttng_trigger_destroy(trigger); } +end: rcu_read_unlock(); return ret; } @@ -2776,6 +2974,36 @@ void free_notification_trigger_tokens_ht_element_rcu(struct rcu_head *node) rcu_node)); } +static +void teardown_tracer_notifier(struct notification_thread_state *state, + const struct lttng_trigger *trigger) +{ + struct cds_lfht_iter iter; + 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; + } + + event_notifier_error_accounting_unregister_event_notifier( + trigger_tokens_ht_element->trigger); + + /* TODO talk to all app and remove it */ + DBG("[notification-thread] Removed trigger from tokens_ht"); + cds_lfht_del(state->trigger_tokens_ht, + &trigger_tokens_ht_element->node); + + call_rcu(&trigger_tokens_ht_element->rcu_node, + free_notification_trigger_tokens_ht_element_rcu); + + break; + } +} + static int handle_notification_thread_command_unregister_trigger( struct notification_thread_state *state, @@ -2824,28 +3052,13 @@ int handle_notification_thread_command_unregister_trigger( } } - 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; - } - - 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 (lttng_trigger_needs_tracer_notifier(trigger)) { + teardown_tracer_notifier(state, trigger); } + trigger_ht_element = caa_container_of(triggers_ht_node, + struct lttng_trigger_ht_element, node); + if (is_trigger_action_notify(trigger)) { /* * Remove and release the client list from @@ -2854,6 +3067,10 @@ int handle_notification_thread_command_unregister_trigger( client_list = get_client_list_from_condition(state, condition); assert(client_list); + pthread_mutex_lock(&client_list->lock); + cds_list_del(&trigger_ht_element->client_list_trigger_node); + pthread_mutex_unlock(&client_list->lock); + /* Put new reference and the hashtable's reference. */ notification_client_list_put(client_list); notification_client_list_put(client_list); @@ -2861,10 +3078,7 @@ int handle_notification_thread_command_unregister_trigger( } /* Remove trigger from triggers_ht. */ - trigger_ht_element = caa_container_of(triggers_ht_node, - struct lttng_trigger_ht_element, node); - cds_lfht_del(state->triggers_by_name_uid_ht, &trigger_ht_element->node_by_name_uid); - cds_lfht_del(state->triggers_ht, triggers_ht_node); + notif_thread_state_remove_trigger_ht_elem(state, trigger_ht_element); /* Release the ownership of the trigger. */ lttng_trigger_destroy(trigger_ht_element->trigger); @@ -2974,6 +3188,19 @@ int handle_notification_thread_command( cmd->reply_code = LTTNG_OK; ret = 1; goto end; + case NOTIFICATION_COMMAND_TYPE_GET_TRIGGER: + { + struct lttng_trigger *trigger = NULL; + + ret = handle_notification_thread_command_get_trigger( + state, + cmd->parameters.get_trigger.trigger, + &trigger, + &cmd->reply_code); + cmd->reply.get_trigger.trigger = trigger; + ret = 0; + break; + } case NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE: { const enum client_transmission_status client_status = @@ -4153,7 +4380,7 @@ int notification_client_list_send_evaluation( pthread_mutex_lock(&client_list->lock); cds_list_for_each_entry_safe(client_list_element, tmp, - &client_list->list, node) { + &client_list->clients_list, node) { enum client_transmission_status transmission_status; struct notification_client *client = client_list_element->client; @@ -4244,6 +4471,8 @@ struct lttng_event_notifier_notification *recv_one_event_notifier_notification( int ret; uint64_t token; struct lttng_event_notifier_notification *notification = NULL; + char *capture_buffer = NULL; + size_t capture_buffer_size; void *reception_buffer; size_t reception_size; @@ -4280,17 +4509,55 @@ struct lttng_event_notifier_notification *recv_one_event_notifier_notification( switch(domain) { case LTTNG_DOMAIN_UST: token = ust_notification.token; + capture_buffer_size = ust_notification.capture_buf_size; break; case LTTNG_DOMAIN_KERNEL: token = kernel_notification.token; + capture_buffer_size = kernel_notification.capture_buf_size; break; default: abort(); } - notification = lttng_event_notifier_notification_create( - token, domain); + if (capture_buffer_size == 0) { + capture_buffer = NULL; + goto skip_capture; + } + + if (capture_buffer_size > MAX_CAPTURE_SIZE) { + ERR("[notification-thread] Event notifier has a capture payload size which exceeds the maximum allowed size: capture_payload_size = %zu bytes, max allowed size = %d bytes", + capture_buffer_size, MAX_CAPTURE_SIZE); + goto end; + } + + capture_buffer = zmalloc(capture_buffer_size); + if (!capture_buffer) { + ERR("[notification-thread] Failed to allocate capture buffer"); + goto end; + } + + /* Fetch additional payload (capture). */ + ret = lttng_read(notification_pipe_read_fd, capture_buffer, capture_buffer_size); + if (ret != capture_buffer_size) { + ERR("[notification-thread] Failed to read from event source pipe (fd = %i)", + notification_pipe_read_fd); + goto end; + } + +skip_capture: + notification = lttng_event_notifier_notification_create(token, domain, + capture_buffer, capture_buffer_size); + if (notification == NULL) { + goto end; + } + + /* + * Ownership transfered to the lttng_event_notifier_notification object. + */ + capture_buffer = NULL; + end: + free(capture_buffer); return notification; } @@ -4307,6 +4574,7 @@ int dispatch_one_event_notifier_notification(struct notification_thread_state *s struct notification_client_list *client_list = NULL; const char *trigger_name; int ret; + unsigned int capture_count = 0; /* Find triggers associated with this token. */ rcu_read_lock(); @@ -4339,14 +4607,34 @@ int dispatch_one_event_notifier_notification(struct notification_thread_state *s trigger_status = lttng_trigger_get_name(element->trigger, &trigger_name); assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + if (lttng_condition_on_event_get_capture_descriptor_count( + lttng_trigger_get_const_condition(element->trigger), + &capture_count) != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to get capture count"); + ret = -1; + goto end; + } + + if (!notification->capture_buffer && capture_count != 0) { + ERR("Expected capture but capture buffer is null"); + ret = -1; + goto end; + } + evaluation = lttng_evaluation_event_rule_create( - trigger_name); + container_of(lttng_trigger_get_const_condition( + element->trigger), + struct lttng_condition_on_event, + parent), + trigger_name, + notification->capture_buffer, + notification->capture_buf_size, false); + if (evaluation == NULL) { ERR("[notification-thread] Failed to create event rule hit evaluation while creating and enqueuing action executor job"); ret = -1; goto end_unlock; } - client_list = get_client_list_from_condition(state, lttng_trigger_get_const_condition(element->trigger)); executor_status = action_executor_enqueue(state->executor, @@ -4374,7 +4662,7 @@ int dispatch_one_event_notifier_notification(struct notification_thread_state *s /* 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) { + &client_list->clients_list, node) { enum client_transmission_status transmission_status; struct notification_client *client = client_list_element->client; @@ -4404,6 +4692,7 @@ next_client: pthread_mutex_unlock(&client_list->lock); break; } + case ACTION_EXECUTOR_STATUS_INVALID: case ACTION_EXECUTOR_STATUS_ERROR: /* Fatal error, shut down everything. */ ERR("Fatal error encoutered while enqueuing action to the action executor"); @@ -4417,6 +4706,7 @@ next_client: end_unlock: notification_client_list_put(client_list); rcu_read_unlock(); +end: return ret; } @@ -4425,14 +4715,14 @@ int handle_one_event_notifier_notification( struct notification_thread_state *state, int pipe, enum lttng_domain_type domain) { - int ret; + int ret = 0; struct lttng_event_notifier_notification *notification = NULL; notification = recv_one_event_notifier_notification(pipe, domain); if (notification == NULL) { + /* Reception failed, don't consider it fatal. */ ERR("[notification-thread] Error receiving an event notifier notification from tracer: fd = %i, domain = %s", pipe, lttng_domain_type_str(domain)); - ret = -1; goto end; } diff --git a/src/bin/lttng-sessiond/notification-thread-internal.h b/src/bin/lttng-sessiond/notification-thread-internal.h index 0403527dc..38d968a4d 100644 --- a/src/bin/lttng-sessiond/notification-thread-internal.h +++ b/src/bin/lttng-sessiond/notification-thread-internal.h @@ -82,6 +82,8 @@ struct channel_info { struct lttng_event_notifier_notification { uint64_t tracer_token; enum lttng_domain_type type; + size_t capture_buf_size; + char *capture_buffer; }; struct notification_client_list_element { @@ -111,8 +113,9 @@ struct notification_client_list_element { struct notification_client_list { pthread_mutex_t lock; struct urcu_ref ref; - const struct lttng_trigger *trigger; - struct cds_list_head list; + struct lttng_condition *condition; + struct cds_list_head triggers_list; + struct cds_list_head clients_list; /* Weak reference to container. */ struct cds_lfht *notification_trigger_clients_ht; struct cds_lfht_node notification_trigger_clients_ht_node; @@ -248,10 +251,15 @@ int notification_thread_client_communication_update( notification_client_id id, enum client_transmission_status transmission_status); +/* + * Takes ownership of the payload if present. + */ LTTNG_HIDDEN struct lttng_event_notifier_notification *lttng_event_notifier_notification_create( uint64_t tracer_token, - enum lttng_domain_type domain); + enum lttng_domain_type domain, + char *payload, + size_t payload_size); LTTNG_HIDDEN void lttng_event_notifier_notification_destroy( diff --git a/src/bin/lttng-sessiond/notification-thread.c b/src/bin/lttng-sessiond/notification-thread.c index dd1dc3e8f..b91dc5798 100644 --- a/src/bin/lttng-sessiond/notification-thread.c +++ b/src/bin/lttng-sessiond/notification-thread.c @@ -27,6 +27,7 @@ #include "lttng-sessiond.h" #include "health-sessiond.h" #include "thread.h" +#include "testpoint.h" #include "kernel.h" #include @@ -35,6 +36,8 @@ #include #include + +int notifier_consumption_paused; /* * Destroy the thread data previously created by the init function. */ @@ -482,6 +485,7 @@ int init_thread_state(struct notification_thread_handle *handle, if (!state->channels_ht) { goto error; } + state->sessions_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); if (!state->sessions_ht) { @@ -574,6 +578,17 @@ static int handle_event_notification_pipe(int event_source_fd, goto end; } + if (testpoint(sessiond_handle_notifier_event_pipe)) { + ret = 0; + goto end; + } + + if (caa_unlikely(notifier_consumption_paused)) { + DBG("Event notifier notification consumption paused, sleeping..."); + sleep(1); + goto end; + } + ret = handle_notification_thread_event_notification( state, event_source_fd, domain); if (ret) { @@ -582,6 +597,7 @@ static int handle_event_notification_pipe(int event_source_fd, ret = -1; goto end; } + end: return ret; } @@ -640,6 +656,10 @@ void *thread_notification(void *data) goto end; } + if (testpoint(sessiond_thread_notification)) { + goto end; + } + while (true) { int fd_count, i; diff --git a/src/bin/lttng-sessiond/register.c b/src/bin/lttng-sessiond/register.c index 6381c1179..ac1583dc2 100644 --- a/src/bin/lttng-sessiond/register.c +++ b/src/bin/lttng-sessiond/register.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -20,7 +21,6 @@ #include "testpoint.h" #include "health-sessiond.h" #include "fd-limit.h" -#include "shm.h" #include "utils.h" #include "thread.h" diff --git a/src/bin/lttng-sessiond/save.c b/src/bin/lttng-sessiond/save.c index 6ca756365..da6d948c4 100644 --- a/src/bin/lttng-sessiond/save.c +++ b/src/bin/lttng-sessiond/save.c @@ -131,6 +131,87 @@ end: return ret; } +static +int save_map_attributes(struct config_writer *writer, + struct lttng_map *map) +{ + int ret; + unsigned int i; + + ret = config_writer_write_element_unsigned_int(writer, + config_element_bitness, + lttng_map_get_bitness(map)==LTTNG_MAP_BITNESS_32BITS ? 32: 64); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + assert( lttng_map_get_boundary_policy(map) == LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW); + ret = config_writer_write_element_string(writer, + config_element_boundary_policy, config_boundary_policy_overflow); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_bool(writer, + config_element_coalesce_hits, + lttng_map_get_coalesce_hits(map)); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_open_element(writer, + config_element_dimensions); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + for (i = 0; i < lttng_map_get_dimension_count(map); i++) { + enum lttng_map_status map_status; + uint64_t dim_len; + + ret = config_writer_open_element(writer, + config_element_dimension); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + map_status = lttng_map_get_dimension_length(map, i, &dim_len); + if (map_status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_unsigned_int(writer, + config_element_dimension_size, dim_len); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + /* dimension */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + } + + /* dimensions */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + +end: + return ret; +} + + /* Return LTTNG_OK on success else a LTTNG_ERR* code. */ static int save_ust_channel_attributes(struct config_writer *writer, @@ -1642,6 +1723,61 @@ end: return ret; } +/* Return LTTNG_OK on success else a LTTNG_ERR* code. */ +static +int save_kernel_map(struct config_writer *writer, + struct ltt_kernel_map *kmap) +{ + int ret; + const char *map_name = NULL; + enum lttng_map_status map_status; + + assert(writer); + assert(kmap); + + ret = config_writer_open_element(writer, config_element_map); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + map_status = lttng_map_get_name(kmap->map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_string(writer, config_element_name, + map_name); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_bool(writer, config_element_enabled, + lttng_map_get_is_enabled(kmap->map)); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = save_map_attributes(writer, kmap->map); + if (ret) { + goto end; + } + + /* map */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = LTTNG_OK; +end: + return ret; +} + /* Return LTTNG_OK on success else a LTTNG_ERR* code. */ static int save_ust_channel(struct config_writer *writer, @@ -1745,6 +1881,56 @@ end: return ret; } +/* Return LTTNG_OK on success else a LTTNG_ERR* code. */ +static +int save_ust_map(struct config_writer *writer, + struct ltt_ust_map *ust_map, + struct ltt_ust_session *session) +{ + int ret; + + assert(writer); + assert(ust_map); + assert(session); + + ret = config_writer_open_element(writer, config_element_map); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_string(writer, config_element_name, + ust_map->name); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = config_writer_write_element_bool(writer, config_element_enabled, + ust_map->enabled); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = save_map_attributes(writer, ust_map->map); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + /* /map */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + + ret = LTTNG_OK; +end: + return ret; +} + /* Return LTTNG_OK on success else a LTTNG_ERR* code. */ static int save_kernel_session(struct config_writer *writer, @@ -1752,6 +1938,7 @@ int save_kernel_session(struct config_writer *writer, { int ret; struct ltt_kernel_channel *kchan; + struct ltt_kernel_map *kmap; assert(writer); assert(session); @@ -1785,6 +1972,14 @@ int save_kernel_session(struct config_writer *writer, } } + cds_list_for_each_entry(kmap, &session->kernel_session->map_list.head, + list) { + ret = save_kernel_map(writer, kmap); + if (ret != LTTNG_OK) { + goto end; + } + } + /* /channels */ ret = config_writer_close_element(writer); if (ret) { @@ -2085,6 +2280,7 @@ int save_ust_domain(struct config_writer *writer, { int ret; struct ltt_ust_channel *ust_chan; + struct ltt_ust_map *ust_map; const char *buffer_type_string; struct lttng_ht_node_str *node; struct lttng_ht_iter iter; @@ -2155,6 +2351,32 @@ int save_ust_domain(struct config_writer *writer, goto end; } + ret = config_writer_open_element(writer, config_element_maps); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + rcu_read_lock(); + cds_lfht_for_each_entry(session->ust_session->domain_global.maps->ht, + &iter.iter, node, node) { + ust_map = caa_container_of(node, struct ltt_ust_map, node); + if (domain == LTTNG_DOMAIN_UST) { + ret = save_ust_map(writer, ust_map, session->ust_session); + if (ret != LTTNG_OK) { + rcu_read_unlock(); + goto end; + } + } + } + rcu_read_unlock(); + + /* /maps */ + ret = config_writer_close_element(writer); + if (ret) { + ret = LTTNG_ERR_SAVE_IO_FAIL; + goto end; + } + if (domain == LTTNG_DOMAIN_UST) { ret = config_writer_open_element( writer, config_element_process_attr_trackers); diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h index 5ff20ad41..3c6c34792 100644 --- a/src/bin/lttng-sessiond/session.h +++ b/src/bin/lttng-sessiond/session.h @@ -210,6 +210,7 @@ void session_unlock(struct ltt_session *session); * are being transmitted to the various applications. */ void session_lock_list(void); + int session_trylock_list(void); void session_unlock_list(void); diff --git a/src/bin/lttng-sessiond/sessiond-config.c b/src/bin/lttng-sessiond/sessiond-config.c index c7a918307..fbd301e97 100644 --- a/src/bin/lttng-sessiond/sessiond-config.c +++ b/src/bin/lttng-sessiond/sessiond-config.c @@ -24,6 +24,7 @@ struct sessiond_config sessiond_config_build_defaults = { .verbose_consumer = 0, .agent_tcp_port = { .begin = DEFAULT_AGENT_TCP_PORT_RANGE_BEGIN, .end = DEFAULT_AGENT_TCP_PORT_RANGE_END }, + .event_notifier_error_counter_bucket = 4096, .app_socket_timeout = DEFAULT_APP_SOCKET_RW_TIMEOUT, .no_kernel = false, diff --git a/src/bin/lttng-sessiond/sessiond-config.h b/src/bin/lttng-sessiond/sessiond-config.h index 9ce036e70..9369f4cc0 100644 --- a/src/bin/lttng-sessiond/sessiond-config.h +++ b/src/bin/lttng-sessiond/sessiond-config.h @@ -29,6 +29,8 @@ struct sessiond_config { int verbose_consumer; /* Agent TCP port range for registration. Used by the agent thread. */ struct config_int_range agent_tcp_port; + + int event_notifier_error_counter_bucket; /* Socket timeout for receiving and sending (in seconds). */ int app_socket_timeout; diff --git a/src/bin/lttng-sessiond/testpoint.h b/src/bin/lttng-sessiond/testpoint.h index 8524e1fd3..2ddd66283 100644 --- a/src/bin/lttng-sessiond/testpoint.h +++ b/src/bin/lttng-sessiond/testpoint.h @@ -22,5 +22,7 @@ TESTPOINT_DECL(sessiond_thread_manage_consumer); TESTPOINT_DECL(sessiond_thread_ht_cleanup); TESTPOINT_DECL(sessiond_thread_app_manage_notify); TESTPOINT_DECL(sessiond_thread_app_reg_dispatch); +TESTPOINT_DECL(sessiond_thread_notification); +TESTPOINT_DECL(sessiond_handle_notifier_event_pipe); #endif /* SESSIOND_TESTPOINT_H */ diff --git a/src/bin/lttng-sessiond/trace-kernel.c b/src/bin/lttng-sessiond/trace-kernel.c index 2623b2971..b550ccab5 100644 --- a/src/bin/lttng-sessiond/trace-kernel.c +++ b/src/bin/lttng-sessiond/trace-kernel.c @@ -13,18 +13,23 @@ #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 @@ -35,6 +40,23 @@ #include "lttng-sessiond.h" #include "notification-thread-commands.h" +/* Next available map key. Access under next_map_key_lock. */ +static uint64_t _next_map_key; +static pthread_mutex_t next_map_key_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * Return the incremented value of next_map_key. + */ +static uint64_t get_next_map_key(void) +{ + uint64_t ret; + + pthread_mutex_lock(&next_map_key_lock); + ret = ++_next_map_key; + pthread_mutex_unlock(&next_map_key_lock); + return ret; +} + /* * Find the channel name for the given kernel session. */ @@ -66,10 +88,40 @@ struct ltt_kernel_channel *trace_kernel_get_channel_by_name( } /* - * Find the event for the given channel. + * Find the map name for the given kernel session. + */ +struct ltt_kernel_map *trace_kernel_get_map_by_name( + const char *name, struct ltt_kernel_session *session) +{ + struct ltt_kernel_map *map; + + assert(session); + assert(name); + + DBG("Trying to find map %s", name); + + cds_list_for_each_entry(map, &session->map_list.head, list) { + enum lttng_map_status status; + const char *cur_map_name; + + status = lttng_map_get_name(map->map, &cur_map_name); + assert(status == LTTNG_MAP_STATUS_OK); + + if (strcmp(name, cur_map_name) == 0) { + DBG("Found map by name %s", name); + return map; + } + } + + return NULL; +} + +/* + * Find the event for the given channel or map. */ struct ltt_kernel_event *trace_kernel_find_event( - char *name, struct ltt_kernel_channel *channel, + struct ltt_kernel_event_list *events_list, + uint64_t tracer_token, char *name, enum lttng_event_type type, struct lttng_bytecode *filter) { @@ -77,9 +129,11 @@ struct ltt_kernel_event *trace_kernel_find_event( int found = 0; assert(name); - assert(channel); - cds_list_for_each_entry(ev, &channel->events_list.head, list) { + cds_list_for_each_entry(ev, &events_list->head, list) { + if (ev->event->token != tracer_token) { + continue; + } if (type != LTTNG_EVENT_ALL && ev->type != type) { continue; } @@ -100,10 +154,10 @@ struct ltt_kernel_event *trace_kernel_find_event( break; } if (found) { - DBG("Found event %s for channel %s", name, - channel->channel->name); + DBG("Kernel event %s found", name); return ev; } else { + DBG("Kernel event %s not found", name); return NULL; } } @@ -160,9 +214,11 @@ struct ltt_kernel_session *trace_kernel_create_session(void) lks->fd = -1; lks->metadata_stream_fd = -1; lks->channel_count = 0; + lks->map_count = 0; lks->stream_count_global = 0; lks->metadata = NULL; CDS_INIT_LIST_HEAD(&lks->channel_list.head); + CDS_INIT_LIST_HEAD(&lks->map_list.head); lks->tracker_pid = process_attr_tracker_create(); if (!lks->tracker_pid) { @@ -274,6 +330,68 @@ error: return NULL; } +struct ltt_kernel_map *trace_kernel_create_map( + const struct lttng_map *map) +{ + struct ltt_kernel_map *kernel_map = NULL; + unsigned int i, number_dimensions; + + kernel_map = zmalloc(sizeof(*kernel_map)); + if (!kernel_map) { + PERROR("ltt_kernel_map zmalloc"); + goto end; + } + + switch (lttng_map_get_boundary_policy(map)) { + case LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW: + kernel_map->counter_conf.arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR; + break; + default: + abort(); + } + + switch (lttng_map_get_bitness(map)) { + case LTTNG_MAP_BITNESS_32BITS: + kernel_map->counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_32; + break; + case LTTNG_MAP_BITNESS_64BITS: + kernel_map->counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_64; + break; + default: + abort(); + } + + kernel_map->counter_conf.coalesce_hits = lttng_map_get_coalesce_hits(map); + + number_dimensions = lttng_map_get_dimension_count(map); + assert(number_dimensions <= LTTNG_KERNEL_COUNTER_DIMENSION_MAX); + + kernel_map->counter_conf.number_dimensions = number_dimensions; + + for (i = 0; i < kernel_map->counter_conf.number_dimensions; i++) { + enum lttng_map_status map_status; + uint64_t dimension_length; + + map_status = lttng_map_get_dimension_length(map, i, + &dimension_length); + assert(map_status == LTTNG_MAP_STATUS_OK); + + kernel_map->counter_conf.dimensions[i].size = dimension_length; + + //FIXME: We need to turn on overflow and underflow + kernel_map->counter_conf.dimensions[i].has_overflow = false; + kernel_map->counter_conf.dimensions[i].has_underflow = false; + } + + kernel_map->fd = -1; + kernel_map->enabled = 1; + kernel_map->key = get_next_map_key(); + + kernel_map->event_counters_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); +end: + return kernel_map; +} + /* * Allocate and init a kernel context object. * @@ -485,6 +603,7 @@ error: enum lttng_error_code trace_kernel_create_event_notifier_rule( struct lttng_trigger *trigger, uint64_t token, + uint64_t error_counter_index, struct ltt_kernel_event_notifier_rule **event_notifier_rule) { enum lttng_error_code ret = LTTNG_OK; @@ -501,9 +620,9 @@ enum lttng_error_code trace_kernel_create_event_notifier_rule( assert(condition); condition_type = lttng_condition_get_type(condition); - assert(condition_type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + assert(condition_type == LTTNG_CONDITION_TYPE_ON_EVENT); - condition_status = lttng_condition_event_rule_get_rule( + condition_status = lttng_condition_on_event_get_rule( condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); assert(event_rule); @@ -522,6 +641,7 @@ enum lttng_error_code trace_kernel_create_event_notifier_rule( local_kernel_token_event_rule->fd = -1; local_kernel_token_event_rule->enabled = 1; local_kernel_token_event_rule->token = token; + local_kernel_token_event_rule->error_counter_index = error_counter_index; /* Get the reference of the event rule. */ lttng_trigger_get(trigger); @@ -538,19 +658,26 @@ error: return ret; } +enum trace_kernel_event_type { + TRACE_KERNEL_EVENT_TYPE_NOTIFIER, + TRACE_KERNEL_EVENT_TYPE_COUNTER, +}; + /* - * Initialize a kernel trigger from an event rule. + * Initialize a kernel event from an event rule. */ -enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( +static +enum lttng_error_code trace_kernel_init_event_from_event_rule( const struct lttng_event_rule *rule, - struct lttng_kernel_event_notifier *kernel_event_notifier) + struct lttng_kernel_event *kernel_event, + enum trace_kernel_event_type event_type) { enum lttng_error_code ret_code; const char *name; int strncpy_ret; switch (lttng_event_rule_get_type(rule)) { - case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: { uint64_t address = 0, offset = 0; const char *symbol_name = NULL; @@ -558,7 +685,7 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( enum lttng_kernel_probe_location_status k_status; enum lttng_event_rule_status status; - status = lttng_event_rule_kprobe_get_location(rule, &location); + status = lttng_event_rule_kernel_probe_get_location(rule, &location); if (status != LTTNG_EVENT_RULE_STATUS_OK) { ret_code = LTTNG_ERR_PROBE_LOCATION_INVAL; goto error; @@ -585,12 +712,68 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( abort(); } - kernel_event_notifier->event.instrumentation = LTTNG_KERNEL_KPROBE; - kernel_event_notifier->event.u.kprobe.addr = address; - kernel_event_notifier->event.u.kprobe.offset = offset; + kernel_event->instrumentation = LTTNG_KERNEL_KPROBE; + kernel_event->u.kprobe.addr = address; + kernel_event->u.kprobe.offset = offset; + if (symbol_name) { + strncpy_ret = lttng_strncpy( + kernel_event->u.kprobe.symbol_name, + symbol_name, LTTNG_KERNEL_SYM_NAME_LEN); + + if (strncpy_ret) { + ret_code = LTTNG_ERR_INVALID; + goto error; + } + } + + kernel_event->u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + + status = lttng_event_rule_kernel_probe_get_event_name(rule, &name); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + ret_code = LTTNG_OK; + break; + } + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + { + uint64_t address = 0, offset = 0; + const char *symbol_name = NULL; + const struct lttng_kernel_function_location *location = NULL; + enum lttng_kernel_function_location_status k_status; + enum lttng_event_rule_status status; + + status = lttng_event_rule_kernel_function_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret_code = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + switch (lttng_kernel_function_location_get_type(location)) { + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS: + { + k_status = lttng_kernel_function_location_address_get_address( + location, &address); + assert(k_status == LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK); + break; + } + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET: + { + k_status = lttng_kernel_function_location_symbol_get_offset( + location, &offset); + assert(k_status == LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK); + symbol_name = lttng_kernel_function_location_symbol_get_name( + location); + break; + } + default: + abort(); + } + + kernel_event->instrumentation = LTTNG_KERNEL_KRETPROBE; + kernel_event->u.kretprobe.addr = address; + kernel_event->u.kretprobe.offset = offset; if (symbol_name) { strncpy_ret = lttng_strncpy( - kernel_event_notifier->event.u.kprobe.symbol_name, + kernel_event->u.kretprobe.symbol_name, symbol_name, LTTNG_KERNEL_SYM_NAME_LEN); if (strncpy_ret) { @@ -599,25 +782,26 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( } } - kernel_event_notifier->event.u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; - status = lttng_event_rule_kprobe_get_name(rule, &name); + kernel_event->u.kretprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + + status = lttng_event_rule_kernel_function_get_event_name(rule, &name); assert(status == LTTNG_EVENT_RULE_STATUS_OK); ret_code = LTTNG_OK; break; } - case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: { const struct lttng_userspace_probe_location* location = NULL; const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; enum lttng_event_rule_status status; - status = lttng_event_rule_uprobe_get_location(rule, &location); + status = lttng_event_rule_userspace_probe_get_location(rule, &location); if (status != LTTNG_EVENT_RULE_STATUS_OK) { ret_code = LTTNG_ERR_PROBE_LOCATION_INVAL; goto error; } - kernel_event_notifier->event.instrumentation = LTTNG_KERNEL_UPROBE; + kernel_event->instrumentation = LTTNG_KERNEL_UPROBE; lookup = lttng_userspace_probe_location_get_lookup_method( location); @@ -633,20 +817,20 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( 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_event_notifier->event.u.uprobe.fd = + kernel_event->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_event_notifier->event.u.uprobe.fd = + kernel_event->u.uprobe.fd = lttng_userspace_probe_location_tracepoint_get_binary_fd(location); break; default: abort(); } - status = lttng_event_rule_uprobe_get_name(rule, &name); + status = lttng_event_rule_userspace_probe_get_event_name(rule, &name); assert(status == LTTNG_EVENT_RULE_STATUS_OK); ret_code = LTTNG_OK; break; @@ -661,7 +845,7 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( assert(domain == LTTNG_DOMAIN_KERNEL); assert(status == LTTNG_EVENT_RULE_STATUS_OK); - kernel_event_notifier->event.instrumentation = + kernel_event->instrumentation = LTTNG_KERNEL_TRACEPOINT; ret_code = LTTNG_OK; @@ -675,24 +859,31 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( assert(status == LTTNG_EVENT_RULE_STATUS_OK); - kernel_event_notifier->event.instrumentation = + kernel_event->instrumentation = LTTNG_KERNEL_SYSCALL; - kernel_event_notifier->event.u.syscall.abi = + kernel_event->u.syscall.abi = LTTNG_KERNEL_SYSCALL_ABI_ALL; - kernel_event_notifier->event.u.syscall.entryexit = - LTTNG_KERNEL_SYSCALL_ENTRY; - kernel_event_notifier->event.u.syscall.match = + kernel_event->u.syscall.match = LTTNG_KERNEL_SYSCALL_MATCH_NAME; + switch (event_type) { + case TRACE_KERNEL_EVENT_TYPE_COUNTER: + kernel_event->u.syscall.entryexit = + LTTNG_KERNEL_SYSCALL_ENTRYEXIT; + break; + case TRACE_KERNEL_EVENT_TYPE_NOTIFIER: + kernel_event->u.syscall.entryexit = + LTTNG_KERNEL_SYSCALL_ENTRY; + break; + } ret_code = LTTNG_OK; break; } - case LTTNG_EVENT_RULE_TYPE_KRETPROBE: default: abort(); break; } - strncpy_ret = lttng_strncpy(kernel_event_notifier->event.name, name, + strncpy_ret = lttng_strncpy(kernel_event->name, name, LTTNG_KERNEL_SYM_NAME_LEN); if (strncpy_ret) { ret_code = LTTNG_ERR_INVALID; @@ -702,6 +893,25 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( error: return ret_code; } + +enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_kernel_event_notifier *kernel_event_notifier) +{ + return trace_kernel_init_event_from_event_rule(rule, + &kernel_event_notifier->event, + TRACE_KERNEL_EVENT_TYPE_NOTIFIER); +} + +enum lttng_error_code trace_kernel_init_event_counter_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_kernel_counter_event *kernel_counter_event) +{ + return trace_kernel_init_event_from_event_rule(rule, + &kernel_counter_event->event, + TRACE_KERNEL_EVENT_TYPE_COUNTER); +} + /* * Allocate and initialize a kernel metadata. * @@ -857,6 +1067,41 @@ void trace_kernel_destroy_event(struct ltt_kernel_event *event) free(event); } +/* + * Free kernel event counter structure from RCU context + */ +static void free_event_counter_rcu(struct rcu_head *rcu_node) +{ + struct ltt_kernel_event_counter *event_counter = caa_container_of(rcu_node, + struct ltt_kernel_event_counter, rcu_node); + + free(event_counter); +} +/* + * Cleanup kernel event counter structure. + */ +void trace_kernel_destroy_event_counter(struct ltt_kernel_event_counter *event_counter) +{ + assert(event_counter); + + if (event_counter->fd >= 0) { + int ret; + + DBG("[trace] Closing event counter fd %d", event_counter->fd); + /* Close kernel fd */ + ret = close(event_counter->fd); + if (ret) { + PERROR("close"); + } + } else { + DBG("[trace] Tearing down event counter (no associated file descriptor)"); + } + + lttng_map_key_put(event_counter->key); + call_rcu(&event_counter->rcu_node, free_event_counter_rcu); +} + + /* * Cleanup kernel event structure. */ @@ -954,6 +1199,38 @@ void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel) free(channel); } +/* + * Cleanup kernel map structure. + */ +void trace_kernel_destroy_map(struct ltt_kernel_map *map) +{ + int ret; + struct ltt_kernel_event_counter *event_counter; + struct lttng_ht_iter iter; + + assert(map); + + DBG("[trace] Closing map fd %d", map->fd); + /* Close kernel fd */ + if (map->fd >= 0) { + ret = close(map->fd); + if (ret) { + PERROR("close"); + } + } + + /* For each event counter in the map hashtable */ + cds_lfht_for_each_entry(map->event_counters_ht->ht, &iter.iter, + event_counter, ht_node.node) { + trace_kernel_destroy_event_counter(event_counter); + } + + /* Remove from map list */ + cds_list_del(&map->list); + + free(map); +} + /* * Cleanup kernel metadata structure. */ @@ -984,6 +1261,7 @@ void trace_kernel_destroy_metadata(struct ltt_kernel_metadata *metadata) void trace_kernel_destroy_session(struct ltt_kernel_session *session) { struct ltt_kernel_channel *channel, *ctmp; + struct ltt_kernel_map *map, *map_tmp; int ret; assert(session); @@ -1012,6 +1290,10 @@ void trace_kernel_destroy_session(struct ltt_kernel_session *session) cds_list_for_each_entry_safe(channel, ctmp, &session->channel_list.head, list) { trace_kernel_destroy_channel(channel); } + + cds_list_for_each_entry_safe(map, map_tmp, &session->map_list.head, list) { + trace_kernel_destroy_map(map); + } } /* Free elements needed by destroy notifiers. */ diff --git a/src/bin/lttng-sessiond/trace-kernel.h b/src/bin/lttng-sessiond/trace-kernel.h index 89a4ab19f..35e054f91 100644 --- a/src/bin/lttng-sessiond/trace-kernel.h +++ b/src/bin/lttng-sessiond/trace-kernel.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,11 @@ struct ltt_kernel_channel_list { struct cds_list_head head; }; +/* map list */ +struct ltt_kernel_map_list { + struct cds_list_head head; +}; + struct ltt_kernel_context { struct lttng_kernel_context ctx; struct cds_list_head list; @@ -52,9 +58,10 @@ struct ltt_kernel_event { struct lttng_userspace_probe_location *userspace_probe_location; }; -/* Kernel event */ +/* Kernel event notifier */ struct ltt_kernel_event_notifier_rule { int fd; + uint64_t error_counter_index; int enabled; enum lttng_event_type type; struct lttng_trigger *trigger; @@ -66,6 +73,24 @@ struct ltt_kernel_event_notifier_rule { struct rcu_head rcu_node; }; +/* Kernel event counter */ +struct ltt_kernel_event_counter { + int fd; + int enabled; + uint64_t action_tracer_token; + struct lttng_kernel_event *event; + struct lttng_event_rule *event_rule; + const struct lttng_bytecode *filter; + struct lttng_userspace_probe_location *userspace_probe_location; + struct cds_list_head list; + struct lttng_ht_node_u64 ht_node; + + /* refcounted */ + struct lttng_map_key *key; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; +}; + /* Kernel channel */ struct ltt_kernel_channel { int fd; @@ -84,6 +109,20 @@ struct ltt_kernel_channel { bool sent_to_consumer; }; +/* Kernel map */ +struct ltt_kernel_map { + int fd; + uint64_t key; /* Key to reference this map with the notification thread. */ + int enabled; + unsigned int event_count; + struct lttng_map *map; + struct lttng_kernel_counter_conf counter_conf; + struct lttng_ht *event_counters_ht; + struct cds_list_head list; + /* Session pointer which has a reference to this object. */ + struct ltt_kernel_session *session; +}; + /* Metadata */ struct ltt_kernel_metadata { int fd; @@ -110,9 +149,11 @@ struct ltt_kernel_session { int metadata_stream_fd; int consumer_fds_sent; unsigned int channel_count; + unsigned int map_count; unsigned int stream_count_global; struct ltt_kernel_metadata *metadata; struct ltt_kernel_channel_list channel_list; + struct ltt_kernel_map_list map_list; /* UID/GID of the user owning the session */ uid_t uid; gid_t gid; @@ -144,18 +185,23 @@ struct ltt_kernel_event *trace_kernel_get_event_by_name( char *name, struct ltt_kernel_channel *channel, enum lttng_event_type type); struct ltt_kernel_event *trace_kernel_find_event( - char *name, struct ltt_kernel_channel *channel, + struct ltt_kernel_event_list *events_list, + uint64_t tracer_token, char *name, enum lttng_event_type type, struct lttng_bytecode *filter); struct ltt_kernel_channel *trace_kernel_get_channel_by_name( const char *name, struct ltt_kernel_session *session); +struct ltt_kernel_map *trace_kernel_get_map_by_name( + const char *name, struct ltt_kernel_session *session); + /* * Create functions malloc() the data structure. */ struct ltt_kernel_session *trace_kernel_create_session(void); struct ltt_kernel_channel *trace_kernel_create_channel( struct lttng_channel *chan); +struct ltt_kernel_map *trace_kernel_create_map(const struct lttng_map *map); enum lttng_error_code trace_kernel_create_event(struct lttng_event *ev, char *filter_expression, struct lttng_bytecode *filter, struct ltt_kernel_event **kernel_event); @@ -167,12 +213,16 @@ struct ltt_kernel_context *trace_kernel_create_context( enum lttng_error_code trace_kernel_create_event_notifier_rule( struct lttng_trigger *trigger, uint64_t token, + uint64_t error_counter_index, struct ltt_kernel_event_notifier_rule **event_notifier_rule); struct ltt_kernel_context *trace_kernel_copy_context( struct ltt_kernel_context *ctx); enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( const struct lttng_event_rule *rule, struct lttng_kernel_event_notifier *kernel_event_notifier); +enum lttng_error_code trace_kernel_init_event_counter_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_kernel_counter_event *kernel_counter_event); /* * Destroy functions free() the data structure and remove from linked list if @@ -181,10 +231,13 @@ enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( void trace_kernel_destroy_session(struct ltt_kernel_session *session); void trace_kernel_destroy_metadata(struct ltt_kernel_metadata *metadata); void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel); +void trace_kernel_destroy_map(struct ltt_kernel_map *map); void trace_kernel_destroy_event(struct ltt_kernel_event *event); void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream); void trace_kernel_destroy_context(struct ltt_kernel_context *ctx); void trace_kernel_destroy_event_notifier_rule(struct ltt_kernel_event_notifier_rule *rule); +void trace_kernel_destroy_event_counter( + struct ltt_kernel_event_counter *event_counter); 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 527b354f1..3df0e65dc 100644 --- a/src/bin/lttng-sessiond/trace-ust.c +++ b/src/bin/lttng-sessiond/trace-ust.c @@ -18,7 +18,11 @@ #include #include +#include +#include + #include "buffer-registry.h" +#include "map.h" #include "trace-ust.h" #include "utils.h" #include "ust-app.h" @@ -73,7 +77,14 @@ int trace_ust_ht_match_event(struct cds_lfht_node *node, const void *_key) key = _key; ev_loglevel_value = event->attr.loglevel; - /* Match the 4 elements of the key: name, filter, loglevel, exclusions. */ + /* Match the 6 elements of the key: tracer_token, map_key, name, filter, loglevel, exclusions. */ + if (event->attr.token != key->tracer_token) { + goto no_match; + } + + if (!lttng_map_key_is_equal(event->key, key->key)) { + goto no_match; + } /* Event name */ if (strncmp(event->attr.name, key->name, sizeof(event->attr.name)) != 0) { @@ -189,14 +200,44 @@ error: return NULL; } +/* + * Find the map in the hashtable and return map pointer. RCU read side + * lock MUST be acquired before calling this. + */ +struct ltt_ust_map *trace_ust_find_map_by_name(struct lttng_ht *ht, + const char *name) +{ + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + + if (name[0] == '\0') { + goto error; + } + + lttng_ht_lookup(ht, (void *)name, &iter); + node = lttng_ht_iter_get_node_str(&iter); + if (node == NULL) { + goto error; + } + + DBG2("Trace UST map %s found by name", name); + + return caa_container_of(node, struct ltt_ust_map, node); + +error: + DBG2("Trace UST map %s not found by name", name); + return NULL; +} + /* * Find the event in the hashtable and return event pointer. RCU read side lock * MUST be acquired before calling this. */ struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, - char *name, struct lttng_bytecode *filter, + uint64_t tracer_token, char *name, struct lttng_bytecode *filter, enum lttng_ust_loglevel_type loglevel_type, int loglevel_value, - struct lttng_event_exclusion *exclusion) + struct lttng_event_exclusion *exclusion, + struct lttng_map_key *map_key) { struct lttng_ht_node_str *node; struct lttng_ht_iter iter; @@ -205,11 +246,13 @@ struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, assert(name); assert(ht); + key.tracer_token = tracer_token; key.name = name; key.filter = filter; key.loglevel_type = loglevel_type; key.loglevel_value = loglevel_value; key.exclusion = exclusion; + key.key = map_key; cds_lfht_lookup(ht->ht, ht->hash_fct((void *) name, lttng_ht_seed), trace_ust_ht_match_event, &key, &iter.iter); @@ -301,6 +344,8 @@ struct ltt_ust_session *trace_ust_create_session(uint64_t session_id) /* Alloc UST global domain channels' HT */ lus->domain_global.channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + /* Alloc UST global domain maps' HT */ + lus->domain_global.maps = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); /* Alloc agent hash table. */ lus->agents = lttng_ht_new(0, LTTNG_HT_TYPE_U64); @@ -330,6 +375,7 @@ error: process_attr_tracker_destroy(lus->tracker_vuid); process_attr_tracker_destroy(lus->tracker_vgid); ht_cleanup_push(lus->domain_global.channels); + ht_cleanup_push(lus->domain_global.maps); ht_cleanup_push(lus->agents); free(lus); error_alloc: @@ -405,6 +451,67 @@ error: return luc; } +/* + * Allocate and initialize a ust map data structure. + * + * Return pointer to structure or NULL. + */ +struct ltt_ust_map *trace_ust_create_map(const struct lttng_map *map) +{ + struct ltt_ust_map *umap = NULL; + enum lttng_map_status map_status; + const char *map_name = NULL; + unsigned int dimension_count; + uint64_t dimension_len; + + umap = zmalloc(sizeof(*umap)); + if (!umap) { + PERROR("ltt_ust_map zmalloc"); + goto end; + } + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Can't get map name"); + umap = NULL; + goto end; + } + + dimension_count = lttng_map_get_dimension_count(map); + assert(dimension_count == 1); + + map_status = lttng_map_get_dimension_length(map, 0, &dimension_len); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Can't get map first dimension length"); + umap = NULL; + goto end; + } + assert(dimension_len > 0); + + strncpy(umap->name, map_name, sizeof(umap->name)); + + umap->enabled = 1; + umap->bucket_count = dimension_len; + umap->coalesce_hits = lttng_map_get_coalesce_hits(map); + umap->bitness = lttng_map_get_bitness(map); + umap->nr_cpu = ustctl_get_nr_cpu_per_counter(); + + umap->dead_app_kv_values.dead_app_kv_values_32bits = lttng_ht_new(0, + LTTNG_HT_TYPE_STRING); + umap->dead_app_kv_values.dead_app_kv_values_64bits = lttng_ht_new(0, + LTTNG_HT_TYPE_STRING); + pthread_mutex_init(&umap->dead_app_kv_values.lock, NULL); + + umap->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + + /* Init node */ + lttng_ht_node_init_str(&umap->node, umap->name); + + DBG2("Trace UST map %s created", umap->name); +end: + return umap; +} + /* * Validates an exclusion list. * @@ -444,7 +551,12 @@ end: * * Return an lttng_error_code */ -enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, +enum lttng_error_code trace_ust_create_event(uint64_t tracer_token, + const char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + enum lttng_loglevel ev_loglevel, char *filter_expression, struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, @@ -454,8 +566,6 @@ enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, struct ltt_ust_event *local_ust_event; enum lttng_error_code ret = LTTNG_OK; - assert(ev); - if (exclusion && validate_exclusion(exclusion)) { ret = LTTNG_ERR_INVALID; goto error; @@ -469,8 +579,9 @@ enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, } local_ust_event->internal = internal_event; + local_ust_event->attr.token = tracer_token; - switch (ev->type) { + switch (ev_type) { case LTTNG_EVENT_PROBE: local_ust_event->attr.instrumentation = LTTNG_UST_PROBE; break; @@ -484,44 +595,53 @@ enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, local_ust_event->attr.instrumentation = LTTNG_UST_TRACEPOINT; break; default: - ERR("Unknown ust instrumentation type (%d)", ev->type); + ERR("Unknown ust instrumentation type (%d)", ev_type); ret = LTTNG_ERR_INVALID; goto error_free_event; } /* Copy event name */ - strncpy(local_ust_event->attr.name, ev->name, LTTNG_UST_SYM_NAME_LEN); + strncpy(local_ust_event->attr.name, ev_name, LTTNG_UST_SYM_NAME_LEN); local_ust_event->attr.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; - switch (ev->loglevel_type) { + switch (ev_loglevel_type) { case LTTNG_EVENT_LOGLEVEL_ALL: local_ust_event->attr.loglevel_type = LTTNG_UST_LOGLEVEL_ALL; local_ust_event->attr.loglevel = -1; /* Force to -1 */ break; case LTTNG_EVENT_LOGLEVEL_RANGE: local_ust_event->attr.loglevel_type = LTTNG_UST_LOGLEVEL_RANGE; - local_ust_event->attr.loglevel = ev->loglevel; + local_ust_event->attr.loglevel = ev_loglevel; break; case LTTNG_EVENT_LOGLEVEL_SINGLE: local_ust_event->attr.loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE; - local_ust_event->attr.loglevel = ev->loglevel; + local_ust_event->attr.loglevel = ev_loglevel; break; default: - ERR("Unknown ust loglevel type (%d)", ev->loglevel_type); + ERR("Unknown ust loglevel type (%d)", ev_loglevel_type); ret = LTTNG_ERR_INVALID; goto error_free_event; } /* Same layout. */ + local_ust_event->key = key; local_ust_event->filter_expression = filter_expression; local_ust_event->filter = filter; local_ust_event->exclusion = exclusion; + /* Take a reference on the lttng_map_key to bounds its lifetime to the + * ust_event. + */ + if (key) { + lttng_map_key_get(key); + } + /* Init node */ lttng_ht_node_init_str(&local_ust_event->node, local_ust_event->attr.name); - DBG2("Trace UST event %s, loglevel (%d,%d) created", - local_ust_event->attr.name, local_ust_event->attr.loglevel_type, + DBG2("Trace UST event %s, tracer token %"PRIu64", loglevel (%d,%d) created", + local_ust_event->attr.name, local_ust_event->attr.token, + local_ust_event->attr.loglevel_type, local_ust_event->attr.loglevel); *ust_event = local_ust_event; @@ -1242,6 +1362,7 @@ void trace_ust_destroy_event(struct ltt_ust_event *event) assert(event); DBG2("Trace destroy UST event %s", event->attr.name); + lttng_map_key_put(event->key); free(event->filter_expression); free(event->filter); free(event->exclusion); @@ -1311,6 +1432,53 @@ static void _trace_ust_destroy_channel(struct ltt_ust_channel *channel) free(channel); } +/* + * Cleanup ust map structure. + * + * Should _NOT_ be called with RCU read lock held. + */ +static void _trace_ust_destroy_map(struct ltt_ust_map *map) +{ + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_iter ht_iter; + struct lttng_ht *dead_app_kv_ht; + + assert(map); + + DBG2("Trace destroy UST map %s", map->name); + + /* + * Remove all the keys before destroying the hashtables. + */ + dead_app_kv_ht = map->dead_app_kv_values.dead_app_kv_values_32bits; + cds_lfht_for_each_entry(dead_app_kv_ht->ht, &ht_iter.iter, kv_entry, node.node) { + struct lttng_ht_iter entry_iter; + + entry_iter.iter.node = &kv_entry->node.node; + lttng_ht_del(dead_app_kv_ht, &entry_iter); + + free(kv_entry->key); + free(kv_entry); + } + lttng_ht_destroy(map->dead_app_kv_values.dead_app_kv_values_32bits); + + dead_app_kv_ht = map->dead_app_kv_values.dead_app_kv_values_64bits; + cds_lfht_for_each_entry(dead_app_kv_ht->ht, &ht_iter.iter, kv_entry, node.node) { + struct lttng_ht_iter entry_iter; + + entry_iter.iter.node = &kv_entry->node.node; + lttng_ht_del(dead_app_kv_ht, &entry_iter); + + free(kv_entry->key); + free(kv_entry); + } + + lttng_ht_destroy(map->dead_app_kv_values.dead_app_kv_values_64bits); + + lttng_map_put(map->map); + free(map); +} + /* * URCU intermediate call to complete destroy channel. */ @@ -1324,6 +1492,19 @@ static void destroy_channel_rcu(struct rcu_head *head) _trace_ust_destroy_channel(channel); } +/* + * URCU intermediate call to complete destroy map. + */ +static void destroy_map_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_str *node = + caa_container_of(head, struct lttng_ht_node_str, head); + struct ltt_ust_map *map = + caa_container_of(node, struct ltt_ust_map, node); + + _trace_ust_destroy_map(map); +} + void trace_ust_destroy_channel(struct ltt_ust_channel *channel) { /* Destroying all events of the channel */ @@ -1334,6 +1515,14 @@ void trace_ust_destroy_channel(struct ltt_ust_channel *channel) call_rcu(&channel->node.head, destroy_channel_rcu); } +void trace_ust_destroy_map(struct ltt_ust_map *map) +{ + /* Destroying all events of the map */ + destroy_events(map->events); + + call_rcu(&map->node.head, destroy_map_rcu); +} + /* * Remove an UST channel from a channel HT. */ @@ -1351,6 +1540,23 @@ void trace_ust_delete_channel(struct lttng_ht *ht, assert(!ret); } +/* + * Remove an UST map from a map HT. + */ +void trace_ust_delete_map(struct lttng_ht *ht, + struct ltt_ust_map *map) +{ + int ret; + struct lttng_ht_iter iter; + + assert(ht); + assert(map); + + iter.iter.node = &map->node.node; + ret = lttng_ht_del(ht, &iter); + assert(!ret); +} + /* * Iterate over a hash table containing channels and cleanup safely. */ @@ -1374,6 +1580,28 @@ static void destroy_channels(struct lttng_ht *channels) ht_cleanup_push(channels); } +/* + * Iterate over a hash table containing maps and cleanup safely. + */ +static void destroy_maps(struct lttng_ht *maps) +{ + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + + assert(maps); + + rcu_read_lock(); + cds_lfht_for_each_entry(maps->ht, &iter.iter, node, node) { + struct ltt_ust_map *map = + caa_container_of(node, struct ltt_ust_map, node); + + trace_ust_delete_map(maps, map); + trace_ust_destroy_map(map); + } + rcu_read_unlock(); + + ht_cleanup_push(maps); +} /* * Cleanup UST global domain. */ @@ -1382,6 +1610,7 @@ static void destroy_domain_global(struct ltt_ust_domain_global *dom) assert(dom); destroy_channels(dom->channels); + destroy_maps(dom->maps); } /* diff --git a/src/bin/lttng-sessiond/trace-ust.h b/src/bin/lttng-sessiond/trace-ust.h index 53e1ab2b5..b254ad05d 100644 --- a/src/bin/lttng-sessiond/trace-ust.h +++ b/src/bin/lttng-sessiond/trace-ust.h @@ -23,11 +23,13 @@ struct agent; struct ltt_ust_ht_key { + uint64_t tracer_token; const char *name; const struct lttng_bytecode *filter; enum lttng_ust_loglevel_type loglevel_type; int loglevel_value; const struct lttng_event_exclusion *exclusion; + struct lttng_map_key *key; }; /* Context hash table nodes */ @@ -45,6 +47,9 @@ struct ltt_ust_event { char *filter_expression; struct lttng_bytecode *filter; struct lttng_event_exclusion *exclusion; + + /* refcounted */ + struct lttng_map_key *key; /* * An internal event is an event which was created by the session daemon * through which, for example, events emitted in Agent domains are @@ -76,9 +81,31 @@ struct ltt_ust_channel { uint64_t monitor_timer_interval; }; +struct ltt_ust_map_dead_pid_kv_values { + pthread_mutex_t lock; + struct lttng_ht *dead_app_kv_values_32bits; + struct lttng_ht *dead_app_kv_values_64bits; +}; + +/* UST map */ +struct ltt_ust_map { + uint64_t id; /* unique id per session. */ + char name[LTTNG_UST_SYM_NAME_LEN]; + unsigned int enabled; + size_t bucket_count; + bool coalesce_hits; + enum lttng_map_bitness bitness; + uint64_t nr_cpu; + struct lttng_ht_node_str node; + struct lttng_map *map; + struct lttng_ht *events; + struct ltt_ust_map_dead_pid_kv_values dead_app_kv_values; +}; + /* UST domain global (LTTNG_DOMAIN_UST) */ struct ltt_ust_domain_global { struct lttng_ht *channels; + struct lttng_ht *maps; struct cds_list_head registry_buffer_uid_list; }; @@ -182,11 +209,14 @@ int trace_ust_ht_match_event_by_name(struct cds_lfht_node *node, * Lookup functions. NULL is returned if not found. */ struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, - char *name, struct lttng_bytecode *filter, + uint64_t tracer_token, char *name, struct lttng_bytecode *filter, enum lttng_ust_loglevel_type loglevel_type, int loglevel_value, - struct lttng_event_exclusion *exclusion); + struct lttng_event_exclusion *exclusion, + struct lttng_map_key *key); struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht, const char *name); +struct ltt_ust_map *trace_ust_find_map_by_name(struct lttng_ht *ht, + const char *name); struct agent *trace_ust_find_agent(struct ltt_ust_session *session, enum lttng_domain_type domain_type); @@ -196,17 +226,26 @@ struct agent *trace_ust_find_agent(struct ltt_ust_session *session, struct ltt_ust_session *trace_ust_create_session(uint64_t session_id); struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, enum lttng_domain_type domain); -enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, +struct ltt_ust_map *trace_ust_create_map(const struct lttng_map *map); +enum lttng_error_code trace_ust_create_event(uint64_t tracer_token, + const char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + enum lttng_loglevel ev_loglevel, char *filter_expression, struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, - bool internal_event, struct ltt_ust_event **ust_event); + bool internal_event, + struct ltt_ust_event **ust_event); struct ltt_ust_context *trace_ust_create_context( const struct lttng_event_context *ctx); int trace_ust_match_context(const struct ltt_ust_context *uctx, const struct lttng_event_context *ctx); void trace_ust_delete_channel(struct lttng_ht *ht, struct ltt_ust_channel *channel); +void trace_ust_delete_map(struct lttng_ht *ht, + struct ltt_ust_map *map); /* * Destroy functions free() the data structure and remove from linked list if @@ -214,6 +253,7 @@ void trace_ust_delete_channel(struct lttng_ht *ht, */ void trace_ust_destroy_session(struct ltt_ust_session *session); void trace_ust_destroy_channel(struct ltt_ust_channel *channel); +void trace_ust_destroy_map(struct ltt_ust_map *map); void trace_ust_destroy_event(struct ltt_ust_event *event); void trace_ust_destroy_context(struct ltt_ust_context *ctx); void trace_ust_free_session(struct ltt_ust_session *session); @@ -255,7 +295,12 @@ struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht, { return NULL; } - +static inline +struct ltt_ust_map *trace_ust_find_map_by_name(struct lttng_ht *ht, + const char *name) +{ + return NULL; +} static inline struct ltt_ust_session *trace_ust_create_session(unsigned int session_id) { @@ -268,11 +313,22 @@ struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, return NULL; } static inline -enum lttng_error_code trace_ust_create_event(struct lttng_event *ev, - const char *filter_expression, +struct ltt_ust_map *trace_ust_create_map(const struct lttng_map *map) +{ + return NULL; +} +static inline +enum lttng_error_code trace_ust_create_event(uint64_t tracer_token, + const char *ev_name, + struct lttng_map_key *key, + enum lttng_event_type ev_type, + enum lttng_loglevel_type ev_loglevel_type, + enum lttng_loglevel ev_loglevel, + char *filter_expression, struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, - bool internal_event, struct ltt_ust_event **ust_event) + bool internal_event, + struct ltt_ust_event **ust_event) { return LTTNG_ERR_NO_UST; } @@ -286,6 +342,11 @@ void trace_ust_destroy_channel(struct ltt_ust_channel *channel) { } +static inline +void trace_ust_destroy_map(struct ltt_ust_map *map) +{ +} + static inline void trace_ust_destroy_event(struct ltt_ust_event *event) { @@ -310,9 +371,10 @@ int trace_ust_match_context(const struct ltt_ust_context *uctx, } static inline struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht, - char *name, struct lttng_bytecode *filter, + uint64_t tracer_token, char *name, struct lttng_bytecode *filter, enum lttng_ust_loglevel_type loglevel_type, int loglevel_value, - struct lttng_event_exclusion *exclusion) + struct lttng_event_exclusion *exclusion, + struct lttng_map_key *key) { return NULL; } diff --git a/src/bin/lttng-sessiond/ust-abi-internal.h b/src/bin/lttng-sessiond/ust-abi-internal.h index 0f12be083..413302c32 100644 --- a/src/bin/lttng-sessiond/ust-abi-internal.h +++ b/src/bin/lttng-sessiond/ust-abi-internal.h @@ -95,6 +95,7 @@ struct lttng_ust_event { enum lttng_ust_loglevel_type loglevel_type; int loglevel; /* value, -1: all */ + uint64_t token; char padding[LTTNG_UST_EVENT_PADDING1]; /* Per instrumentation type configuration */ @@ -109,9 +110,10 @@ struct lttng_ust_event_notifier { char padding[LTTNG_UST_EVENT_NOTIFIER_PADDING]; } LTTNG_PACKED; -#define LTTNG_UST_EVENT_NOTIFIER_NOTIFICATION_PADDING 34 +#define LTTNG_UST_EVENT_NOTIFIER_NOTIFICATION_PADDING 32 struct lttng_ust_event_notifier_notification { uint64_t token; + uint16_t capture_buf_size; char padding[LTTNG_UST_EVENT_NOTIFIER_NOTIFICATION_PADDING]; } LTTNG_PACKED; diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index 43864bd0f..839a71437 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -7,11 +7,14 @@ */ #define _LGPL_SOURCE +#include +#include #include #include #include #include #include +#include #include #include #include @@ -22,16 +25,24 @@ #include #include #include +#include +#include #include #include #include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include "buffer-registry.h" +#include "condition-internal.h" #include "fd-limit.h" #include "health-sessiond.h" #include "ust-app.h" @@ -44,6 +55,9 @@ #include "notification-thread-commands.h" #include "rotate.h" #include "event.h" +#include "event-notifier-error-accounting.h" +#include "map.h" + struct lttng_ht *ust_app_ht; struct lttng_ht *ust_app_ht_by_sock; @@ -56,6 +70,10 @@ int ust_app_flush_app_session(struct ust_app *app, struct ust_app_session *ua_se static uint64_t _next_channel_key; static pthread_mutex_t next_channel_key_lock = PTHREAD_MUTEX_INITIALIZER; +/* Next available map key. Access under next_map_key_lock. */ +static uint64_t _next_map_key; +static pthread_mutex_t next_map_key_lock = PTHREAD_MUTEX_INITIALIZER; + /* Next available session ID. Access under next_session_id_lock. */ static uint64_t _next_session_id; static pthread_mutex_t next_session_id_lock = PTHREAD_MUTEX_INITIALIZER; @@ -73,6 +91,19 @@ static uint64_t get_next_channel_key(void) return ret; } +/* + * Return the incremented value of next_map_key. + */ +static uint64_t get_next_map_key(void) +{ + uint64_t ret; + + pthread_mutex_lock(&next_map_key_lock); + ret = ++_next_map_key; + pthread_mutex_unlock(&next_map_key_lock); + return ret; +} + /* * Return the atomically incremented value of next_session_id. */ @@ -119,7 +150,14 @@ static int ht_match_ust_app_event(struct cds_lfht_node *node, const void *_key) key = _key; ev_loglevel_value = event->attr.loglevel; - /* Match the 4 elements of the key: name, filter, loglevel, exclusions */ + /* + * Match the 5 elements of the key: + * tracer token, name, filter, loglevel, exclusions + */ + + if (event->attr.token != key->tracer_token) { + goto no_match; + } /* Event name */ if (strncmp(event->attr.name, key->name, sizeof(event->attr.name)) != 0) { @@ -182,25 +220,23 @@ no_match: * Unique add of an ust app event in the given ht. This uses the custom * ht_match_ust_app_event match function and the event name as hash. */ -static void add_unique_ust_app_event(struct ust_app_channel *ua_chan, +static void add_unique_ust_app_event(struct lttng_ht *events_ht, struct ust_app_event *event) { struct cds_lfht_node *node_ptr; struct ust_app_ht_key key; - struct lttng_ht *ht; - assert(ua_chan); - assert(ua_chan->events); + assert(events_ht); assert(event); - ht = ua_chan->events; key.name = event->attr.name; key.filter = event->filter; key.loglevel_type = event->attr.loglevel; key.exclusion = event->exclusion; + key.tracer_token = event->attr.token; - node_ptr = cds_lfht_add_unique(ht->ht, - ht->hash_fct(event->node.key, lttng_ht_seed), + node_ptr = cds_lfht_add_unique(events_ht->ht, + events_ht->hash_fct(event->node.key, lttng_ht_seed), ht_match_ust_app_event, &key, &event->node.node); assert(node_ptr == &event->node.node); } @@ -395,6 +431,33 @@ static int release_ust_app_stream(int sock, struct ust_app_stream *stream, return ret; } +/* + * Release ust data object of the given map_counter. + * + * Return 0 on success or else a negative value. + */ +static int release_ust_app_map_counter(int sock, struct ust_app_map_counter *map_counter, + struct ust_app *app) +{ + int ret = 0; + + assert(map_counter); + + if (map_counter->obj) { + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_release_object(sock, map_counter->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app sock %d release map_counter obj failed with ret %d", + sock, ret); + } + lttng_fd_put(LTTNG_FD_APPS, 2); + free(map_counter->obj); + } + + return ret; +} + /* * Delete ust app stream safely. RCU read lock must be held before calling * this function. @@ -409,6 +472,20 @@ void delete_ust_app_stream(int sock, struct ust_app_stream *stream, free(stream); } +/* + * Delete ust app map_counter safely. RCU read lock must be held before calling + * this function. + */ +static +void delete_ust_app_map_counter(int sock, struct ust_app_map_counter *map_counter, + struct ust_app *app) +{ + assert(map_counter); + + (void) release_ust_app_map_counter(sock, map_counter, app); + free(map_counter); +} + /* * We need to execute ht_destroy outside of RCU read-side critical * section and outside of call_rcu thread, so we postpone its execution @@ -426,6 +503,22 @@ void delete_ust_app_channel_rcu(struct rcu_head *head) free(ua_chan); } +/* + * We need to execute ht_destroy outside of RCU read-side critical + * section and outside of call_rcu thread, so we postpone its execution + * using ht_cleanup_push. It is simpler than to change the semantic of + * the many callers of delete_ust_app_session(). + */ +static +void delete_ust_app_map_rcu(struct rcu_head *head) +{ + struct ust_app_map *ua_map = + caa_container_of(head, struct ust_app_map, rcu_head); + + ht_cleanup_push(ua_map->events); + free(ua_map); +} + /* * Extract the lost packet or discarded events counter when the channel is * being deleted and store the value in the parent channel so we can @@ -555,7 +648,7 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan, if (ua_chan->obj != NULL) { /* Remove channel from application UST object descriptor. */ iter.iter.node = &ua_chan->ust_objd_node.node; - ret = lttng_ht_del(app->ust_objd, &iter); + ret = lttng_ht_del(app->ust_chan_objd, &iter); assert(!ret); pthread_mutex_lock(&app->sock_lock); ret = ustctl_release_object(sock, ua_chan->obj); @@ -570,6 +663,123 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan, call_rcu(&ua_chan->rcu_head, delete_ust_app_channel_rcu); } +static +void copy_ust_app_map_values(int sock, struct ust_app_map *ua_map, + struct ust_app *app) +{ + struct ltt_ust_map_dead_pid_kv_values *kv_pair_list = ua_map->dead_app_kv_values; + struct ust_registry_session *ust_reg_sess; + struct ust_registry_map *ust_reg_map; + struct ust_registry_map_index_ht_entry *map_index_entry; + struct lttng_ht_iter key_iter; + struct lttng_ht *dead_app_kv_values; + + assert(app->buffer_type == LTTNG_BUFFER_PER_PID); + ust_reg_sess = get_session_registry(ua_map->session); + + pthread_mutex_lock(&ust_reg_sess->lock); + ust_reg_map = ust_registry_map_find(ust_reg_sess, ua_map->key); + pthread_mutex_unlock(&ust_reg_sess->lock); + assert(ust_reg_map); + + DBG("Aggregating dead map values"); + + pthread_mutex_lock(&kv_pair_list->lock); + + if (app->bits_per_long == 32) { + dead_app_kv_values = kv_pair_list->dead_app_kv_values_32bits; + } else { + dead_app_kv_values = kv_pair_list->dead_app_kv_values_64bits; + } + + /* Iterate over all the formated_key -> counter index */ + cds_lfht_for_each_entry(ust_reg_map->key_string_to_bucket_index_ht->ht, + &key_iter.iter, map_index_entry, node.node) { + bool overflow = 0, underflow = 0; + int64_t local_value = 0; + int ret; + size_t dimension_indexes[1] = {map_index_entry->index}; + + ret = ustctl_counter_aggregate(ua_map->map_handle, + dimension_indexes, &local_value, &overflow, + &underflow); + if (ret) { + ERR("Error getting counter value from the tracer: key = '%s'", + map_index_entry->formated_key); + ret = -1; + goto end; + } + + map_add_or_increment_map_values(dead_app_kv_values, + map_index_entry->formated_key, local_value, + underflow, overflow); + + } + +end: + pthread_mutex_unlock(&kv_pair_list->lock); + return; +} +/* + * Delete ust app map safely. RCU read lock must be held before calling + * this function. + * + * The session list lock must be held by the caller. + */ +static +void delete_ust_app_map(int sock, struct ust_app_map *ua_map, + struct ust_app *app) +{ + int ret; + struct lttng_ht_iter iter; + struct ust_app_event *ua_event; + struct ust_app_map_counter *map_counter, *ctmp; + struct ust_registry_session *registry; + + assert(ua_map); + + DBG3("UST app deleting map %s", ua_map->name); + + /* Wipe stream */ + cds_list_for_each_entry_safe(map_counter, ctmp, &ua_map->counters.head, list) { + cds_list_del(&map_counter->list); + delete_ust_app_map_counter(sock, map_counter, app); + } + + /* Wipe events */ + cds_lfht_for_each_entry(ua_map->events->ht, &iter.iter, ua_event, + node.node) { + ret = lttng_ht_del(ua_map->events, &iter); + assert(!ret); + delete_ust_app_event(sock, ua_event, app); + } + + if (ua_map->session->buffer_type == LTTNG_BUFFER_PER_PID) { + /* Wipe and free registry from session registry. */ + registry = get_session_registry(ua_map->session); + if (registry) { + ust_registry_map_del_free(registry, ua_map->key); + } + } + + if (ua_map->obj != NULL) { + /* Remove map from application UST object descriptor. */ + iter.iter.node = &ua_map->ust_objd_node.node; + ret = lttng_ht_del(app->ust_map_objd, &iter); + assert(!ret); + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_release_object(sock, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app sock %d release map obj failed with ret %d", + sock, ret); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + free(ua_map->obj); + } + call_rcu(&ua_map->rcu_head, delete_ust_app_map_rcu); +} + int ust_app_register_done(struct ust_app *app) { int ret; @@ -858,6 +1068,7 @@ void delete_ust_app_session_rcu(struct rcu_head *head) caa_container_of(head, struct ust_app_session, rcu_head); ht_cleanup_push(ua_sess->channels); + ht_cleanup_push(ua_sess->maps); free(ua_sess); } @@ -874,6 +1085,7 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess, int ret; struct lttng_ht_iter iter; struct ust_app_channel *ua_chan; + struct ust_app_map *ua_map; struct ust_registry_session *registry; assert(ua_sess); @@ -908,6 +1120,16 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess, delete_ust_app_channel(sock, ua_chan, app); } + cds_lfht_for_each_entry(ua_sess->maps->ht, &iter.iter, ua_map, + node.node) { + if (ua_sess->buffer_type == LTTNG_BUFFER_PER_PID) { + copy_ust_app_map_values(sock, ua_map, app); + } + ret = lttng_ht_del(ua_sess->maps, &iter); + assert(!ret); + delete_ust_app_map(sock, ua_map, app); + } + /* In case of per PID, the registry is kept in the session. */ if (ua_sess->buffer_type == LTTNG_BUFFER_PER_PID) { struct buffer_reg_pid *reg_pid = buffer_reg_pid_find(ua_sess->id); @@ -990,7 +1212,8 @@ void delete_ust_app(struct ust_app *app) ht_cleanup_push(app->sessions); ht_cleanup_push(app->ust_sessions_objd); - ht_cleanup_push(app->ust_objd); + ht_cleanup_push(app->ust_chan_objd); + ht_cleanup_push(app->ust_map_objd); ht_cleanup_push(app->token_to_event_notifier_rule_ht); /* @@ -999,6 +1222,8 @@ void delete_ust_app(struct ust_app *app) */ if (app->event_notifier_group.object) { enum lttng_error_code ret_code; + enum event_notifier_error_accounting_status status; + const int event_notifier_read_fd = lttng_pipe_get_readfd( app->event_notifier_group.event_pipe); @@ -1009,6 +1234,11 @@ void delete_ust_app(struct ust_app *app) ERR("Failed to remove application tracer event source from notification thread"); } + status = event_notifier_error_accounting_unregister_app(app); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error unregistering app from event notifier error accounting"); + } + ustctl_release_object(sock, app->event_notifier_group.object); free(app->event_notifier_group.object); } @@ -1110,6 +1340,7 @@ struct ust_app_session *alloc_ust_app_session(void) ua_sess->handle = -1; ua_sess->channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); ua_sess->metadata_attr.type = LTTNG_UST_CHAN_METADATA; + ua_sess->maps = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); pthread_mutex_init(&ua_sess->lock, NULL); return ua_sess; @@ -1172,6 +1403,43 @@ error: return NULL; } +/* + * Alloc new UST app map. + */ +static +struct ust_app_map *alloc_ust_app_map(const char *name, + struct ust_app_session *ua_sess) +{ + struct ust_app_map *ua_map; + + /* Init most of the default value by allocating and zeroing */ + ua_map = zmalloc(sizeof(struct ust_app_map)); + if (ua_map == NULL) { + PERROR("malloc"); + goto error; + } + + /* Setup map name */ + strncpy(ua_map->name, name, sizeof(ua_map->name)); + ua_map->name[sizeof(ua_map->name) - 1] = '\0'; + + ua_map->enabled = 1; + ua_map->handle = -1; + ua_map->session = ua_sess; + ua_map->key = get_next_map_key(); + ua_map->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + lttng_ht_node_init_str(&ua_map->node, ua_map->name); + + CDS_INIT_LIST_HEAD(&ua_map->counters.head); + + DBG3("UST app map %s allocated", ua_map->name); + + return ua_map; + +error: + return NULL; +} + /* * Allocate and initialize a UST app stream. * @@ -1194,6 +1462,28 @@ error: return stream; } +/* + * Allocate and initialize a UST app map_counter. + * + * Return newly allocated map_counter pointer or NULL on error. + */ +struct ust_app_map_counter *ust_app_alloc_map_counter(void) +{ + struct ust_app_map_counter *map_counter = NULL; + + map_counter = zmalloc(sizeof(*map_counter)); + if (map_counter == NULL) { + PERROR("zmalloc ust app map_counter"); + goto error; + } + + /* Zero could be a valid value for a handle so flag it to -1. */ + map_counter->handle = -1; + +error: + return map_counter; +} + /* * Alloc new UST app event. */ @@ -1228,6 +1518,7 @@ error: return NULL; } + /* * Allocate a new UST app event notifier rule. */ @@ -1253,9 +1544,9 @@ static struct ust_app_event_notifier_rule *alloc_ust_app_event_notifier_rule( condition = lttng_trigger_get_condition(trigger); assert(condition); - assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); - assert(LTTNG_CONDITION_STATUS_OK == lttng_condition_event_rule_get_rule(condition, &event_rule)); + assert(LTTNG_CONDITION_STATUS_OK == lttng_condition_on_event_get_rule(condition, &event_rule)); assert(event_rule); /* Acquire the event notifier's reference to the trigger. */ @@ -1429,7 +1720,8 @@ error: static struct ust_app_event *find_ust_app_event(struct lttng_ht *ht, const char *name, const struct lttng_bytecode *filter, int loglevel_value, - const struct lttng_event_exclusion *exclusion) + const struct lttng_event_exclusion *exclusion, + uint64_t tracer_token) { struct lttng_ht_iter iter; struct lttng_ht_node_str *node; @@ -1445,6 +1737,7 @@ static struct ust_app_event *find_ust_app_event(struct lttng_ht *ht, key.loglevel_type = loglevel_value; /* lttng_event_exclusion and lttng_ust_event_exclusion structures are similar */ key.exclusion = exclusion; + key.tracer_token = tracer_token; /* Lookup using the event name as hash and a custom match fct. */ cds_lfht_lookup(ht->ht, ht->hash_fct((void *) name, lttng_ht_seed), @@ -1775,6 +2068,44 @@ error: return ret; } +/* + * Disable the specified map on to UST tracer for the UST session. + */ +static int disable_ust_map(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_map *ua_map) +{ + int ret; + + health_code_update(); + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_disable(app->sock, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app map %s disable failed for app (pid: %d) " + "and session handle %d with ret %d", + ua_map->name, app->pid, ua_sess->handle, 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 disable map failed. Application is dead."); + } + goto error; + } + + DBG2("UST app map %s disabled successfully for app (pid: %d)", + ua_map->name, app->pid); + +error: + health_code_update(); + return ret; +} + /* * Enable the specified channel on to UST tracer for the UST session. */ @@ -1815,6 +2146,46 @@ error: return ret; } +/* + * Enable the specified map on to UST tracer for the UST session. + */ +static int enable_ust_map(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_map *ua_map) +{ + int ret; + + health_code_update(); + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_enable(app->sock, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app map %s enable failed for app (pid: %d) " + "and session handle %d with ret %d", + ua_map->name, app->pid, ua_sess->handle, 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 enable map failed. Application is dead."); + } + goto error; + } + + ua_map->enabled = 1; + + DBG2("UST app map %s enabled successfully for app (pid: %d)", + ua_map->name, app->pid); + +error: + health_code_update(); + return ret; +} + /* * Enable the specified event on to UST tracer for the UST session. */ @@ -1904,13 +2275,64 @@ error: return ret; } +/* + * Send map and stream buffer to application. + * + * Return 0 on success. On error, a negative value is returned. + */ +static int send_map_pid_to_ust(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_map *ua_map) +{ + int ret; + struct ust_app_map_counter *counter, *ctmp; + + assert(app); + assert(ua_sess); + assert(ua_map); + + health_code_update(); + + DBG("UST app sending map %s to UST app sock %d", ua_map->name, + app->sock); + + /* Send map to the application. */ + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_data_to_ust(app->sock, + ua_sess->handle, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + assert(ret == 0); + + ua_map->handle = ua_map->obj->handle; + + health_code_update(); + + /* Send all streams to application. */ + cds_list_for_each_entry_safe(counter, ctmp, &ua_map->counters.head, list) { + pthread_mutex_lock(&app->sock_lock); + // Do send the per cpu counter here + ret = ustctl_send_counter_cpu_data_to_ust(app->sock, + ua_map->obj, counter->obj); + pthread_mutex_unlock(&app->sock_lock); + assert(ret == 0); + + /* We don't need the stream anymore once sent to the tracer. */ + cds_list_del(&counter->list); + delete_ust_app_map_counter(-1, counter, app); + } + /* Flag the map that it is sent to the application. */ + ua_map->is_sent = 1; + + health_code_update(); + return ret; +} + /* * Create the specified event onto the UST tracer for a UST session. * * Should be called with session mutex held. */ static -int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, +int create_ust_channel_event(struct ust_app *app, struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan, struct ust_app_event *ua_event) { int ret = 0; @@ -1995,13 +2417,173 @@ error: return ret; } -static int init_ust_event_notifier_from_event_rule( - const struct lttng_event_rule *rule, - struct lttng_ust_event_notifier *event_notifier) +static +void add_key_token(struct lttng_ust_key_token *ust_key_token, + const struct lttng_map_key_token *key_token) { - enum lttng_event_rule_status status; - enum lttng_loglevel_type loglevel_type; - enum lttng_ust_loglevel_type ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + switch (key_token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + const struct lttng_map_key_token_string *str_token; + str_token = (typeof(str_token)) key_token; + + ust_key_token->type = LTTNG_UST_KEY_TOKEN_STRING; + strncpy(ust_key_token->arg.string, str_token->string, + LTTNG_UST_KEY_TOKEN_STRING_LEN_MAX); + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + const struct lttng_map_key_token_variable *var_token; + var_token = (typeof(var_token)) key_token; + switch (var_token->type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + ust_key_token->type = LTTNG_UST_KEY_TOKEN_EVENT_NAME; + break; + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + ust_key_token->type = LTTNG_UST_KEY_TOKEN_PROVIDER_NAME; + break; + default: + abort(); + } + + break; + } + default: + abort(); + } +} + +/* + * Create the specified event onto the UST tracer for a UST session. + * + * Should be called with session mutex held. + */ +static +int create_ust_map_event(struct ust_app *app, struct ust_app_session *ua_sess, + struct ust_app_map *ua_map, const struct lttng_map_key *key, + struct ust_app_event *ua_event) +{ + int ret = 0; + unsigned int i, key_token_count; + enum lttng_map_key_status status; + struct lttng_ust_counter_event counter_event = {0}; + + health_code_update(); + + memcpy(&counter_event.event, &ua_event->attr, sizeof(struct lttng_ust_event)); + + status = lttng_map_key_get_token_count(key, &key_token_count); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + ret = LTTNG_ERR_UNK; + goto error; + } + + assert(key_token_count > 0); + + counter_event.key.nr_dimensions = 1; + counter_event.key.key_dimensions[0].nr_key_tokens = key_token_count; + + if (key_token_count > LTTNG_UST_NR_KEY_TOKEN) { + ERR("Too many key tokens for UST tracer: token count = %u token count max =%u", + key_token_count, LTTNG_UST_NR_KEY_TOKEN); + ret = LTTNG_ERR_INVALID; + goto error; + } + + for (i = 0; i < key_token_count; i++) { + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + + add_key_token(&counter_event.key.key_dimensions[0].key_tokens[i], + token); + } + + /* Create UST event on tracer */ + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_counter_create_event(app->sock, &counter_event, ua_map->obj, + &ua_event->obj); + pthread_mutex_unlock(&app->sock_lock); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + abort(); + ERR("Error ustctl counter create event %s for app pid: %d with ret %d", + ua_event->attr.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 counter create event failed. Application is dead."); + } + goto error; + } + + ua_event->handle = ua_event->obj->handle; + + DBG2("UST app map 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_object_filter(app, ua_event->filter, ua_event->obj); + if (ret < 0) { + goto error; + } + } + + /* Set exclusions for the event */ + if (ua_event->exclusion) { + ret = set_ust_object_exclusions(app, ua_event->exclusion, ua_event->obj); + if (ret < 0) { + goto error; + } + } + + /* If event not enabled, disable it on the tracer */ + if (ua_event->enabled) { + /* + * We now need to explicitly enable the event, since it + * is now disabled at creation. + */ + ret = enable_ust_object(app, ua_event->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; + } + } + +error: + health_code_update(); + return ret; +} + +static int init_ust_event_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_ust_event *event) +{ + enum lttng_event_rule_status status; + enum lttng_ust_loglevel_type ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; int loglevel = -1, ret = 0; const char *pattern; @@ -2009,8 +2591,6 @@ static int init_ust_event_notifier_from_event_rule( assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT); - memset(event_notifier, 0, sizeof(*event_notifier)); - if (lttng_event_rule_targets_agent_domain(rule)) { /* * Special event for agents @@ -2023,44 +2603,43 @@ static int init_ust_event_notifier_from_event_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. */ - abort(); - } + const struct lttng_log_level_rule *log_level_rule; - status = lttng_event_rule_tracepoint_get_log_level_type( - rule, &loglevel_type); + status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern); if (status != LTTNG_EVENT_RULE_STATUS_OK) { /* At this point, this is a fatal error. */ abort(); } - switch (loglevel_type) { - case LTTNG_EVENT_LOGLEVEL_ALL: + status = lttng_event_rule_tracepoint_get_log_level_rule( + rule, &log_level_rule); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { 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; - default: - /* Unknown log level specification type. */ - abort(); - } + } else if (status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_log_level_rule_status llr_status; + + switch (lttng_log_level_rule_get_type(log_level_rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE; + llr_status = lttng_log_level_rule_exactly_get_level(log_level_rule, &loglevel); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_RANGE; + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(log_level_rule, &loglevel); + break; + default: + abort(); + } - if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { - status = lttng_event_rule_tracepoint_get_log_level( - rule, &loglevel); - assert(status == LTTNG_EVENT_RULE_STATUS_OK); + assert(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + } else { + /* At this point this is a fatal error */ + assert(0); } } - event_notifier->event.instrumentation = LTTNG_UST_TRACEPOINT; - ret = lttng_strncpy(event_notifier->event.name, pattern, + event->instrumentation = LTTNG_UST_TRACEPOINT; + ret = lttng_strncpy(event->name, pattern, LTTNG_UST_SYM_NAME_LEN - 1); if (ret) { ERR("Failed to copy event rule pattern to notifier: pattern = '%s' ", @@ -2068,8 +2647,8 @@ static int init_ust_event_notifier_from_event_rule( goto end; } - event_notifier->event.loglevel_type = ust_loglevel_type; - event_notifier->event.loglevel = loglevel; + event->loglevel_type = ust_loglevel_type; + event->loglevel = loglevel; end: return ret; } @@ -2082,9 +2661,8 @@ static int create_ust_event_notifier(struct ust_app *app, struct ust_app_event_notifier_rule *ua_event_notifier_rule) { int ret = 0; - enum lttng_condition_status condition_status; + struct lttng_ust_event_notifier event_notifier = {0}; const struct lttng_condition *condition = NULL; - struct lttng_ust_event_notifier event_notifier; const struct lttng_event_rule *event_rule = NULL; unsigned int capture_bytecode_count = 0, i; enum lttng_condition_status cond_status; @@ -2095,15 +2673,15 @@ static int create_ust_event_notifier(struct ust_app *app, condition = lttng_trigger_get_const_condition( ua_event_notifier_rule->trigger); assert(condition); - assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); - condition_status = lttng_condition_event_rule_get_rule(condition, &event_rule); - assert(condition_status == LTTNG_CONDITION_STATUS_OK); + lttng_condition_on_event_get_rule(condition, &event_rule); assert(event_rule); assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT); - init_ust_event_notifier_from_event_rule(event_rule, &event_notifier); + init_ust_event_from_event_rule(event_rule, &event_notifier.event); event_notifier.event.token = ua_event_notifier_rule->token; + event_notifier.error_counter_index = ua_event_notifier_rule->error_counter_index; /* Create UST event notifier against the tracer. */ pthread_mutex_lock(&app->sock_lock); @@ -2158,13 +2736,13 @@ static int create_ust_event_notifier(struct ust_app *app, } /* Set the capture bytecodes. */ - cond_status = lttng_condition_event_rule_get_capture_descriptor_count( + cond_status = lttng_condition_on_event_get_capture_descriptor_count( condition, &capture_bytecode_count); assert(cond_status == LTTNG_CONDITION_STATUS_OK); for (i = 0; i < capture_bytecode_count; i++) { const struct lttng_bytecode *capture_bytecode = - lttng_condition_event_rule_get_capture_bytecode_at_index( + lttng_condition_on_event_get_capture_bytecode_at_index( condition, i); ret = set_ust_capture(app, capture_bytecode, i, @@ -2841,6 +3419,26 @@ error: return ret; } +/* + * Lookup ust app map for session and disable it on the tracer side. + */ +static +int disable_ust_app_map(struct ust_app_session *ua_sess, + struct ust_app_map *ua_map, struct ust_app *app) +{ + int ret; + + ret = disable_ust_map(app, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + + ua_map->enabled = 0; + +error: + return ret; +} + /* * Lookup ust app channel for session and enable it on the tracer side. This * MUST be called with a RCU read side lock acquired. @@ -2872,6 +3470,37 @@ error: return ret; } +/* + * Lookup ust app map for session and enable it on the tracer side. This + * MUST be called with a RCU read side lock acquired. + */ +static int enable_ust_app_map(struct ust_app_session *ua_sess, + struct ltt_ust_map *umap, struct ust_app *app) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app_map *ua_map; + + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &iter); + ua_map_node = lttng_ht_iter_get_node_str(&iter); + if (ua_map_node == NULL) { + DBG2("Unable to find map %s in ust session id %" PRIu64, + umap->name, ua_sess->tracing_id); + goto error; + } + + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + ret = enable_ust_map(app, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + +error: + return ret; +} + /* * Ask the consumer to create a channel and get it if successful. * @@ -2969,6 +3598,118 @@ error: return ret; } +static int create_map_object(struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, struct ust_app_map *ua_map) +{ + int i, ret, nr_counter_cpu; + struct ustctl_counter_dimension dimension[1] = {0}; + struct ustctl_daemon_counter *daemon_counter; + struct lttng_ust_object_data **counter_cpus; + enum ustctl_counter_bitness bitness; + int *counter_cpu_fds; + + assert(usess); + assert(ua_sess); + assert(ua_map); + assert(ua_map->bucket_count > 0); + + DBG("Creating UST map \"%s\"", ua_map->name); + + if (ua_map->bitness == LTTNG_MAP_BITNESS_32BITS) { + bitness = USTCTL_COUNTER_BITNESS_32; + } else { + bitness = USTCTL_COUNTER_BITNESS_64; + } + + nr_counter_cpu = ustctl_get_nr_cpu_per_counter(); + counter_cpu_fds = zmalloc(nr_counter_cpu * sizeof(*counter_cpu_fds)); + if (!counter_cpu_fds) { + ret = -1; + goto end; + } + + counter_cpus = zmalloc(nr_counter_cpu * sizeof(**counter_cpus)); + if (!counter_cpus) { + ret = -1; + goto free_cpu_fds; + } + + /* Need one fd for each cpu counter of the map. */ + ret = lttng_fd_get(LTTNG_FD_APPS, nr_counter_cpu); + if (ret < 0) { + ERR("Exhausted number of available FD upon create map"); + goto free_cpu_counters; + } + + for (i = 0; i < nr_counter_cpu; i++) { + counter_cpu_fds[i] = shm_create_anonymous("ust-map-counter"); + if (counter_cpu_fds[i] < 0) { + ERR("Error creating anonymous shared memory object"); + ret = -1; + goto error; + } + } + + dimension[0].size = ua_map->bucket_count; + dimension[0].has_underflow = false; + dimension[0].has_overflow = false; + + daemon_counter = ustctl_create_counter(1, dimension, 0, -1, + nr_counter_cpu, counter_cpu_fds, + bitness, + USTCTL_COUNTER_ARITHMETIC_MODULAR, + USTCTL_COUNTER_ALLOC_PER_CPU, + ua_map->coalesce_hits); + assert(daemon_counter); + + DBG("Created daemon counter succesfully"); + + ua_map->map_handle = daemon_counter; + + ret = ustctl_create_counter_data(daemon_counter, &ua_map->obj); + assert(ret == 0); + DBG("Created counter data succesfully"); + + for (i = 0; i < nr_counter_cpu; i++) { + struct ust_app_map_counter *counter; + + /* Create UST counter */ + counter = ust_app_alloc_map_counter(); + if (counter == NULL) { + ret = -ENOMEM; + goto release_counters; + } + + ret = ustctl_create_counter_cpu_data(daemon_counter, i, + &counter->obj); + if (ret < 0) { + ERR("Creating map counter cpu data"); + free(counter); + goto error; + } + + cds_list_add_tail(&counter->list, &ua_map->counters.head); + ua_map->counters.count++; + + DBG2("UST app map counter %d created successfully", + ua_map->counters.count); + } + + ret = 0; + goto end; + +error: +release_counters: + //TODO +free_cpu_counters: + free(counter_cpus); + +free_cpu_fds: + free(counter_cpu_fds); +end: + return ret; +} + /* * Duplicate the ust data object of the ust app stream and save it in the * buffer registry stream. @@ -2983,7 +3724,7 @@ static int duplicate_stream_object(struct buffer_reg_stream *reg_stream, assert(reg_stream); assert(stream); - /* Reserve the amount of file descriptor we need. */ + /* Duplicating a stream requires 2 new fds. Reserve them. */ ret = lttng_fd_get(LTTNG_FD_APPS, 2); if (ret < 0) { ERR("Exhausted number of available FD upon duplicate stream"); @@ -3005,21 +3746,57 @@ error: return ret; } +/* + * Duplicate the ust data object of the ust app map_counter and save it in the + * buffer registry map_counter. + * + * Return 0 on success or else a negative value. + */ +static int duplicate_map_counter_object(struct buffer_reg_map_counter *reg_map_counter, + struct ust_app_map_counter *map_counter) +{ + int ret; + + assert(reg_map_counter); + assert(map_counter); + + /* Duplicating a map_counter requires 2 new fds. Reserve them. */ + ret = lttng_fd_get(LTTNG_FD_APPS, 2); + if (ret < 0) { + ERR("Exhausted number of available FD upon duplicate map_counter"); + goto error; + } + + /* Duplicate object for map_counter once the original is in the registry. */ + ret = ustctl_duplicate_ust_object_data(&map_counter->obj, + reg_map_counter->obj.ust); + if (ret < 0) { + ERR("Duplicate map_counter obj from %p to %p failed with ret %d", + reg_map_counter->obj.ust, map_counter->obj, ret); + lttng_fd_put(LTTNG_FD_APPS, 2); + goto error; + } + map_counter->handle = map_counter->obj->handle; + +error: + return ret; +} + /* * Duplicate the ust data object of the ust app. channel and save it in the * buffer registry channel. * * Return 0 on success or else a negative value. */ -static int duplicate_channel_object(struct buffer_reg_channel *reg_chan, +static int duplicate_channel_object(struct buffer_reg_channel *buf_reg_chan, struct ust_app_channel *ua_chan) { int ret; - assert(reg_chan); + assert(buf_reg_chan); assert(ua_chan); - /* Need two fds for the channel. */ + /* Duplicating a channel requires 1 new fd. Reserve it. */ ret = lttng_fd_get(LTTNG_FD_APPS, 1); if (ret < 0) { ERR("Exhausted number of available FD upon duplicate channel"); @@ -3027,10 +3804,10 @@ static int duplicate_channel_object(struct buffer_reg_channel *reg_chan, } /* Duplicate object for stream once the original is in the registry. */ - ret = ustctl_duplicate_ust_object_data(&ua_chan->obj, reg_chan->obj.ust); + ret = ustctl_duplicate_ust_object_data(&ua_chan->obj, buf_reg_chan->obj.ust); if (ret < 0) { ERR("Duplicate channel obj from %p to %p failed with ret: %d", - reg_chan->obj.ust, ua_chan->obj, ret); + buf_reg_chan->obj.ust, ua_chan->obj, ret); goto error; } ua_chan->handle = ua_chan->obj->handle; @@ -3044,22 +3821,60 @@ error_fd_get: } /* - * For a given channel buffer registry, setup all streams of the given ust - * application channel. + * Duplicate the ust data object of the ust app. map and save it in the + * buffer registry map. * * Return 0 on success or else a negative value. */ -static int setup_buffer_reg_streams(struct buffer_reg_channel *reg_chan, - struct ust_app_channel *ua_chan, - struct ust_app *app) +static int duplicate_map_object(struct buffer_reg_map *buf_reg_map, + struct ust_app_map *ua_map) { - int ret = 0; - struct ust_app_stream *stream, *stmp; + int ret; - assert(reg_chan); - assert(ua_chan); + assert(buf_reg_map); + assert(ua_map); - DBG2("UST app setup buffer registry stream"); + /* Duplicating a map requires 1 new fd. Reserve it. */ + ret = lttng_fd_get(LTTNG_FD_APPS, 1); + if (ret < 0) { + ERR("Exhausted number of available FD upon duplicate map"); + goto error_fd_get; + } + + /* Duplicate object for stream once the original is in the registry. */ + ret = ustctl_duplicate_ust_object_data(&ua_map->obj, buf_reg_map->obj.ust); + if (ret < 0) { + ERR("Duplicate map obj from %p to %p failed with ret: %d", + buf_reg_map->obj.ust, ua_map->obj, ret); + goto error; + } + ua_map->handle = ua_map->obj->handle; + + return 0; + +error: + lttng_fd_put(LTTNG_FD_APPS, 1); +error_fd_get: + return ret; +} + +/* + * For a given channel buffer registry, setup all streams of the given ust + * application channel. + * + * Return 0 on success or else a negative value. + */ +static int setup_buffer_reg_streams(struct buffer_reg_channel *buf_reg_chan, + struct ust_app_channel *ua_chan, + struct ust_app *app) +{ + int ret = 0; + struct ust_app_stream *stream, *stmp; + + assert(buf_reg_chan); + assert(ua_chan); + + DBG2("UST app setup buffer registry stream"); /* Send all streams to application. */ cds_list_for_each_entry_safe(stream, stmp, &ua_chan->streams.head, list) { @@ -3076,7 +3891,7 @@ static int setup_buffer_reg_streams(struct buffer_reg_channel *reg_chan, */ reg_stream->obj.ust = stream->obj; stream->obj = NULL; - buffer_reg_stream_add(reg_stream, reg_chan); + buffer_reg_stream_add(reg_stream, buf_reg_chan); /* We don't need the streams anymore. */ cds_list_del(&stream->list); @@ -3087,6 +3902,50 @@ error: return ret; } +/* + * For a given map buffer registry, setup all counters of the given ust + * application map. + * + * Return 0 on success or else a negative value. + */ +static int setup_buffer_reg_map_counters(struct buffer_reg_map *buf_reg_map, + struct ust_app_map *ua_map, + struct ust_app *app) +{ + int ret = 0; + struct ust_app_map_counter *counter, *stmp; + + assert(buf_reg_map); + assert(ua_map); + + DBG2("UST app setup buffer registry counter"); + + /* Send all counters to application. */ + cds_list_for_each_entry_safe(counter, stmp, &ua_map->counters.head, list) { + struct buffer_reg_map_counter *reg_counter; + + ret = buffer_reg_map_counter_create(®_counter); + if (ret < 0) { + goto error; + } + + /* + * Keep original pointer and nullify it in the counter so the delete + * counter call does not release the object. + */ + reg_counter->obj.ust = counter->obj; + counter->obj = NULL; + buffer_reg_map_counter_add(reg_counter, buf_reg_map); + + /* We don't need the counters anymore. */ + cds_list_del(&counter->list); + delete_ust_app_map_counter(-1, counter, app); + } + +error: + return ret; +} + /* * Create a buffer registry channel for the given session registry and * application channel object. If regp pointer is valid, it's set with the @@ -3099,7 +3958,7 @@ static int create_buffer_reg_channel(struct buffer_reg_session *reg_sess, struct ust_app_channel *ua_chan, struct buffer_reg_channel **regp) { int ret; - struct buffer_reg_channel *reg_chan = NULL; + struct buffer_reg_channel *buf_reg_chan = NULL; assert(reg_sess); assert(ua_chan); @@ -3107,14 +3966,14 @@ static int create_buffer_reg_channel(struct buffer_reg_session *reg_sess, DBG2("UST app creating buffer registry channel for %s", ua_chan->name); /* Create buffer registry channel. */ - ret = buffer_reg_channel_create(ua_chan->tracing_channel_id, ®_chan); + ret = buffer_reg_channel_create(ua_chan->tracing_channel_id, &buf_reg_chan); if (ret < 0) { goto error_create; } - assert(reg_chan); - reg_chan->consumer_key = ua_chan->key; - reg_chan->subbuf_size = ua_chan->attr.subbuf_size; - reg_chan->num_subbuf = ua_chan->attr.num_subbuf; + assert(buf_reg_chan); + buf_reg_chan->consumer_key = ua_chan->key; + buf_reg_chan->subbuf_size = ua_chan->attr.subbuf_size; + buf_reg_chan->num_subbuf = ua_chan->attr.num_subbuf; /* Create and add a channel registry to session. */ ret = ust_registry_channel_add(reg_sess->reg.ust, @@ -3122,17 +3981,63 @@ static int create_buffer_reg_channel(struct buffer_reg_session *reg_sess, if (ret < 0) { goto error; } - buffer_reg_channel_add(reg_sess, reg_chan); + buffer_reg_channel_add(reg_sess, buf_reg_chan); if (regp) { - *regp = reg_chan; + *regp = buf_reg_chan; } return 0; error: /* Safe because the registry channel object was not added to any HT. */ - buffer_reg_channel_destroy(reg_chan, LTTNG_DOMAIN_UST); + buffer_reg_channel_destroy(buf_reg_chan, LTTNG_DOMAIN_UST); +error_create: + return ret; +} + +/* + * Create a buffer registry map for the given session registry and + * application map object. If regp pointer is valid, it's set with the + * created object. Important, the created object is NOT added to the session + * registry hash table. + * + * Return 0 on success else a negative value. + */ +static int create_buffer_reg_map(struct buffer_reg_session *reg_sess, + struct ust_app_map *ua_map, struct buffer_reg_map **regp) +{ + int ret; + struct buffer_reg_map *buf_reg_map = NULL; + + assert(reg_sess); + assert(ua_map); + + DBG2("UST app creating buffer registry map for %s", ua_map->name); + + /* Create buffer registry map. */ + ret = buffer_reg_map_create(ua_map->tracing_map_id, &buf_reg_map); + if (ret < 0) { + goto error_create; + } + assert(buf_reg_map); + + /* Create and add a map registry to session. */ + ret = ust_registry_map_add(reg_sess->reg.ust, ua_map->tracing_map_id); + if (ret < 0) { + goto error; + } + buffer_reg_map_add(reg_sess, buf_reg_map); + + if (regp) { + *regp = buf_reg_map; + } + + return 0; + +error: + /* Safe because the registry map object was not added to any HT. */ + buffer_reg_map_destroy(buf_reg_map, LTTNG_DOMAIN_UST); error_create: return ret; } @@ -3144,32 +4049,70 @@ error_create: * Return 0 on success else a negative value. */ static int setup_buffer_reg_channel(struct buffer_reg_session *reg_sess, - struct ust_app_channel *ua_chan, struct buffer_reg_channel *reg_chan, + struct ust_app_channel *ua_chan, struct buffer_reg_channel *buf_reg_chan, struct ust_app *app) { int ret; assert(reg_sess); - assert(reg_chan); + assert(buf_reg_chan); assert(ua_chan); assert(ua_chan->obj); DBG2("UST app setup buffer registry channel for %s", ua_chan->name); /* Setup all streams for the registry. */ - ret = setup_buffer_reg_streams(reg_chan, ua_chan, app); + ret = setup_buffer_reg_streams(buf_reg_chan, ua_chan, app); if (ret < 0) { goto error; } - reg_chan->obj.ust = ua_chan->obj; + buf_reg_chan->obj.ust = ua_chan->obj; ua_chan->obj = NULL; return 0; error: - buffer_reg_channel_remove(reg_sess, reg_chan); - buffer_reg_channel_destroy(reg_chan, LTTNG_DOMAIN_UST); + buffer_reg_channel_remove(reg_sess, buf_reg_chan); + buffer_reg_channel_destroy(buf_reg_chan, LTTNG_DOMAIN_UST); + return ret; +} + +/* + * Setup buffer registry map for the given session registry and application + * map object. If regp pointer is valid, it's set with the created object. + * + * Return 0 on success else a negative value. + */ +static int setup_buffer_reg_map(struct buffer_reg_session *reg_sess, + struct ust_app_map *ua_map, struct buffer_reg_map *buf_reg_map, + struct ust_app *app) +{ + int ret; + + assert(reg_sess); + assert(buf_reg_map); + assert(ua_map); + assert(ua_map->obj); + + DBG2("UST app setup buffer registry map for %s", ua_map->name); + + /* Setup all counters for the registry. */ + ret = setup_buffer_reg_map_counters(buf_reg_map, ua_map, app); + if (ret < 0) { + goto error; + } + + buf_reg_map->obj.ust = ua_map->obj; + ua_map->obj = NULL; + buf_reg_map->daemon_counter = ua_map->map_handle; + ua_map->map_handle = NULL; + + return 0; + +error: + buffer_reg_map_remove(reg_sess, buf_reg_map); + buffer_reg_map_destroy(buf_reg_map, LTTNG_DOMAIN_UST); return ret; } @@ -3178,21 +4121,21 @@ error: * * Return 0 on success else a negative value. */ -static int send_channel_uid_to_ust(struct buffer_reg_channel *reg_chan, +static int send_channel_uid_to_ust(struct buffer_reg_channel *buf_reg_chan, struct ust_app *app, struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan) { int ret; struct buffer_reg_stream *reg_stream; - assert(reg_chan); + assert(buf_reg_chan); assert(app); assert(ua_sess); assert(ua_chan); DBG("UST app sending buffer registry channel to ust sock %d", app->sock); - ret = duplicate_channel_object(reg_chan, ua_chan); + ret = duplicate_channel_object(buf_reg_chan, ua_chan); if (ret < 0) { goto error; } @@ -3209,8 +4152,8 @@ static int send_channel_uid_to_ust(struct buffer_reg_channel *reg_chan, health_code_update(); /* Send all streams to application. */ - pthread_mutex_lock(®_chan->stream_list_lock); - cds_list_for_each_entry(reg_stream, ®_chan->streams, lnode) { + pthread_mutex_lock(&buf_reg_chan->stream_list_lock); + cds_list_for_each_entry(reg_stream, &buf_reg_chan->streams, lnode) { struct ust_app_stream stream; ret = duplicate_stream_object(reg_stream, &stream); @@ -3236,7 +4179,87 @@ static int send_channel_uid_to_ust(struct buffer_reg_channel *reg_chan, ua_chan->is_sent = 1; error_stream_unlock: - pthread_mutex_unlock(®_chan->stream_list_lock); + pthread_mutex_unlock(&buf_reg_chan->stream_list_lock); +error: + return ret; +} + +/* + * Send buffer registry map to the application. + * + * Return 0 on success else a negative value. + */ +static int send_map_uid_to_ust(struct buffer_reg_map *buf_reg_map, + struct ust_app *app, struct ust_app_session *ua_sess, + struct ust_app_map *ua_map) +{ + int ret; + struct buffer_reg_map_counter *reg_map_counter; + + assert(buf_reg_map); + assert(app); + assert(ua_sess); + assert(ua_map); + + DBG("UST app sending buffer registry map to ust sock %d", app->sock); + + ret = duplicate_map_object(buf_reg_map, ua_map); + if (ret < 0) { + goto error; + } + + /* Send map to the application. */ + + pthread_mutex_lock(&app->sock_lock); + ret = ustctl_send_counter_data_to_ust(app->sock, + ua_sess->handle, ua_map->obj); + pthread_mutex_unlock(&app->sock_lock); + assert(ret == 0); + if (ret < 0) { + if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) { + ret = -ENOTCONN; /* Caused by app exiting. */ + } + goto error_map_counter_unlock; + } + + ua_map->handle = ua_map->obj->handle; + + health_code_update(); + + /* Send all map_counters to application. */ + pthread_mutex_lock(&buf_reg_map->counter_list_lock); + cds_list_for_each_entry(reg_map_counter, &buf_reg_map->counters, lnode) { + struct ust_app_map_counter map_counter; + + ret = duplicate_map_counter_object(reg_map_counter, &map_counter); + if (ret < 0) { + goto error_map_counter_unlock; + } + + pthread_mutex_lock(&app->sock_lock); + // Do send the per cpu counter here + ret = ustctl_send_counter_cpu_data_to_ust(app->sock, + ua_map->obj, map_counter.obj); + pthread_mutex_unlock(&app->sock_lock); + assert(ret == 0); + if (ret < 0) { + (void) release_ust_app_map_counter(-1, &map_counter, app); + if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) { + ret = -ENOTCONN; /* Caused by app exiting. */ + } + goto error_map_counter_unlock; + } + + /* + * The return value is not important here. This function will + * output an error if needed. + */ + (void) release_ust_app_map_counter(-1, &map_counter, app); + } + ua_map->is_sent = 1; + +error_map_counter_unlock: + pthread_mutex_unlock(&buf_reg_map->counter_list_lock); error: return ret; } @@ -3255,10 +4278,10 @@ static int create_channel_per_uid(struct ust_app *app, { int ret; struct buffer_reg_uid *reg_uid; - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct ltt_session *session = NULL; enum lttng_error_code notification_ret; - struct ust_registry_channel *chan_reg; + struct ust_registry_channel *ust_reg_chan; assert(app); assert(usess); @@ -3275,14 +4298,14 @@ static int create_channel_per_uid(struct ust_app *app, */ assert(reg_uid); - reg_chan = buffer_reg_channel_find(ua_chan->tracing_channel_id, + buf_reg_chan = buffer_reg_channel_find(ua_chan->tracing_channel_id, reg_uid); - if (reg_chan) { + if (buf_reg_chan) { goto send_channel; } /* Create the buffer registry channel object. */ - ret = create_buffer_reg_channel(reg_uid->registry, ua_chan, ®_chan); + ret = create_buffer_reg_channel(reg_uid->registry, ua_chan, &buf_reg_chan); if (ret < 0) { ERR("Error creating the UST channel \"%s\" registry instance", ua_chan->name); @@ -3311,8 +4334,8 @@ static int create_channel_per_uid(struct ust_app *app, */ ust_registry_channel_del_free(reg_uid->registry->reg.ust, ua_chan->tracing_channel_id, false); - buffer_reg_channel_remove(reg_uid->registry, reg_chan); - buffer_reg_channel_destroy(reg_chan, LTTNG_DOMAIN_UST); + buffer_reg_channel_remove(reg_uid->registry, buf_reg_chan); + buffer_reg_channel_destroy(buf_reg_chan, LTTNG_DOMAIN_UST); goto error; } @@ -3320,7 +4343,7 @@ static int create_channel_per_uid(struct ust_app *app, * Setup the streams and add it to the session registry. */ ret = setup_buffer_reg_channel(reg_uid->registry, - ua_chan, reg_chan, app); + ua_chan, buf_reg_chan, app); if (ret < 0) { ERR("Error setting up UST channel \"%s\"", ua_chan->name); goto error; @@ -3328,11 +4351,11 @@ static int create_channel_per_uid(struct ust_app *app, /* Notify the notification subsystem of the channel's creation. */ pthread_mutex_lock(®_uid->registry->reg.ust->lock); - chan_reg = ust_registry_channel_find(reg_uid->registry->reg.ust, + ust_reg_chan = ust_registry_channel_find(reg_uid->registry->reg.ust, ua_chan->tracing_channel_id); - assert(chan_reg); - chan_reg->consumer_key = ua_chan->key; - chan_reg = NULL; + assert(ust_reg_chan); + ust_reg_chan->consumer_key = ua_chan->key; + ust_reg_chan = NULL; pthread_mutex_unlock(®_uid->registry->reg.ust->lock); notification_ret = notification_thread_command_add_channel( @@ -3350,7 +4373,7 @@ static int create_channel_per_uid(struct ust_app *app, send_channel: /* Send buffers to the application. */ - ret = send_channel_uid_to_ust(reg_chan, app, ua_sess, ua_chan); + ret = send_channel_uid_to_ust(buf_reg_chan, app, ua_sess, ua_chan); if (ret < 0) { if (ret != -ENOTCONN) { ERR("Error sending channel to application"); @@ -3365,6 +4388,142 @@ error: return ret; } +/* + * Create and send to the application the created buffers with per UID buffers. + * + * This MUST be called with a RCU read side lock acquired. + * The session list lock and the session's lock must be acquired. + * + * Return 0 on success else a negative value. + */ +static int create_map_per_uid(struct ust_app *app, + struct ltt_ust_session *usess, struct ust_app_session *ua_sess, + struct ust_app_map *ua_map) +{ + int ret; + struct buffer_reg_uid *buffer_reg_uid; + struct buffer_reg_map *buffer_reg_map; + struct ltt_session *session = NULL; + struct ust_registry_map *ust_reg_map; + + assert(app); + assert(usess); + assert(ua_sess); + assert(ua_map); + + DBG("UST app creating map %s with per UID buffers", ua_map->name); + + buffer_reg_uid = buffer_reg_uid_find(usess->id, app->bits_per_long, app->uid); + /* + * The session creation handles the creation of this global registry + * object. If none can be find, there is a code flow problem or a + * teardown race. + */ + assert(buffer_reg_uid); + + buffer_reg_map = buffer_reg_map_find(ua_map->tracing_map_id, + buffer_reg_uid); + if (buffer_reg_map) { + goto send_map; + } + + /* Create the buffer registry map object. */ + ret = create_buffer_reg_map(buffer_reg_uid->registry, ua_map, + &buffer_reg_map); + if (ret < 0) { + ERR("Error creating the UST map \"%s\" registry instance", + ua_map->name); + goto error; + } + + session = session_find_by_id(ua_sess->tracing_id); + assert(session); + assert(pthread_mutex_trylock(&session->lock)); + assert(session_trylock_list()); + + /* + */ + ret = create_map_object(usess, ua_sess, ua_map); + assert(ret == 0); + if (ret < 0) { + ERR("Error creating UST map object: map_name = \"%s\"", ua_map->name); + goto error; + } + + /* + * Setup the streams and add it to the session registry. + */ + ret = setup_buffer_reg_map(buffer_reg_uid->registry, ua_map, + buffer_reg_map, app); + if (ret < 0) { + ERR("Error setting up UST map \"%s\"", ua_map->name); + goto error; + } + + /* Notify the notification subsystem of the map's creation. */ + pthread_mutex_lock(&buffer_reg_uid->registry->reg.ust->lock); + ust_reg_map = ust_registry_map_find(buffer_reg_uid->registry->reg.ust, + ua_map->tracing_map_id); + assert(ust_reg_map); + ust_reg_map = NULL; + pthread_mutex_unlock(&buffer_reg_uid->registry->reg.ust->lock); + +send_map: + /* Send buffers to the application. */ + ret = send_map_uid_to_ust(buffer_reg_map, app, ua_sess, ua_map); + if (ret < 0) { + if (ret != -ENOTCONN) { + ERR("Error sending map to application"); + } + goto error; + } + +error: + if (session) { + session_put(session); + } + return ret; +} + +//static int destroy_map_per_uid(struct ust_app *app, +// struct ltt_ust_session *usess, struct ust_app_session *ua_sess, +// struct ust_app_map *ua_map) +//{ +// int ret; +// struct buffer_reg_uid *buffer_reg_uid; +// struct buffer_reg_map *buffer_reg_map; +// +// assert(app); +// assert(usess); +// assert(ua_sess); +// assert(ua_map); +// +// DBG("UST app destroy map %s with per UID buffers", ua_map->name); +// +// buffer_reg_uid = buffer_reg_uid_find(usess->id, app->bits_per_long, app->uid); +// /* +// * The session creation handles the creation of this global registry +// * object. If none can be find, there is a code flow problem or a +// * teardown race. +// */ +// assert(buffer_reg_uid); +// +// buffer_reg_map = buffer_reg_map_find(ua_map->tracing_map_id, +// buffer_reg_uid); +// if (!buffer_reg_map) { +// ERR("Can't find map in buffer registry: map-name = '%s', uid = %d", +// ua_map->name, app->uid); +// ret = -1; +// goto end; +// } +// +// buffer_reg_map_destroy(buffer_reg_map, LTTNG_DOMAIN_UST); +// +// ret = 0; +//end: +// return ret; +//} + /* * Create and send to the application the created buffers with per PID buffers. * @@ -3382,7 +4541,7 @@ static int create_channel_per_pid(struct ust_app *app, enum lttng_error_code cmd_ret; struct ltt_session *session = NULL; uint64_t chan_reg_key; - struct ust_registry_channel *chan_reg; + struct ust_registry_channel *ust_reg_chan; assert(app); assert(usess); @@ -3431,9 +4590,9 @@ static int create_channel_per_pid(struct ust_app *app, chan_reg_key = ua_chan->key; pthread_mutex_lock(®istry->lock); - chan_reg = ust_registry_channel_find(registry, chan_reg_key); - assert(chan_reg); - chan_reg->consumer_key = ua_chan->key; + ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key); + assert(ust_reg_chan); + ust_reg_chan->consumer_key = ua_chan->key; pthread_mutex_unlock(®istry->lock); cmd_ret = notification_thread_command_add_channel( @@ -3462,54 +4621,133 @@ error: } /* - * From an already allocated ust app channel, create the channel buffers if - * needed and send them to the application. This MUST be called with a RCU read - * side lock acquired. + * Create and send to the application the created buffers with per PID buffers. * * Called with UST app session lock held. + * The session list lock and the session's lock must be acquired. * - * Return 0 on success or else a negative value. Returns -ENOTCONN if - * the application exited concurrently. + * Return 0 on success else a negative value. */ -static int ust_app_channel_send(struct ust_app *app, +static int create_map_per_pid(struct ust_app *app, struct ltt_ust_session *usess, struct ust_app_session *ua_sess, - struct ust_app_channel *ua_chan) + struct ust_app_map *ua_map) { int ret; + struct ust_registry_session *registry; + struct ltt_session *session = NULL; + uint64_t map_reg_key; + struct ust_registry_map *ust_reg_map; assert(app); assert(usess); - assert(usess->active); assert(ua_sess); - assert(ua_chan); + assert(ua_map); - /* Handle buffer type before sending the channel to the application. */ - switch (usess->buffer_type) { - case LTTNG_BUFFER_PER_UID: - { - ret = create_channel_per_uid(app, usess, ua_sess, ua_chan); - if (ret < 0) { - goto error; - } - break; - } - case LTTNG_BUFFER_PER_PID: - { - ret = create_channel_per_pid(app, usess, ua_sess, ua_chan); - if (ret < 0) { - goto error; - } - break; - } - default: - assert(0); - ret = -EINVAL; - goto error; - } + DBG("UST app creating map %s with per PID buffers", ua_map->name); - /* Initialize ust objd object using the received handle and add it. */ - lttng_ht_node_init_ulong(&ua_chan->ust_objd_node, ua_chan->handle); - lttng_ht_add_unique_ulong(app->ust_objd, &ua_chan->ust_objd_node); + rcu_read_lock(); + + registry = get_session_registry(ua_sess); + /* The UST app session lock is held, registry shall not be null. */ + assert(registry); + + /* Create and add a new map registry to session. */ + ret = ust_registry_map_add(registry, ua_map->key); + if (ret < 0) { + ERR("Error creating the UST map \"%s\" registry instance", + ua_map->name); + goto error; + } + + session = session_find_by_id(ua_sess->tracing_id); + assert(session); + + assert(pthread_mutex_trylock(&session->lock)); + assert(session_trylock_list()); + + /* Create and get map. */ + ret = create_map_object(usess, ua_sess, ua_map); + if (ret < 0) { + ERR("Error creating UST map object: map_name = \"%s\" ", + ua_map->name); + goto error_remove_from_registry; + } + + ret = send_map_pid_to_ust(app, ua_sess, ua_map); + if (ret < 0) { + if (ret != -ENOTCONN) { + ERR("Error sending map to application"); + } + goto error_remove_from_registry; + } + + map_reg_key = ua_map->key; + pthread_mutex_lock(®istry->lock); + ust_reg_map = ust_registry_map_find(registry, map_reg_key); + assert(ust_reg_map); + pthread_mutex_unlock(®istry->lock); + +error_remove_from_registry: + if (ret) { + ust_registry_map_del_free(registry, ua_map->key); + } +error: + rcu_read_unlock(); + if (session) { + session_put(session); + } + return ret; +} + +/* + * From an already allocated ust app channel, create the channel buffers if + * needed and send them to the application. This MUST be called with a RCU read + * side lock acquired. + * + * Called with UST app session lock held. + * + * Return 0 on success or else a negative value. Returns -ENOTCONN if + * the application exited concurrently. + */ +static int ust_app_channel_send(struct ust_app *app, + struct ltt_ust_session *usess, struct ust_app_session *ua_sess, + struct ust_app_channel *ua_chan) +{ + int ret; + + assert(app); + assert(usess); + assert(usess->active); + assert(ua_sess); + assert(ua_chan); + + /* Handle buffer type before sending the channel to the application. */ + switch (usess->buffer_type) { + case LTTNG_BUFFER_PER_UID: + { + ret = create_channel_per_uid(app, usess, ua_sess, ua_chan); + if (ret < 0) { + goto error; + } + break; + } + case LTTNG_BUFFER_PER_PID: + { + ret = create_channel_per_pid(app, usess, ua_sess, ua_chan); + if (ret < 0) { + goto error; + } + break; + } + default: + assert(0); + ret = -EINVAL; + goto error; + } + + /* Initialize ust objd object using the received handle and add it. */ + lttng_ht_node_init_ulong(&ua_chan->ust_objd_node, ua_chan->handle); + lttng_ht_add_unique_ulong(app->ust_chan_objd, &ua_chan->ust_objd_node); /* If channel is not enabled, disable it on the tracer */ if (!ua_chan->enabled) { @@ -3523,6 +4761,68 @@ error: return ret; } +/* + * From an already allocated ust app map, create the map buffers if + * needed and send them to the application. This MUST be called with a RCU read + * side lock acquired. + * + * Called with UST app session lock held. + * + * Return 0 on success or else a negative value. Returns -ENOTCONN if + * the application exited concurrently. + */ +static int ust_app_map_send(struct ust_app *app, + struct ltt_ust_session *usess, struct ust_app_session *ua_sess, + struct ust_app_map *ua_map) +{ + int ret; + + assert(app); + assert(usess); + assert(usess->active); + assert(ua_sess); + assert(ua_map); + + /* Handle buffer type before sending the map to the application. */ + switch (usess->buffer_type) { + case LTTNG_BUFFER_PER_UID: + { + ret = create_map_per_uid(app, usess, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + break; + } + case LTTNG_BUFFER_PER_PID: + { + ret = create_map_per_pid(app, usess, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + break; + } + default: + assert(0); + ret = -EINVAL; + goto error; + } + + /* Initialize ust objd object using the received handle and add it. */ + lttng_ht_node_init_ulong(&ua_map->ust_objd_node, ua_map->handle); + lttng_ht_add_unique_ulong(app->ust_map_objd, &ua_map->ust_objd_node); + + /* If map is not enabled, disable it on the tracer */ + if (!ua_map->enabled) { + ret = disable_ust_map(app, ua_sess, ua_map); + if (ret < 0) { + goto error; + } + } + +error: + return ret; +} + /* * Create UST app channel and return it through ua_chanp if not NULL. * @@ -3573,6 +4873,63 @@ error: return ret; } +/* + * Create UST app map and return it through ua_mapp if not NULL. + * + * Called with UST app session lock and RCU read-side lock held. + * + * Return 0 on success or else a negative value. + */ +static int ust_app_map_allocate(struct ust_app_session *ua_sess, + struct ltt_ust_map *umap, + enum lttng_ust_chan_type type, struct ltt_ust_session *usess, + struct ust_app_map **ua_mapp) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app_map *ua_map; + + DBG("Allocating map id = %"PRIu64, umap->id); + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &iter); + ua_map_node = lttng_ht_iter_get_node_str(&iter); + if (ua_map_node != NULL) { + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + goto end; + } + + ua_map = alloc_ust_app_map(umap->name, ua_sess); + if (ua_map == NULL) { + /* Only malloc can fail here */ + ret = -ENOMEM; + goto error; + } + //shadow_copy_map(ua_map, umap); + ua_map->tracing_map_id = umap->id; + ua_map->coalesce_hits = umap->coalesce_hits; + ua_map->dead_app_kv_values = &umap->dead_app_kv_values; + ua_map->bitness = umap->bitness; + + /* Set map type. */ + //ua_map->attr.type = type; + ua_map->bucket_count = umap->bucket_count; + + /* Only add the map if successful on the tracer side. */ + lttng_ht_add_unique_str(ua_sess->maps, &ua_map->node); +end: + if (ua_mapp) { + *ua_mapp = ua_map; + } + + /* Everything went well. */ + return 0; + +error: + return ret; +} + /* * Create UST app event and create it on the tracer side. * @@ -3580,7 +4937,7 @@ error: * Called with ust app session mutex held. */ static -int create_ust_app_event(struct ust_app_session *ua_sess, +int create_ust_app_channel_event(struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan, struct ltt_ust_event *uevent, struct ust_app *app) { @@ -3596,7 +4953,7 @@ int create_ust_app_event(struct ust_app_session *ua_sess, shadow_copy_event(ua_event, uevent); /* Create it on the tracer side */ - ret = create_ust_event(app, ua_sess, ua_chan, ua_event); + ret = create_ust_channel_event(app, ua_sess, ua_chan, ua_event); if (ret < 0) { /* * Not found previously means that it does not exist on the @@ -3614,7 +4971,7 @@ int create_ust_app_event(struct ust_app_session *ua_sess, goto error; } - add_unique_ust_app_event(ua_chan, ua_event); + add_unique_ust_app_event(ua_chan->events, ua_event); DBG2("UST app create event completed: app = '%s' (ppid: %d)", app->name, app->ppid); @@ -3628,6 +4985,86 @@ error: return ret; } +/* + * Create UST app event and create it on the tracer side. + * + * Must be called with the RCU read side lock held. + * Called with ust app session mutex held. + */ +static +int create_ust_app_map_event(struct ust_app_session *ua_sess, + struct ust_app_map *ua_map, struct ltt_ust_event *uevent, + struct ust_app *app) +{ + int ret = 0; + uint64_t map_reg_key; + struct ust_app_event *ua_event; + struct ust_registry_session *registry; + + ua_event = alloc_ust_app_event(uevent->attr.name, &uevent->attr); + if (ua_event == NULL) { + /* Only failure mode of alloc_ust_app_event(). */ + ret = -ENOMEM; + goto end; + } + shadow_copy_event(ua_event, uevent); + + registry = get_session_registry(ua_sess); + if (!registry) { + DBG("Application session is being torn down. Abort event notify"); + ret = 0; + goto error; + } + + if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) { + map_reg_key = ua_map->tracing_map_id; + } else { + map_reg_key = ua_map->key; + } + + pthread_mutex_lock(®istry->lock); + ret = ust_registry_map_add_token_key_mapping(registry, map_reg_key, + uevent->attr.token, uevent->key); + assert(ret == 0); + pthread_mutex_unlock(®istry->lock); + + /* Create it on the tracer side */ + ret = create_ust_map_event(app, ua_sess, ua_map, uevent->key, ua_event); + 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 an event being created already existed: " + "event_name = \"%s\", pid = %d, ppid = %d, uid = %d, gid = %d", + uevent->attr.name, + app->pid, app->ppid, app->uid, + app->gid); + } + + /* + * FIXME: frdeso: remove key from tokey->key mapping. + */ + goto error; + } + + add_unique_ust_app_event(ua_map->events, ua_event); + + DBG2("UST app create event completed: app = '%s', tracer token = %"PRIu64" (ppid: %d)", + app->name, uevent->attr.token, app->ppid); + +end: + return ret; + +error: + /* Valid. Calling here is already in a read side lock */ + delete_ust_app_event(-1, ua_event, app); + return ret; +} + /* * Create UST app event notifier rule and create it on the tracer side. * @@ -3672,12 +5109,12 @@ int create_ust_app_event_notifier_rule(struct lttng_trigger *trigger, DBG2("UST app create token event rule completed: app = '%s' (ppid: %d), token = %" PRIu64, app->name, app->ppid, lttng_trigger_get_tracer_token(trigger)); -end: - return ret; + goto end; error: /* The RCU read side lock is already being held by the caller. */ delete_ust_app_event_notifier_rule(-1, ua_event_notifier_rule, app); +end: return ret; } @@ -3885,7 +5322,8 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) lta->v_major = msg->major; lta->v_minor = msg->minor; lta->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_U64); - lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + lta->ust_chan_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + lta->ust_map_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->token_to_event_notifier_rule_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); @@ -3992,6 +5430,7 @@ int ust_app_setup_event_notifier_group(struct ust_app *app) int event_pipe_write_fd; struct lttng_ust_object_data *event_notifier_group = NULL; enum lttng_error_code lttng_ret; + enum event_notifier_error_accounting_status event_notifier_error_accounting_status; assert(app); @@ -4040,6 +5479,14 @@ int ust_app_setup_event_notifier_group(struct ust_app *app) /* Assign handle only when the complete setup is valid. */ app->event_notifier_group.object = event_notifier_group; + + event_notifier_error_accounting_status = event_notifier_error_accounting_register_app(app); + if (event_notifier_error_accounting_status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Failed to setup event notifier error accounting for app"); + ret = -1; + goto error; + } + return ret; error: @@ -4575,24 +6022,27 @@ int ust_app_disable_channel_glb(struct ltt_ust_session *usess, } /* - * For a specific UST session, enable the channel for all registered apps. + * For a specific UST session, disable the channel for all registered apps. */ -int ust_app_enable_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) +int ust_app_disable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) { int ret = 0; struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_map_node; struct ust_app *app; struct ust_app_session *ua_sess; + struct ust_app_map *ua_map; assert(usess->active); - DBG2("UST app enabling channel %s to global domain for session id %" PRIu64, - uchan->name, usess->id); + DBG2("UST app disabling map %s from global domain for session id %" PRIu64, + umap->name, usess->id); rcu_read_lock(); /* For every registered applications */ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + struct lttng_ht_iter uiter; if (!app->compatible) { /* * TODO: In time, we should notice the caller of this error by @@ -4605,8 +6055,104 @@ int ust_app_enable_channel_glb(struct ltt_ust_session *usess, continue; } - /* Enable channel onto application */ - ret = enable_ust_app_channel(ua_sess, uchan, app); + /* Get map */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter); + ua_map_node = lttng_ht_iter_get_node_str(&uiter); + /* If the session if found for the app, the map must be there */ + assert(ua_map_node); + + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + /* The map must not be already disabled */ + assert(ua_map->enabled == 1); + + /* Disable map onto application */ + ret = disable_ust_app_map(ua_sess, ua_map, app); + if (ret < 0) { + /* XXX: We might want to report this error at some point... */ + continue; + } + } + + rcu_read_unlock(); + return ret; +} + +/* + * For a specific UST session, enable the channel for all registered apps. + */ +int ust_app_enable_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app *app; + struct ust_app_session *ua_sess; + + assert(usess->active); + DBG2("UST app enabling channel %s to global domain for session id %" PRIu64, + uchan->name, usess->id); + + rcu_read_lock(); + + /* For every registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + continue; + } + + /* Enable channel onto application */ + ret = enable_ust_app_channel(ua_sess, uchan, app); + if (ret < 0) { + /* XXX: We might want to report this error at some point... */ + continue; + } + } + + rcu_read_unlock(); + return ret; +} + +/* + * For a specific UST session, enable the map for all registered apps. + */ +int ust_app_enable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app *app; + struct ust_app_session *ua_sess; + + assert(usess->active); + DBG2("UST app enabling map %s to global domain for session id %" PRIu64, + umap->name, usess->id); + + rcu_read_lock(); + + /* For every registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + continue; + } + + /* Enable map onto application */ + ret = enable_ust_app_map(ua_sess, umap, app); if (ret < 0) { /* XXX: We might want to report this error at some point... */ continue; @@ -4620,7 +6166,7 @@ int ust_app_enable_channel_glb(struct ltt_ust_session *usess, /* * Disable an event in a channel and for a specific session. */ -int ust_app_disable_event_glb(struct ltt_ust_session *usess, +int ust_app_disable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { int ret = 0; @@ -4665,7 +6211,7 @@ int ust_app_disable_event_glb(struct ltt_ust_session *usess, ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name, uevent->filter, uevent->attr.loglevel, - uevent->exclusion); + uevent->exclusion, uevent->attr.token); if (ua_event == NULL) { DBG2("Event %s not found in channel %s for app pid %d." "Skipping", uevent->attr.name, uchan->name, app->pid); @@ -4683,6 +6229,72 @@ int ust_app_disable_event_glb(struct ltt_ust_session *usess, return ret; } +/* + * Disable an event in a map and for a specific session. + */ +int ust_app_disable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_map *ua_map; + struct ust_app_event *ua_event; + + assert(usess->active); + DBG("UST app disabling event %s for all apps in map " + "%s for session id %" PRIu64, + uevent->attr.name, umap->name, usess->id); + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + /* Next app */ + continue; + } + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter); + ua_map_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_map_node == NULL) { + DBG2("map %s not found in session id %" PRIu64 " for app pid %d." + "Skipping", umap->name, usess->id, app->pid); + continue; + } + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + ua_event = find_ust_app_event(ua_map->events, uevent->attr.name, + uevent->filter, uevent->attr.loglevel, + uevent->exclusion, uevent->attr.token); + if (ua_event == NULL) { + DBG2("Event %s not found in map %s for app pid %d." + "Skipping", uevent->attr.name, umap->name, app->pid); + continue; + } + + ret = disable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + /* XXX: Report error someday... */ + continue; + } + } + + rcu_read_unlock(); + return ret; +} + /* The ua_sess lock must be held by the caller. */ static int ust_app_channel_create(struct ltt_ust_session *usess, @@ -4759,10 +6371,67 @@ error: return ret; } +/* The ua_sess lock must be held by the caller. */ +static +int ust_app_map_create(struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, + struct ltt_ust_map *umap, struct ust_app *app, + struct ust_app_map **_ua_map) +{ + int ret = 0; + struct ust_app_map *ua_map = NULL; + + assert(ua_sess); + ASSERT_LOCKED(ua_sess->lock); + + /* + * Create map onto application and synchronize its + * configuration. + */ + ret = ust_app_map_allocate(ua_sess, umap, + LTTNG_UST_CHAN_PER_CPU, usess, + &ua_map); + if (ret < 0) { + goto error; + } + + ret = ust_app_map_send(app, usess, ua_sess, ua_map); + if (ret) { + goto error; + } + +error: + if (ret < 0) { + switch (ret) { + case -ENOTCONN: + /* + * The application's socket is not valid. Either a bad socket + * or a timeout on it. We can't inform the caller that for a + * specific app, the session failed so lets continue here. + */ + ret = 0; /* Not an error. */ + break; + case -ENOMEM: + default: + break; + } + } + + if (ret == 0 && _ua_map) { + /* + * Only return the application's map on success. Note + * that the map can still be part of the application's + * map hashtable on error. + */ + *_ua_map = ua_map; + } + return ret; +} + /* * Enable event for a specific session and channel on the tracer. */ -int ust_app_enable_event_glb(struct ltt_ust_session *usess, +int ust_app_enable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { int ret = 0; @@ -4800,68 +6469,747 @@ int ust_app_enable_event_glb(struct ltt_ust_session *usess, continue; } - pthread_mutex_lock(&ua_sess->lock); + pthread_mutex_lock(&ua_sess->lock); + + if (ua_sess->deleted) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + /* + * It is possible that the channel cannot be found is + * the channel/event creation occurs concurrently with + * an application exit. + */ + if (!ua_chan_node) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + /* Get event node */ + ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name, + uevent->filter, uevent->attr.loglevel, uevent->exclusion, + uevent->attr.token); + if (ua_event == NULL) { + DBG3("UST app enable event %s not found for app PID %d." + "Skipping app", uevent->attr.name, app->pid); + goto next_app; + } + + ret = enable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + pthread_mutex_unlock(&ua_sess->lock); + goto error; + } + next_app: + pthread_mutex_unlock(&ua_sess->lock); + } + +error: + rcu_read_unlock(); + return ret; +} + +/* + * Enable event for a specific session and map on the tracer. + */ +int ust_app_enable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_map *ua_map; + struct ust_app_event *ua_event; + + assert(usess->active); + DBG("UST app enabling event %s for all apps for session id %" PRIu64, + uevent->attr.name, usess->id); + + /* + * NOTE: At this point, this function is called only if the session and + * map passed are already created for all apps. and enabled on the + * tracer also. + */ + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (!ua_sess) { + /* The application has problem or is probably dead. */ + continue; + } + + pthread_mutex_lock(&ua_sess->lock); + + if (ua_sess->deleted) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter); + ua_map_node = lttng_ht_iter_get_node_str(&uiter); + /* + * It is possible that the map cannot be found is + * the map/event creation occurs concurrently with + * an application exit. + */ + if (!ua_map_node) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + /* Get event node */ + ua_event = find_ust_app_event(ua_map->events, uevent->attr.name, + uevent->filter, uevent->attr.loglevel, uevent->exclusion, + uevent->attr.token); + if (ua_event == NULL) { + DBG3("UST app enable event %s not found for app PID %d." + "Skipping app", uevent->attr.name, app->pid); + goto next_app; + } + + ret = enable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + pthread_mutex_unlock(&ua_sess->lock); + goto error; + } + next_app: + pthread_mutex_unlock(&ua_sess->lock); + } + +error: + rcu_read_unlock(); + return ret; +} + +/* + * For a specific existing UST session and UST channel, creates the event for + * all registered apps. + */ +int ust_app_create_channel_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *ua_chan_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + + assert(usess->active); + DBG("UST app creating event %s for all apps for session id %" PRIu64, + uevent->attr.name, usess->id); + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) { + if (!app->compatible) { + /* + * TODO: In time, we should notice the caller of this error by + * telling him that this is a version error. + */ + continue; + } + ua_sess = lookup_session_by_app(usess, app); + if (!ua_sess) { + /* The application has problem or is probably dead. */ + continue; + } + + pthread_mutex_lock(&ua_sess->lock); + + if (ua_sess->deleted) { + pthread_mutex_unlock(&ua_sess->lock); + continue; + } + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + /* If the channel is not found, there is a code flow error */ + assert(ua_chan_node); + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + ret = create_ust_app_channel_event(ua_sess, ua_chan, uevent, app); + pthread_mutex_unlock(&ua_sess->lock); + if (ret < 0) { + if (ret != -LTTNG_UST_ERR_EXIST) { + /* Possible value at this point: -ENOMEM. If so, we stop! */ + break; + } + DBG2("UST app event %s already exist on app PID %d", + uevent->attr.name, app->pid); + continue; + } + } + + rcu_read_unlock(); + return ret; +} + +static +int snapshot_key_values(struct ustctl_daemon_counter *map_handle, + struct lttng_ht *key_to_bucket_index_ht, int cpu, + const char *key_filter, struct lttng_ht *values) +{ + int ret; + struct lttng_ht_iter key_iter; + struct ust_registry_map_index_ht_entry *map_index_entry; + + /* Iterate over all the formated_key -> counter index */ + cds_lfht_for_each_entry(key_to_bucket_index_ht->ht, + &key_iter.iter, map_index_entry, node.node) { + bool overflow = 0, underflow = 0; + int64_t local_value = 0; + size_t dimension_indexes[1] = {map_index_entry->index}; + + if (key_filter && strcmp(key_filter, + map_index_entry->formated_key) != 0) { + continue; + } + + ret = ustctl_counter_read(map_handle, + dimension_indexes, cpu, &local_value, + &overflow, &underflow); + if (ret) { + ERR("Error getting counter value from the tracer: key = '%s'", + map_index_entry->formated_key); + ret = -1; + goto end; + } + + map_add_or_increment_map_values(values, + map_index_entry->formated_key, local_value, + underflow, overflow); + } + ret = 0; +end: + return ret; +} + +static +int ust_app_map_list_values_per_uid_with_bitness_and_cpu( + const struct ltt_ust_session *usess, + const struct ltt_ust_map *umap, + uint32_t app_bitness, + uint32_t cpu, + const char *key_filter, + struct lttng_ht *values) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct buffer_reg_uid *buf_reg_uid; + struct buffer_reg_map *buf_reg_map; + struct ust_registry_session *ust_reg_sess; + struct lttng_ht_node_u64 *ust_reg_map_node; + struct ust_registry_map *ust_reg_map; + + buf_reg_uid = buffer_reg_uid_find(usess->id, app_bitness, usess->uid); + if (!buf_reg_uid) { + /* + * Buffer registry entry for uid not found. Probably no app for + * this UID at the moment. + */ + DBG("No buffer registry entry found for uid: ust-sess-id = %"PRIu64", bitness = %"PRIu32", uid = %d", + usess->id, app_bitness, usess->uid); + /* + * Not an error. Leave the key value pair unchanged and return. + */ + ret = 0; + goto end; + } + + buf_reg_map = buffer_reg_map_find(umap->id, buf_reg_uid); + if (!buf_reg_uid) { + ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64, + umap->id); + ret = -1; + goto end; + } + + ust_reg_sess = buf_reg_uid->registry->reg.ust; + + /* Get the ust_reg map object from the registry */ + // FIXME: frdeso: This can be changed to ust_registry_map_find() right? + + lttng_ht_lookup(ust_reg_sess->maps, (void *) &umap->id, &iter); + ust_reg_map_node = lttng_ht_iter_get_node_u64(&iter); + if (!ust_reg_map_node) { + ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64, + umap->id); + ret = -1; + goto end; + } + ust_reg_map = caa_container_of(ust_reg_map_node, + struct ust_registry_map, node); + + ret = snapshot_key_values(buf_reg_map->daemon_counter, + ust_reg_map->key_string_to_bucket_index_ht, + cpu, key_filter, values); + if (ret) { + abort(); + } + + + ret = 0; +end: + return ret; +} + +static +int ust_app_map_list_values_per_uid(const struct ltt_ust_session *usess, + const struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content *map_content) +{ + int i, ret = 0; + enum lttng_map_query_status map_query_status; + const char *key_filter; + struct lttng_ht *values = NULL; + bool sum_cpus = lttng_map_query_get_config_sum_by_cpu(query); + enum lttng_map_query_config_buffer config_buffer; + enum lttng_map_query_config_cpu config_cpu; + int selected_cpu; + + map_query_status = lttng_map_query_get_key_filter(query, &key_filter); + if (map_query_status == LTTNG_MAP_QUERY_STATUS_NONE) { + key_filter = NULL; + } else if (map_query_status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + + config_cpu = lttng_map_query_get_config_cpu(query); + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int count; + map_query_status = lttng_map_query_get_cpu_count(query, &count); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + assert(count == 1); + + map_query_status = lttng_map_query_get_cpu_at_index(query, 0, + &selected_cpu); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + config_buffer = lttng_map_query_get_config_buffer(query); + if (config_buffer == LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET) { + unsigned int count; + uid_t selected_uid; + + map_query_status = lttng_map_query_get_uid_count(query, &count); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + assert(count == 1); + + map_query_status = lttng_map_query_get_uid_at_index(query, 0, + &selected_uid); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + + if (selected_uid != usess->uid) { + ret = 0; + goto end; + } + } + + if (sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + for (i = 0; i < umap->nr_cpu; i++) { + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + if (selected_cpu != i) { + continue; + } + } + + if (!sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + ret = ust_app_map_list_values_per_uid_with_bitness_and_cpu( + usess, umap, 32, i, key_filter, + values); + if (ret) { + abort(); + } + + ret = ust_app_map_list_values_per_uid_with_bitness_and_cpu( + usess, umap, 64, i, key_filter, + values); + if (ret) { + abort(); + } + if (!sum_cpus) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID, + sum_cpus, usess->uid, i, values); + if (ret) { + abort(); + } + + lttng_ht_destroy(values); + } + } + + if (sum_cpus) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID, + sum_cpus, usess->uid, 0, values); + if (ret) { + abort(); + } + lttng_ht_destroy(values); + } + +end: + return ret; +} + +static +int append_dead_app_kv(struct ltt_ust_map *umap, + const char *key_filter, + struct lttng_map_content *map_content) +{ + int ret; + struct lttng_ht *dead_app_kv_ht; + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_iter key_iter; + + struct lttng_ht *values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + + pthread_mutex_lock(&(umap->dead_app_kv_values.lock)); + + assert(umap->dead_app_kv_values.dead_app_kv_values_64bits); + dead_app_kv_ht = umap->dead_app_kv_values.dead_app_kv_values_64bits; + + cds_lfht_for_each_entry(dead_app_kv_ht->ht, &key_iter.iter, kv_entry, + node.node) { + if (key_filter && strcmp(key_filter, kv_entry->key) != 0) { + continue; + } + map_add_or_increment_map_values(values, kv_entry->key, + kv_entry->value, kv_entry->has_underflowed, + kv_entry->has_overflowed); + } + + assert(umap->dead_app_kv_values.dead_app_kv_values_32bits); + + dead_app_kv_ht = umap->dead_app_kv_values.dead_app_kv_values_32bits; + cds_lfht_for_each_entry(dead_app_kv_ht->ht, &key_iter.iter, kv_entry, + node.node) { + if (key_filter && strcmp(key_filter, kv_entry->key) != 0) { + continue; + } + map_add_or_increment_map_values(values, kv_entry->key, + kv_entry->value, kv_entry->has_underflowed, + kv_entry->has_overflowed); + } + + pthread_mutex_unlock(&umap->dead_app_kv_values.lock); + + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED, + true, 0, 0, values); + + lttng_ht_destroy(values); + if (ret) { + ERR("Error appending deadapp kv"); + goto end; + } + + + ret = 0; + +end: + return ret; +} + +static +int ust_app_map_list_values_per_pid_with_bitness_and_cpu( + const struct ltt_ust_session *usess, + struct ust_app *app, + struct ltt_ust_map *umap, + uint32_t app_bitness, + uint32_t cpu, + const char *key_filter, + struct lttng_ht *values) +{ + int ret = 0; + + struct lttng_ht_iter map_iter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app_map *ua_map; + struct ust_app_session *ua_sess; + struct ust_registry_session *ust_reg_sess; + struct ust_registry_map *ust_reg_map; + + if (app->bits_per_long != app_bitness) { + ret = 0; + goto end;; + } + + ua_sess = lookup_session_by_app(usess, app); + if (!ua_sess) { + /* Session not associated with this app. */ + ret = 0; + goto end;; + } + + ust_reg_sess = get_session_registry(ua_sess); + if (!ust_reg_sess) { + DBG("Application session is being torn down. Skip application."); + ret = 0; + goto end;; + } + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &map_iter); + ua_map_node = lttng_ht_iter_get_node_str(&map_iter); + + assert(ua_map_node != NULL); + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + pthread_mutex_lock(&ust_reg_sess->lock); + ust_reg_map = ust_registry_map_find(ust_reg_sess, ua_map->key); + pthread_mutex_unlock(&ust_reg_sess->lock); + assert(ust_reg_map); + + ret = snapshot_key_values(ua_map->map_handle, + ust_reg_map->key_string_to_bucket_index_ht, + cpu, key_filter, values); + if (ret) { + ERR("Error snapshoting the content of map"); + goto end; + } + +end: + return ret; +} + +static +int ust_app_map_list_values_per_pid(const struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content *map_content) +{ + enum lttng_map_query_status map_query_status; + const char *key_filter; + struct lttng_ht *values; + bool sum_cpus = lttng_map_query_get_config_sum_by_cpu(query); + bool sum_pids = lttng_map_query_get_config_sum_by_pid(query); + enum lttng_map_query_config_cpu config_cpu; + int selected_cpu, i, ret = 0; + struct lttng_ht_iter app_iter; + struct ust_app *app; + + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + + map_query_status = lttng_map_query_get_key_filter(query, &key_filter); + if (map_query_status == LTTNG_MAP_QUERY_STATUS_NONE) { + key_filter = NULL; + } else if (map_query_status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + + config_cpu = lttng_map_query_get_config_cpu(query); + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int count; + map_query_status = lttng_map_query_get_cpu_count(query, &count); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + assert(count == 1); + + map_query_status = lttng_map_query_get_cpu_at_index(query, 0, + &selected_cpu); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + /* Sum all cpus and pids on the same table. */ + if (sum_cpus && sum_pids) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + if (!sum_cpus && sum_pids) { + /* Iterate over all currently registered apps. */ + for (i = 0; i < umap->nr_cpu; i++) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + cds_lfht_for_each_entry(ust_app_ht->ht, &app_iter.iter, app, pid_n.node) { + ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu( + usess, app, umap, 32, i, key_filter, values); + if (ret) { + abort(); + } + ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu( + usess, app, umap, 64, i, key_filter, values); + if (ret) { + abort(); + } + } + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + sum_cpus, app->pid, i, values); + if (ret) { + abort(); + } + + lttng_ht_destroy(values); + } + } else { + /* Iterate over all currently registered apps. */ + cds_lfht_for_each_entry(ust_app_ht->ht, &app_iter.iter, app, pid_n.node) { + + if (sum_cpus && !sum_pids) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + for (i = 0; i < umap->nr_cpu; i++) { + + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + if (selected_cpu != i) { + continue; + } + } + + if (!sum_cpus && !sum_pids) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + } + + ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu( + usess, app, umap, 32, i, key_filter, values); + if (ret) { + abort(); + } + ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu( + usess, app, umap, 64, i, key_filter, values); + if (ret) { + abort(); + } + + if (!sum_cpus && !sum_pids) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + sum_cpus, app->pid, i, values); + if (ret) { + abort(); + } - if (ua_sess->deleted) { - pthread_mutex_unlock(&ua_sess->lock); - continue; + lttng_ht_destroy(values); + } + } + if (sum_cpus && !sum_pids) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + sum_cpus, app->pid, i, values); + if (ret) { + abort(); + } + + lttng_ht_destroy(values); + } } + } - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - /* - * It is possible that the channel cannot be found is - * the channel/event creation occurs concurrently with - * an application exit. - */ - if (!ua_chan_node) { - pthread_mutex_unlock(&ua_sess->lock); - continue; + if (sum_cpus && sum_pids) { + ret = map_new_content_section(map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID, + sum_cpus, 0, 0, values); + if (ret) { + abort(); } + lttng_ht_destroy(values); + } - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + /* Append dead app aggregated key-value pairs. */ + ret = append_dead_app_kv(umap, key_filter, map_content); + if (ret) { + ERR("Error appending values from dead apps map"); + goto end; + } - /* Get event node */ - ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name, - uevent->filter, uevent->attr.loglevel, uevent->exclusion); - if (ua_event == NULL) { - DBG3("UST app enable event %s not found for app PID %d." - "Skipping app", uevent->attr.name, app->pid); - goto next_app; - } +end: + return ret; +} - ret = enable_ust_app_event(ua_sess, ua_event, app); - if (ret < 0) { - pthread_mutex_unlock(&ua_sess->lock); - goto error; +int ust_app_map_list_values(const struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content **map_content) +{ + int ret; + struct lttng_map_content *local_map_content = NULL; + + local_map_content = lttng_map_content_create(usess->buffer_type); + if (!local_map_content) { + ERR("Error creating a map content list"); + ret = -1; + goto end; + } + rcu_read_lock(); + if (usess->buffer_type == LTTNG_BUFFER_PER_UID) { + ret = ust_app_map_list_values_per_uid(usess, umap, query, + local_map_content); + if (ret) { + ERR("Error adding per-uid map value"); + ret = -1; + goto end; + } + } else { + ret = ust_app_map_list_values_per_pid(usess, umap, query, + local_map_content); + if (ret) { + ERR("Error adding per-pid map value"); + ret = -1; + goto end; } - next_app: - pthread_mutex_unlock(&ua_sess->lock); } - -error: + *map_content = local_map_content; + local_map_content = NULL; + ret = 0; +end: rcu_read_unlock(); + + lttng_map_content_destroy(local_map_content); return ret; } /* - * For a specific existing UST session and UST channel, creates the event for + * For a specific existing UST session and UST map, creates the event for * all registered apps. */ -int ust_app_create_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +int ust_app_create_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) { int ret = 0; struct lttng_ht_iter iter, uiter; - struct lttng_ht_node_str *ua_chan_node; + struct lttng_ht_node_str *ua_map_node; struct ust_app *app; struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; + struct ust_app_map *ua_map; assert(usess->active); - DBG("UST app creating event %s for all apps for session id %" PRIu64, - uevent->attr.name, usess->id); + DBG("UST app creating event %s in map %s for all apps for session id %" PRIu64, + uevent->attr.name, umap->name, usess->id); rcu_read_lock(); @@ -4887,15 +7235,16 @@ int ust_app_create_event_glb(struct ltt_ust_session *usess, continue; } - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - /* If the channel is not found, there is a code flow error */ - assert(ua_chan_node); + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter); + ua_map_node = lttng_ht_iter_get_node_str(&uiter); + /* If the map is not found, there is a code flow error */ + assert(ua_map_node); - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); - ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); + ret = create_ust_app_map_event(ua_sess, ua_map, uevent, app); + assert(!ret); pthread_mutex_unlock(&ua_sess->lock); if (ret < 0) { if (ret != -LTTNG_UST_ERR_EXIST) { @@ -5190,7 +7539,7 @@ int ust_app_flush_session(struct ltt_ust_session *usess) /* Flush all per UID buffers associated to that session. */ cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { struct ust_registry_session *ust_session_reg; - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; /* Get consumer socket to use to push the metadata.*/ @@ -5202,13 +7551,13 @@ int ust_app_flush_session(struct ltt_ust_session *usess) } cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { + buf_reg_chan, node.node) { /* * The following call will print error values so the return * code is of little importance because whatever happens, we * have to try them all. */ - (void) consumer_flush_channel(socket, reg_chan->consumer_key); + (void) consumer_flush_channel(socket, buf_reg_chan->consumer_key); } ust_session_reg = reg->registry->reg.ust; @@ -5337,7 +7686,7 @@ int ust_app_clear_quiescent_session(struct ltt_ust_session *usess) */ cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { struct consumer_socket *socket; - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; /* Get associated consumer socket.*/ socket = consumer_find_socket_by_bitness( @@ -5351,7 +7700,7 @@ int ust_app_clear_quiescent_session(struct ltt_ust_session *usess) } cds_lfht_for_each_entry(reg->registry->channels->ht, - &iter.iter, reg_chan, node.node) { + &iter.iter, buf_reg_chan, node.node) { /* * The following call will print error values so * the return code is of little importance @@ -5359,7 +7708,7 @@ int ust_app_clear_quiescent_session(struct ltt_ust_session *usess) * all. */ (void) consumer_clear_quiescent_channel(socket, - reg_chan->consumer_key); + buf_reg_chan->consumer_key); } } break; @@ -5562,6 +7911,36 @@ end: return ret; } +/* The ua_sess lock must be held by the caller. */ +static +int find_or_create_ust_app_map( + struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, + struct ust_app *app, + struct ltt_ust_map *umap, + struct ust_app_map **ua_map) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_map_node; + + lttng_ht_lookup(ua_sess->maps, (void *) umap->name, &iter); + ua_map_node = lttng_ht_iter_get_node_str(&iter); + if (ua_map_node) { + *ua_map = caa_container_of(ua_map_node, + struct ust_app_map, node); + goto end; + } + + DBG("UST map id = %"PRIu64" not found. Creating it.", umap->id); + ret = ust_app_map_create(usess, ua_sess, umap, app, ua_map); + if (ret) { + goto end; + } +end: + return ret; +} + static int ust_app_channel_synchronize_event(struct ust_app_channel *ua_chan, struct ltt_ust_event *uevent, struct ust_app_session *ua_sess, @@ -5571,9 +7950,11 @@ int ust_app_channel_synchronize_event(struct ust_app_channel *ua_chan, struct ust_app_event *ua_event = NULL; ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name, - uevent->filter, uevent->attr.loglevel, uevent->exclusion); + uevent->filter, uevent->attr.loglevel, uevent->exclusion, + uevent->attr.token); if (!ua_event) { - ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); + ret = create_ust_app_channel_event(ua_sess, ua_chan, uevent, app); + if (ret < 0) { goto end; } @@ -5590,6 +7971,34 @@ end: } /* Called with RCU read-side lock held. */ +static +int ust_app_map_synchronize_event(struct ust_app_map *ua_map, + struct ltt_ust_event *uevent, struct ust_app_session *ua_sess, + struct ust_app *app) +{ + int ret = 0; + struct ust_app_event *ua_event = NULL; + + ua_event = find_ust_app_event(ua_map->events, uevent->attr.name, + uevent->filter, uevent->attr.loglevel, uevent->exclusion, + uevent->attr.token); + if (!ua_event) { + ret = create_ust_app_map_event(ua_sess, ua_map, uevent, app); + if (ret < 0) { + goto end; + } + } else { + if (ua_event->enabled != uevent->enabled) { + ret = uevent->enabled ? + enable_ust_app_event(ua_sess, ua_event, app) : + disable_ust_app_event(ua_sess, ua_event, app); + } + } + +end: + return ret; +} + static void ust_app_synchronize_event_notifier_rules(struct ust_app *app) { @@ -5635,8 +8044,8 @@ void ust_app_synchronize_event_notifier_rules(struct ust_app *app) } for (i = 0; i < count; i++) { - struct lttng_condition *condition; - struct lttng_event_rule *event_rule; + const struct lttng_condition *condition; + const struct lttng_event_rule *event_rule; struct lttng_trigger *trigger; const struct ust_app_event_notifier_rule *looked_up_event_notifier_rule; enum lttng_condition_status condition_status; @@ -5646,14 +8055,13 @@ void ust_app_synchronize_event_notifier_rules(struct ust_app *app) assert(trigger); token = lttng_trigger_get_tracer_token(trigger); - condition = lttng_trigger_get_condition(trigger); + condition = lttng_trigger_get_const_condition(trigger); - if (lttng_condition_get_type(condition) != LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) { - /* Does not apply */ + if (!lttng_trigger_needs_tracer_notifier(trigger)) { continue; } - condition_status = lttng_condition_event_rule_borrow_rule_mutable(condition, &event_rule); + condition_status = lttng_condition_on_event_get_rule(condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); if (lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL) { @@ -5733,38 +8141,20 @@ end: } /* - * The caller must ensure that the application is compatible and is tracked - * by the process attribute trackers. + * Called with RCU read-side lock held. */ static -void ust_app_synchronize(struct ltt_ust_session *usess, +void ust_app_synchronize_all_channels(struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, struct ust_app *app) { int ret = 0; struct cds_lfht_iter uchan_iter; struct ltt_ust_channel *uchan; - struct ust_app_session *ua_sess = NULL; - - /* - * The application's configuration should only be synchronized for - * active sessions. - */ - assert(usess->active); - ret = find_or_create_ust_app_session(usess, app, &ua_sess, NULL); - if (ret < 0) { - /* Tracer is probably gone or ENOMEM. */ - goto error; - } + assert(usess); assert(ua_sess); - - pthread_mutex_lock(&ua_sess->lock); - if (ua_sess->deleted) { - pthread_mutex_unlock(&ua_sess->lock); - goto end; - } - - rcu_read_lock(); + assert(app); cds_lfht_for_each_entry(usess->domain_global.channels->ht, &uchan_iter, uchan, node.node) { @@ -5783,7 +8173,7 @@ void ust_app_synchronize(struct ltt_ust_session *usess, app, uchan, &ua_chan); if (ret) { /* Tracer is probably gone or ENOMEM. */ - goto error_unlock; + goto end; } if (!ua_chan) { @@ -5796,7 +8186,7 @@ void ust_app_synchronize(struct ltt_ust_session *usess, ret = ust_app_channel_synchronize_event(ua_chan, uevent, ua_sess, app); if (ret) { - goto error_unlock; + goto end; } } @@ -5805,10 +8195,106 @@ void ust_app_synchronize(struct ltt_ust_session *usess, enable_ust_app_channel(ua_sess, uchan, app) : disable_ust_app_channel(ua_sess, ua_chan, app); if (ret) { - goto error_unlock; + goto end; + } + } + } +end: + return; +} + +/* + * Called with RCU read-side lock held. + */ +static +void ust_app_synchronize_all_maps(struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, + struct ust_app *app) +{ + int ret = 0; + struct cds_lfht_iter umap_iter; + struct ltt_ust_map *umap; + + assert(usess); + assert(ua_sess); + assert(app); + + cds_lfht_for_each_entry(usess->domain_global.maps->ht, &umap_iter, + umap, node.node) { + struct ust_app_map *ua_map; + struct cds_lfht_iter uevent_iter; + struct ltt_ust_event *uevent; + + DBG("Synchronizing UST map id = %"PRIu64, umap->id); + + ret = find_or_create_ust_app_map(usess, ua_sess, + app, umap, &ua_map); + if (ret) { + /* Tracer is probably gone or ENOMEM. */ + goto end; + } + + DBG("Synchronizing all events of UST map id = %"PRIu64, umap->id); + cds_lfht_for_each_entry(umap->events->ht, &uevent_iter, uevent, + node.node) { + ret = ust_app_map_synchronize_event(ua_map, + uevent, ua_sess, app); + if (ret) { + goto end; + } + } + + if (ua_map->enabled != umap->enabled) { + if (umap->enabled) { + DBG("Map disabled on the tracer side but shouldn't"); + ret = enable_ust_app_map(ua_sess, umap, app); + } else { + DBG("Map enabled on the tracer side but shouldn't"); + ret = disable_ust_app_map(ua_sess, ua_map, app); + } + if (ret) { + goto end; } } } +end: + return; +} + +/* + * The caller must ensure that the application is compatible and is tracked + * by the process attribute trackers. + */ +static +void ust_app_synchronize(struct ltt_ust_session *usess, + struct ust_app *app) +{ + int ret = 0; + struct ust_app_session *ua_sess = NULL; + + /* + * The application's configuration should only be synchronized for + * active sessions. + */ + assert(usess->active); + + ret = find_or_create_ust_app_session(usess, app, &ua_sess, NULL); + if (ret < 0) { + /* Tracer is probably gone or ENOMEM. */ + goto error; + } + assert(ua_sess); + + + rcu_read_lock(); + + pthread_mutex_lock(&ua_sess->lock); + if (ua_sess->deleted) { + pthread_mutex_unlock(&ua_sess->lock); + goto end; + } + ust_app_synchronize_all_channels(usess, ua_sess, app); + ust_app_synchronize_all_maps(usess, ua_sess, app); /* * Create the metadata for the application. This returns gracefully if a @@ -5941,6 +8427,20 @@ void ust_app_global_update_all_event_notifier_rules(void) rcu_read_unlock(); } +void ust_app_update_event_notifier_error_count(struct lttng_trigger *trigger) +{ + uint64_t error_count = 0; + enum event_notifier_error_accounting_status status; + struct lttng_condition *condition = lttng_trigger_get_condition(trigger); + + status = event_notifier_error_accounting_get_count(trigger, &error_count); + if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error getting trigger error count."); + } + + lttng_condition_on_event_set_error_count(condition, error_count); +} + /* * Add context to a specific channel for global UST domain. */ @@ -6070,34 +8570,61 @@ static struct ust_app_session *find_session_by_objd(struct ust_app *app, ua_sess = caa_container_of(node, struct ust_app_session, ust_objd_node); error: - return ua_sess; + return ua_sess; +} + +/* + * Return a ust app channel object using the application object and the channel + * object descriptor has a key. If not found, NULL is returned. A RCU read side + * lock MUST be acquired before calling this function. + */ +static struct ust_app_channel *find_channel_by_objd(struct ust_app *app, + int objd) +{ + struct lttng_ht_node_ulong *node; + struct lttng_ht_iter iter; + struct ust_app_channel *ua_chan = NULL; + + assert(app); + + lttng_ht_lookup(app->ust_chan_objd, (void *)((unsigned long) objd), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + DBG2("UST app channel find by objd %d not found", objd); + goto error; + } + + ua_chan = caa_container_of(node, struct ust_app_channel, ust_objd_node); + +error: + return ua_chan; } /* - * Return a ust app channel object using the application object and the channel + * Return a ust app map object using the application object and the map * object descriptor has a key. If not found, NULL is returned. A RCU read side * lock MUST be acquired before calling this function. */ -static struct ust_app_channel *find_channel_by_objd(struct ust_app *app, +static struct ust_app_map *find_map_by_objd(struct ust_app *app, int objd) { struct lttng_ht_node_ulong *node; struct lttng_ht_iter iter; - struct ust_app_channel *ua_chan = NULL; + struct ust_app_map *ua_map = NULL; assert(app); - lttng_ht_lookup(app->ust_objd, (void *)((unsigned long) objd), &iter); + lttng_ht_lookup(app->ust_map_objd, (void *)((unsigned long) objd), &iter); node = lttng_ht_iter_get_node_ulong(&iter); if (node == NULL) { - DBG2("UST app channel find by objd %d not found", objd); + DBG2("UST app map find by objd %d not found", objd); goto error; } - ua_chan = caa_container_of(node, struct ust_app_channel, ust_objd_node); + ua_map = caa_container_of(node, struct ust_app_map, ust_objd_node); error: - return ua_chan; + return ua_map; } /* @@ -6119,7 +8646,7 @@ static int reply_ust_register_channel(int sock, int cobjd, struct ust_app_channel *ua_chan; struct ust_app_session *ua_sess; struct ust_registry_session *registry; - struct ust_registry_channel *chan_reg; + struct ust_registry_channel *ust_reg_chan; rcu_read_lock(); @@ -6160,30 +8687,30 @@ static int reply_ust_register_channel(int sock, int cobjd, pthread_mutex_lock(®istry->lock); - chan_reg = ust_registry_channel_find(registry, chan_reg_key); - assert(chan_reg); + ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key); + assert(ust_reg_chan); - if (!chan_reg->register_done) { + if (!ust_reg_chan->register_done) { /* * TODO: eventually use the registry event count for * this channel to better guess header type for per-pid * buffers. */ type = USTCTL_CHANNEL_HEADER_LARGE; - chan_reg->nr_ctx_fields = nr_fields; - chan_reg->ctx_fields = fields; + ust_reg_chan->nr_ctx_fields = nr_fields; + ust_reg_chan->ctx_fields = fields; fields = NULL; - chan_reg->header_type = type; + ust_reg_chan->header_type = type; } else { /* Get current already assigned values. */ - type = chan_reg->header_type; + type = ust_reg_chan->header_type; } /* Channel id is set during the object creation. */ - chan_id = chan_reg->chan_id; + chan_id = ust_reg_chan->chan_id; /* Append to metadata */ - if (!chan_reg->metadata_dumped) { - ret_code = ust_metadata_channel_statedump(registry, chan_reg); + if (!ust_reg_chan->metadata_dumped) { + ret_code = ust_metadata_channel_statedump(registry, ust_reg_chan); if (ret_code) { ERR("Error appending channel metadata (errno = %d)", ret_code); goto reply; @@ -6206,7 +8733,7 @@ reply: } /* This channel registry registration is completed. */ - chan_reg->register_done = 1; + ust_reg_chan->register_done = 1; error: pthread_mutex_unlock(®istry->lock); @@ -6216,45 +8743,21 @@ error_rcu_unlock: return ret; } -/* - * Add event to the UST channel registry. When the event is added to the - * registry, the metadata is also created. Once done, this replies to the - * application with the appropriate error code. - * - * The session UST registry lock is acquired in the function. - * - * On success 0 is returned else a negative value. - */ -static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, +static int add_event_ust_chan_registry(int sock, struct ust_app *ua, + struct ust_app_channel *ua_chan, int sobjd, int cobjd, char *name, char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel_value, char *model_emf_uri) { int ret, ret_code; uint32_t event_id = 0; uint64_t chan_reg_key; - struct ust_app *app; - struct ust_app_channel *ua_chan; struct ust_app_session *ua_sess; struct ust_registry_session *registry; - - rcu_read_lock(); - - /* Lookup application. If not found, there is a code flow error. */ - app = find_app_by_notify_sock(sock); - if (!app) { - DBG("Application socket %d is being torn down. Abort event notify", - sock); - ret = 0; - goto error_rcu_unlock; - } - - /* Lookup channel by UST object descriptor. */ - ua_chan = find_channel_by_objd(app, cobjd); - if (!ua_chan) { - DBG("Application channel is being torn down. Abort event notify"); - ret = 0; - goto error_rcu_unlock; - } + /* + * The counter index is unused for channel events. It's only used for + * map events. + */ + uint64_t counter_index = 0; assert(ua_chan->session); ua_sess = ua_chan->session; @@ -6263,7 +8766,7 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, if (!registry) { DBG("Application session is being torn down. Abort event notify"); ret = 0; - goto error_rcu_unlock; + goto error; } if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) { @@ -6279,10 +8782,10 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, * and model_emf_uri meaning any free are done inside it if needed. These * three variables MUST NOT be read/write after this. */ - ret_code = ust_registry_create_event(registry, chan_reg_key, + ret_code = ust_registry_chan_create_event(registry, chan_reg_key, sobjd, cobjd, name, sig, nr_fields, fields, loglevel_value, model_emf_uri, ua_sess->buffer_type, - &event_id, app); + &event_id, ua); sig = NULL; fields = NULL; model_emf_uri = NULL; @@ -6292,7 +8795,7 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, * application can be notified. In case of an error, it's important not to * return a negative error or else the application will get closed. */ - ret = ustctl_reply_register_event(sock, event_id, ret_code); + ret = ustctl_reply_register_event(sock, event_id, counter_index, ret_code); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { ERR("UST app reply event failed with ret %d", ret); @@ -6311,11 +8814,148 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, error: pthread_mutex_unlock(®istry->lock); -error_rcu_unlock: + return ret; +} + +static int add_event_ust_map_registry(int sock, struct ust_app *ua, + struct ust_app_map *ua_map, int sobjd, int cobjd, char *name, + char *sig, size_t nr_fields, struct ustctl_field *fields, + int loglevel_value, char *model_emf_uri, uint64_t tracer_token) +{ + int ret, ret_code; + uint64_t map_reg_key, counter_index; + struct ust_app_session *ua_sess; + struct ust_registry_session *registry; + + assert(ua_map->session); + ua_sess = ua_map->session; + + registry = get_session_registry(ua_sess); + if (!registry) { + DBG("Application session is being torn down. Abort event notify"); + ret = 0; + goto error; + } + + if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) { + map_reg_key = ua_map->tracing_map_id; + } else { + map_reg_key = ua_map->key; + } + + pthread_mutex_lock(®istry->lock); + + /* + * From this point on, this call acquires the ownership of the sig, fields + * and model_emf_uri meaning any free are done inside it if needed. These + * three variables MUST NOT be read/write after this. + */ + DBG("Registry_map_create_event on map=%"PRIu64" with token=%"PRIu64, + map_reg_key, tracer_token); + ret_code = ust_registry_map_create_event(registry, map_reg_key, + sobjd, cobjd, name, sig, nr_fields, fields, + loglevel_value, model_emf_uri, ua_sess->buffer_type, + tracer_token, &counter_index, ua); + assert(!ret_code); + + sig = NULL; + fields = NULL; + model_emf_uri = NULL; + + /* + * The return value is returned to ustctl so in case of an error, the + * application can be notified. In case of an error, it's important not to + * return a negative error or else the application will get closed. + */ + ret = ustctl_reply_register_event(sock, counter_index, counter_index, + ret_code); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app reply event failed with ret %d", ret); + } else { + DBG3("UST app reply event failed. Application died"); + } + /* + * No need to wipe the create event since the application socket will + * get close on error hence cleaning up everything by itself. + */ + goto error; + } + + DBG3("UST registry map event %s with counter index %" PRIu64 " added successfully", + name, counter_index); + +error: + pthread_mutex_unlock(®istry->lock); + return ret; +} + + +/* + * Add event to the UST channel registry. When the event is added to the + * registry, the metadata is also created. Once done, this replies to the + * application with the appropriate error code. + * + * The session UST registry lock is acquired in the function. + * + * On success 0 is returned else a negative value. + */ +static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, + char *sig, size_t nr_fields, struct ustctl_field *fields, + int loglevel_value, char *model_emf_uri, uint64_t tracer_token) +{ + int ret; + struct ust_app *app; + struct ust_app_channel *ua_chan = NULL; + struct ust_app_map *ua_map = NULL; + + rcu_read_lock(); + + /* Lookup application. If not found, there is a code flow error. */ + app = find_app_by_notify_sock(sock); + if (!app) { + DBG("Application socket %d is being torn down. Abort event notify", + sock); + ret = 0; + goto end; + } + + /* Lookup channel by UST object descriptor. */ + ua_chan = find_channel_by_objd(app, cobjd); + if (ua_chan) { + ret = add_event_ust_chan_registry(sock, app, ua_chan, sobjd, cobjd, + name, sig, nr_fields, fields, loglevel_value, + model_emf_uri); + if (ret) { + ERR("Error adding channel event to registry: event_name = '%s'", name); + } + goto found; + } + + /* Lookup map by UST object descriptor. */ + ua_map = find_map_by_objd(app, cobjd); + if (ua_map) { + ret = add_event_ust_map_registry(sock, app, ua_map, sobjd, cobjd, + name, sig, nr_fields, fields, loglevel_value, + model_emf_uri, tracer_token); + if (ret) { + ERR("Error adding map event to registry: event_name = '%s'", name); + goto end; + } + goto found; + } + + if (!ua_chan && !ua_map) { + DBG("Application event container is being torn down. Abort event notify"); + ret = 0; + goto end; + } + +found: + ret = 0; + +end: rcu_read_unlock(); - free(sig); - free(fields); - free(model_emf_uri); return ret; } @@ -6431,13 +9071,14 @@ int ust_app_recv_notify(int sock) int sobjd, cobjd, loglevel_value; char name[LTTNG_UST_SYM_NAME_LEN], *sig, *model_emf_uri; size_t nr_fields; + uint64_t tracer_token = 0; struct ustctl_field *fields; DBG2("UST app ustctl register event received"); ret = ustctl_recv_register_event(sock, &sobjd, &cobjd, name, &loglevel_value, &sig, &nr_fields, &fields, - &model_emf_uri); + &model_emf_uri, &tracer_token); if (ret < 0) { if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { ERR("UST app recv event failed with ret %d", ret); @@ -6454,7 +9095,7 @@ int ust_app_recv_notify(int sock) * to the this function. */ ret = add_event_ust_registry(sock, sobjd, cobjd, name, sig, nr_fields, - fields, loglevel_value, model_emf_uri); + fields, loglevel_value, model_emf_uri, tracer_token); if (ret < 0) { goto error; } @@ -6648,7 +9289,7 @@ enum lttng_error_code ust_app_snapshot_record( struct buffer_reg_uid *reg; cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; char pathname[PATH_MAX]; size_t consumer_path_offset = 0; @@ -6685,9 +9326,9 @@ enum lttng_error_code ust_app_snapshot_record( } /* Add the UST default trace dir to path. */ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { + buf_reg_chan, node.node) { status = consumer_snapshot_channel(socket, - reg_chan->consumer_key, + buf_reg_chan->consumer_key, output, 0, usess->uid, usess->gid, &trace_path[consumer_path_offset], wait, nb_packets_per_stream); @@ -6815,19 +9456,19 @@ uint64_t ust_app_get_size_one_more_packet_per_stream( struct buffer_reg_uid *reg; cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; rcu_read_lock(); cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { - if (cur_nr_packets >= reg_chan->num_subbuf) { + buf_reg_chan, node.node) { + if (cur_nr_packets >= buf_reg_chan->num_subbuf) { /* * Don't take channel into account if we * already grab all its packets. */ continue; } - tot_size += reg_chan->subbuf_size * reg_chan->stream_count; + tot_size += buf_reg_chan->subbuf_size * buf_reg_chan->stream_count; } rcu_read_unlock(); } @@ -7051,7 +9692,7 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) struct buffer_reg_uid *reg; cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; if (!reg->registry->reg.ust->metadata_key) { @@ -7069,9 +9710,9 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) /* Rotate the data channels. */ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { + buf_reg_chan, node.node) { ret = consumer_rotate_channel(socket, - reg_chan->consumer_key, + buf_reg_chan->consumer_key, usess->uid, usess->gid, usess->consumer, /* is_metadata_channel */ false); @@ -7284,7 +9925,8 @@ error: * * Return LTTNG_OK on success or else an LTTng error code. */ -enum lttng_error_code ust_app_clear_session(struct ltt_session *session) +static +enum lttng_error_code ust_app_clear_session_channels(struct ltt_session *session) { int ret; enum lttng_error_code cmd_ret = LTTNG_OK; @@ -7308,7 +9950,7 @@ enum lttng_error_code ust_app_clear_session(struct ltt_session *session) struct buffer_reg_uid *reg; cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; /* Get consumer socket to use to push the metadata.*/ @@ -7321,9 +9963,9 @@ enum lttng_error_code ust_app_clear_session(struct ltt_session *session) /* Clear the data channels. */ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter, - reg_chan, node.node) { + buf_reg_chan, node.node) { ret = consumer_clear_channel(socket, - reg_chan->consumer_key); + buf_reg_chan->consumer_key); if (ret < 0) { goto error; } @@ -7427,6 +10069,228 @@ end: return cmd_ret; } +static +enum lttng_error_code ust_app_clear_session_maps_per_uid( + struct ltt_ust_session *usess, struct ltt_ust_map *umap, + uint32_t app_bitness) +{ + struct lttng_ht_iter iter; + struct buffer_reg_uid *buf_reg_uid; + struct buffer_reg_map *buf_reg_map; + struct ust_registry_session *ust_reg_sess; + struct lttng_ht_node_u64 *ust_reg_map_node; + struct ust_registry_map *ust_reg_map; + struct ust_registry_map_index_ht_entry *map_index_entry; + enum lttng_error_code status; + + buf_reg_uid = buffer_reg_uid_find(usess->id, app_bitness, usess->uid); + if (!buf_reg_uid) { + /* + * Buffer registry entry for uid not found. Probably no app for + * this UID at the moment. + */ + DBG("No buffer registry entry found for uid: ust-sess-id = %"PRIu64", bitness = %"PRIu32", uid = %d", + usess->id, app_bitness, usess->uid); + /* + * Not an error. Leave the key value pair unchanged and return. + */ + status = LTTNG_OK; + goto end; + } + + buf_reg_map = buffer_reg_map_find(umap->id, buf_reg_uid); + if (!buf_reg_uid) { + ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64, + umap->id); + status = LTTNG_ERR_UNK; + goto end; + } + + ust_reg_sess = buf_reg_uid->registry->reg.ust; + + /* Get the ust_reg map object from the registry */ + // FIXME: frdeso: This can be changed to ust_registry_map_find() right? + + lttng_ht_lookup(ust_reg_sess->maps, (void *) &umap->id, &iter); + ust_reg_map_node = lttng_ht_iter_get_node_u64(&iter); + if (!ust_reg_map_node) { + ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64, + umap->id); + status = LTTNG_ERR_UNK; + goto end; + } + ust_reg_map = caa_container_of(ust_reg_map_node, + struct ust_registry_map, node); + + cds_lfht_for_each_entry(ust_reg_map->key_string_to_bucket_index_ht->ht, + &iter.iter, map_index_entry, node.node) { + int ret; + size_t dimension_indexes[1] = {map_index_entry->index}; + + ret = ustctl_counter_clear(buf_reg_map->daemon_counter, dimension_indexes); + if (ret) { + ERR("clearing counter index %"PRIu64, map_index_entry->index); + //fixme: frdeso: convert ust errors to tools errors + status = LTTNG_ERR_UNK; + goto end; + } + } + + status = LTTNG_OK; + +end: + return status; +} + +static +enum lttng_error_code ust_app_clear_session_maps_per_pid( + struct ltt_ust_session *usess, struct ltt_ust_map *umap, + uint32_t app_bitness) +{ + struct lttng_ht_iter app_iter; + enum lttng_error_code status; + struct ust_app *app; + struct map_kv_ht_entry *kv_entry; + struct lttng_ht_iter iter; + + cds_lfht_for_each_entry(ust_app_ht->ht, &app_iter.iter, app, pid_n.node) { + struct lttng_ht_iter map_iter, key_iter; + struct lttng_ht_node_str *ua_map_node; + struct ust_app_map *ua_map; + struct ust_app_session *ua_sess; + struct ust_registry_session *ust_reg_sess; + struct ust_registry_map *ust_reg_map; + struct ust_registry_map_index_ht_entry *map_index_entry; + + if (app->bits_per_long != app_bitness) { + continue; + } + + ua_sess = lookup_session_by_app(usess, app); + if (!ua_sess) { + /* Session not associated with this app. */ + continue; + } + + ust_reg_sess = get_session_registry(ua_sess); + if (!ust_reg_sess) { + DBG("Application session is being torn down. Skip application."); + continue; + } + + /* Lookup map in the ust app session */ + lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &map_iter); + ua_map_node = lttng_ht_iter_get_node_str(&map_iter); + + assert(ua_map_node != NULL); + ua_map = caa_container_of(ua_map_node, struct ust_app_map, node); + + pthread_mutex_lock(&ust_reg_sess->lock); + ust_reg_map = ust_registry_map_find(ust_reg_sess, ua_map->key); + pthread_mutex_unlock(&ust_reg_sess->lock); + assert(ust_reg_map); + + /* Iterate over all the formated_key -> counter index */ + cds_lfht_for_each_entry(ust_reg_map->key_string_to_bucket_index_ht->ht, + &key_iter.iter, map_index_entry, node.node) { + + int ret; + size_t dimension_indexes[1] = {map_index_entry->index}; + + ret = ustctl_counter_clear(ua_map->map_handle, + dimension_indexes); + if (ret) { + ERR("clearing counter index %"PRIu64, map_index_entry->index); + //fixme: frdeso: convert ust errors to tools errors + status = LTTNG_ERR_UNK; + goto end; + } + } + } + + /* + * Emptying the dead app key values. + */ + pthread_mutex_lock(&umap->dead_app_kv_values.lock); + + if (app_bitness == 32) { + cds_lfht_for_each_entry(umap->dead_app_kv_values.dead_app_kv_values_32bits->ht, + &iter.iter, kv_entry, node.node) { + kv_entry->value = 0; + } + } else { + + cds_lfht_for_each_entry(umap->dead_app_kv_values.dead_app_kv_values_64bits->ht, + &iter.iter, kv_entry, node.node) { + kv_entry->value = 0; + } + } + + pthread_mutex_unlock(&umap->dead_app_kv_values.lock); + + status = LTTNG_OK; +end: + return status; +} + +static +enum lttng_error_code ust_app_clear_session_maps(struct ltt_session *session) +{ + struct ltt_ust_session *usess = session->ust_session; + enum lttng_error_code status; + struct lttng_ht_iter iter; + struct ltt_ust_map *umap; + + cds_lfht_for_each_entry(usess->domain_global.maps->ht, &iter.iter, + umap, node.node) { + + if (usess->buffer_type == LTTNG_BUFFER_PER_UID) { + status = ust_app_clear_session_maps_per_uid(session->ust_session, + umap, 32); + assert(status == LTTNG_OK); + + status = ust_app_clear_session_maps_per_uid(session->ust_session, + umap, 64); + assert(status == LTTNG_OK); + //fixme:frdeso:error handling + } else { + status = ust_app_clear_session_maps_per_pid(session->ust_session, + umap, 32); + assert(status == LTTNG_OK); + + status = ust_app_clear_session_maps_per_pid(session->ust_session, + umap, 64); + assert(status == LTTNG_OK); + //fixme:frdeso:error handling + // + // + } + + } + + return LTTNG_OK; +} + +enum lttng_error_code ust_app_clear_session(struct ltt_session *session) +{ + enum lttng_error_code cmd_ret; + + + cmd_ret = ust_app_clear_session_channels(session); + if (cmd_ret != LTTNG_OK) { + ERR("Clearing session's channels"); + goto end; + } + + cmd_ret = ust_app_clear_session_maps(session); + if (cmd_ret != LTTNG_OK) { + ERR("Clearing session's maps"); + goto end; + } +end: + return cmd_ret; +} + /* * This function skips the metadata channel as the begin/end timestamps of a * metadata packet are useless. @@ -7460,7 +10324,7 @@ enum lttng_error_code ust_app_open_packets(struct ltt_session *session) cds_list_for_each_entry ( reg, &usess->buffer_reg_uid_list, lnode) { - struct buffer_reg_channel *reg_chan; + struct buffer_reg_channel *buf_reg_chan; struct consumer_socket *socket; socket = consumer_find_socket_by_bitness( @@ -7471,11 +10335,11 @@ enum lttng_error_code ust_app_open_packets(struct ltt_session *session) } cds_lfht_for_each_entry(reg->registry->channels->ht, - &iter.iter, reg_chan, node.node) { + &iter.iter, buf_reg_chan, node.node) { const int open_ret = consumer_open_channel_packets( socket, - reg_chan->consumer_key); + buf_reg_chan->consumer_key); if (open_ret < 0) { ret = LTTNG_ERR_UNK; diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h index 29024b4de..5325c64fc 100644 --- a/src/bin/lttng-sessiond/ust-app.h +++ b/src/bin/lttng-sessiond/ust-app.h @@ -11,7 +11,9 @@ #include +#include #include +#include #include "trace-ust.h" #include "ust-registry.h" @@ -42,6 +44,7 @@ struct ust_app_ht_key { const struct lttng_bytecode *filter; enum lttng_ust_loglevel_type loglevel_type; const struct lttng_event_exclusion *exclusion; + uint64_t tracer_token; }; /* @@ -93,6 +96,12 @@ struct ust_app_stream_list { struct cds_list_head head; }; +/* counter list containing ust_app_counter. */ +struct ust_app_counter_list { + unsigned int count; + struct cds_list_head head; +}; + struct ust_app_ctx { int handle; struct lttng_ust_context_attr ctx; @@ -114,6 +123,7 @@ struct ust_app_event { struct ust_app_event_notifier_rule { int enabled; + uint64_t error_counter_index; int handle; struct lttng_ust_object_data *obj; /* Holds a strong reference. */ @@ -139,6 +149,13 @@ struct ust_app_stream { struct cds_list_head list; }; +struct ust_app_map_counter { + int handle; + struct lttng_ust_object_data *obj; + /* Using a list of counters to keep order. */ + struct cds_list_head list; +}; + struct ust_app_channel { int enabled; int handle; @@ -185,6 +202,43 @@ struct ust_app_channel { struct rcu_head rcu_head; }; +struct ust_app_map { + int enabled; + int handle; + /* Counters were sent to the UST tracer. */ + int is_sent; + /* + * FIXME frdeso: what is difference between key and tracing_map_id + * Unique key used to identify the map. + */ + uint64_t key; + /* Id of the tracing map set on creation. */ + uint64_t tracing_map_id; + bool coalesce_hits; + enum lttng_map_bitness bitness; + char name[LTTNG_UST_SYM_NAME_LEN]; + struct lttng_ust_object_data *obj; + struct ust_app_counter_list counters; + /* Session pointer that owns this object. */ + struct ust_app_session *session; + struct lttng_ht *events; + struct ltt_ust_map_dead_pid_kv_values *dead_app_kv_values; + + size_t bucket_count; + struct ustctl_daemon_counter *map_handle; + /* + * Node indexed by channel name in the channels' hash table of a session. + */ + struct lttng_ht_node_str node; + /* + * Node indexed by UST channel object descriptor (handle). Stored in the + * ust_objd hash table in the ust_app object. + */ + struct lttng_ht_node_ulong ust_objd_node; + /* For delayed reclaim */ + struct rcu_head rcu_head; +}; + struct ust_app_session { /* * Lock protecting this session's ust app interaction. Held @@ -207,6 +261,7 @@ struct ust_app_session { uint64_t tracing_id; uint64_t id; /* Unique session identifier */ struct lttng_ht *channels; /* Registered channels */ + struct lttng_ht *maps; /* Registered maps */ struct lttng_ht_node_u64 node; /* * Node indexed by UST session object descriptor (handle). Stored in the @@ -291,7 +346,11 @@ struct ust_app { /* * Hash table containing ust_app_channel indexed by channel objd. */ - struct lttng_ht *ust_objd; + struct lttng_ht *ust_chan_objd; + /* + * Hash table containing ust_app_map indexed by map objd. + */ + struct lttng_ht *ust_map_objd; /* * Hash table containing ust_app_session indexed by objd. */ @@ -319,6 +378,9 @@ struct ust_app { */ struct lttng_ust_object_data *object; struct lttng_pipe *event_pipe; + struct lttng_ust_object_data *counter; + struct lttng_ust_object_data **counter_cpu; + int nr_counter_cpu; } event_notifier_group; /* * Hashtable indexing the application's event notifier rule's @@ -338,16 +400,31 @@ int ust_app_stop_trace_all(struct ltt_ust_session *usess); int ust_app_destroy_trace_all(struct ltt_ust_session *usess); int ust_app_list_events(struct lttng_event **events); int ust_app_list_event_fields(struct lttng_event_field **fields); -int ust_app_create_event_glb(struct ltt_ust_session *usess, +int ust_app_create_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); int ust_app_disable_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan); int ust_app_enable_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan); -int ust_app_enable_event_glb(struct ltt_ust_session *usess, +int ust_app_enable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); -int ust_app_disable_event_glb(struct ltt_ust_session *usess, +int ust_app_disable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); +int ust_app_map_list_values(const struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content **map_content); +int ust_app_enable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); +int ust_app_disable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap); +int ust_app_create_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent); +int ust_app_enable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent); +int ust_app_disable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent); + 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); @@ -355,10 +432,13 @@ void ust_app_global_update_all(struct ltt_ust_session *usess); void ust_app_global_update_event_notifier_rules(struct ust_app *app); void ust_app_global_update_all_event_notifier_rules(void); +void ust_app_update_event_notifier_error_count(struct lttng_trigger *trigger); + void ust_app_clean_list(void); int ust_app_ht_alloc(void); struct ust_app *ust_app_find_by_pid(pid_t pid); struct ust_app_stream *ust_app_alloc_stream(void); +struct ust_app_map_counter *ust_app_alloc_map_counter(void); int ust_app_recv_registration(int sock, struct ust_register_msg *msg); int ust_app_recv_notify(int sock); void ust_app_add(struct ust_app *app); @@ -504,24 +584,62 @@ int ust_app_enable_channel_glb(struct ltt_ust_session *usess, return 0; } static inline -int ust_app_create_event_glb(struct ltt_ust_session *usess, +int ust_app_create_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { return 0; } static inline -int ust_app_disable_event_glb(struct ltt_ust_session *usess, +int ust_app_disable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { return 0; } static inline -int ust_app_enable_event_glb(struct ltt_ust_session *usess, +int ust_app_enable_channel_event_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) { return 0; } static inline +int ust_app_disable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + return 0; +} +static inline +int ust_app_map_list_values(const struct ltt_ust_session *usess, + struct ltt_ust_map *umap, + const struct lttng_map_query *query, + struct lttng_map_content **map_content) +{ + return 0; +} +static inline +int ust_app_enable_map_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap) +{ + return 0; +} +static inline +int ust_app_create_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + return 0; +} +static inline +int ust_app_disable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + return 0; +} +static inline +int ust_app_enable_map_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_map *umap, struct ltt_ust_event *uevent) +{ + return 0; +} +static inline int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx) { @@ -579,7 +697,12 @@ unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess) { return 0; } - +static inline +void ust_app_update_event_notifier_error_count( + struct lttng_trigger *lttng_trigger) +{ + return; +} static inline int ust_app_supported(void) { diff --git a/src/bin/lttng-sessiond/ust-consumer.c b/src/bin/lttng-sessiond/ust-consumer.c index cd6b53ee1..158f727f2 100644 --- a/src/bin/lttng-sessiond/ust-consumer.c +++ b/src/bin/lttng-sessiond/ust-consumer.c @@ -42,7 +42,7 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, uint64_t key, chan_reg_key; char *pathname = NULL; struct lttcomm_consumer_msg msg; - struct ust_registry_channel *chan_reg; + struct ust_registry_channel *ust_reg_chan; char shm_path[PATH_MAX] = ""; char root_shm_path[PATH_MAX] = ""; bool is_local_trace; @@ -105,9 +105,9 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, * those buffer files. */ } else { - chan_reg = ust_registry_channel_find(registry, chan_reg_key); - assert(chan_reg); - chan_id = chan_reg->chan_id; + ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key); + assert(ust_reg_chan); + chan_id = ust_reg_chan->chan_id; if (ua_sess->shm_path[0]) { strncpy(shm_path, ua_sess->shm_path, sizeof(shm_path)); shm_path[sizeof(shm_path) - 1] = '\0'; diff --git a/src/bin/lttng-sessiond/ust-ctl-internal.h b/src/bin/lttng-sessiond/ust-ctl-internal.h index 7a89dbc26..af380bc87 100644 --- a/src/bin/lttng-sessiond/ust-ctl-internal.h +++ b/src/bin/lttng-sessiond/ust-ctl-internal.h @@ -9,8 +9,9 @@ #ifndef LTTNG_UST_CTL_INTERNAL_H #define LTTNG_UST_CTL_INTERNAL_H -#include #include +#include +#include #include "lttng-ust-abi.h" diff --git a/src/bin/lttng-sessiond/ust-registry.c b/src/bin/lttng-sessiond/ust-registry.c index 1b1e65eba..384d8030e 100644 --- a/src/bin/lttng-sessiond/ust-registry.c +++ b/src/bin/lttng-sessiond/ust-registry.c @@ -5,6 +5,7 @@ * */ +#include "bin/lttng-sessiond/event-notifier-error-accounting.h" #define _LGPL_SOURCE #include #include @@ -12,6 +13,8 @@ #include #include #include +#include +#include #include "ust-registry.h" #include "ust-app.h" @@ -284,7 +287,7 @@ int validate_event_fields(size_t nr_fields, struct ustctl_field *fields, * registry. */ static struct ust_registry_event *alloc_event(int session_objd, - int channel_objd, char *name, char *sig, size_t nr_fields, + int container_objd, char *name, char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel_value, char *model_emf_uri, struct ust_app *app) { @@ -304,7 +307,7 @@ static struct ust_registry_event *alloc_event(int session_objd, } event->session_objd = session_objd; - event->channel_objd = channel_objd; + event->container_objd = container_objd; /* Allocated by ustctl. */ event->signature = sig; event->nr_fields = nr_fields; @@ -352,6 +355,33 @@ static void destroy_event_rcu(struct rcu_head *head) destroy_event(event); } +/* + * Destroy ust_registry_map_key_ht_entry function call of the call RCU. + */ +static void destroy_ust_registry_map_key_ht_entry(struct rcu_head *head) +{ + struct lttng_ht_node_u64 *node = + caa_container_of(head, struct lttng_ht_node_u64, head); + struct ust_registry_map_key_ht_entry *entry = + caa_container_of(node, struct ust_registry_map_key_ht_entry, node); + + lttng_map_key_put(entry->key); + free(entry); +} + +/* + * Destroy ust_registry_map_index_ht_entry function call of the call RCU. + */ +static void destroy_ust_registry_map_index_ht_entry(struct rcu_head *head) +{ + struct lttng_ht_node_str *node = + caa_container_of(head, struct lttng_ht_node_str, head); + struct ust_registry_map_index_ht_entry *entry = + caa_container_of(node, struct ust_registry_map_index_ht_entry, node); + + free(entry); +} + /* * Find an event using the name and signature in the given registry. RCU read * side lock MUST be acquired before calling this function and as long as the @@ -359,7 +389,7 @@ static void destroy_event_rcu(struct rcu_head *head) * * On success, the event pointer is returned else NULL. */ -struct ust_registry_event *ust_registry_find_event( +struct ust_registry_event *ust_registry_chan_find_event( struct ust_registry_channel *chan, char *name, char *sig) { struct lttng_ht_node_u64 *node; @@ -398,7 +428,7 @@ end: * * Should be called with session registry mutex held. */ -int ust_registry_create_event(struct ust_registry_session *session, +int ust_registry_chan_create_event(struct ust_registry_session *session, uint64_t chan_key, int session_objd, int channel_objd, char *name, char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel_value, char *model_emf_uri, int buffer_type, @@ -447,7 +477,7 @@ int ust_registry_create_event(struct ust_registry_session *session, DBG3("UST registry creating event with event: %s, sig: %s, id: %u, " "chan_objd: %u, sess_objd: %u, chan_id: %u", event->name, - event->signature, event->id, event->channel_objd, + event->signature, event->id, event->container_objd, event->session_objd, chan->chan_id); /* @@ -472,13 +502,13 @@ int ust_registry_create_event(struct ust_registry_session *session, ERR("UST registry create event add unique failed for event: %s, " "sig: %s, id: %u, chan_objd: %u, sess_objd: %u", event->name, event->signature, event->id, - event->channel_objd, event->session_objd); + event->container_objd, event->session_objd); ret = -EINVAL; goto error_unlock; } } else { /* Request next event id if the node was successfully added. */ - event_id = event->id = ust_registry_get_next_event_id(chan); + event_id = event->id = ust_registry_channel_get_next_event_id(chan); } *event_id_p = event_id; @@ -506,11 +536,334 @@ error_unlock: return ret; } +static +int format_event_key(const struct lttng_map_key *key, + const char *full_event_name, char **formated_key) +{ + int ret; + char _key[LTTNG_UST_KEY_TOKEN_STRING_LEN_MAX] = {0}; + enum lttng_map_key_status key_status; + unsigned int i, token_count; + char *cloned_full_event_name; + const char *provider_name, *event_name; + + assert(key); + assert(full_event_name); + + cloned_full_event_name = strdup(full_event_name); + + provider_name = strtok(cloned_full_event_name, ":"); + event_name = strtok(NULL, ":"); + + key_status = lttng_map_key_get_token_count(key, &token_count); + if (key_status != LTTNG_MAP_KEY_STATUS_OK) { + ERR("Error getting map key token count"); + ret = -1; + goto end; + } + + if (token_count == 0) { + ERR("Map key token number is zero"); + ret = -1; + goto end; + } + + for (i = 0; i < token_count; i++) { + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + switch (token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + struct lttng_map_key_token_string *str_token = + (struct lttng_map_key_token_string *) token; + DBG("Appending a string type key token: str = '%s'", str_token->string); + + strcat(_key, lttng_map_key_token_string_get_string(str_token)); + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + struct lttng_map_key_token_variable *var_token = + (struct lttng_map_key_token_variable *) token; + + switch (var_token->type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + DBG("Serializing a event name variable type key token: event_name = '%s'", + event_name); + strcat(_key, event_name); + break; + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + DBG("Serializing a provider name variable type key token: provider_name = '%s'", + provider_name); + strcat(_key, provider_name); + break; + default: + abort(); + } + break; + } + default: + abort(); + } + } + + *formated_key = strdup(_key); + + ret = 0; +end: + free(cloned_full_event_name); + return ret; +} + +static +const struct lttng_map_key *ust_registry_map_find_key_for_token( + struct ust_registry_map *map, + uint64_t tracer_token) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_u64 *key_node; + struct ust_registry_map_key_ht_entry *key_entry; + const struct lttng_map_key *key = NULL;; + + assert(map); + lttng_ht_lookup(map->tracer_token_to_map_key_ht, + (void *) &tracer_token, &iter); + + key_node = lttng_ht_iter_get_node_u64(&iter); + if (!key_node) { + goto end; + } + + /* + * It's already mapped. Return the key we allocated already. + */ + key_entry = caa_container_of(key_node, + struct ust_registry_map_key_ht_entry, node); + assert(key_entry); + + key = key_entry->key; + + DBG("Returning map key object associated to the tracer token: key = %p, tracer_token = %"PRIu64, + key_entry->key, tracer_token); + +end: + return key; +} + +int ust_registry_map_add_token_key_mapping(struct ust_registry_session *session, + uint64_t map_key, uint64_t tracer_token, + struct lttng_map_key *key) +{ + int ret; + struct ust_registry_map_key_ht_entry *key_entry; + struct ust_registry_map *map; + const struct lttng_map_key *existing_mapping = NULL; + + rcu_read_lock(); + map = ust_registry_map_find(session, map_key); + if (!map) { + ret = -EINVAL; + goto end; + } + rcu_read_unlock(); + + /* JORAJ check if the mapping already exist, we might want to *move this + * to the caller or at least provide more check if for some scenario + * (PID) this should never happen + */ + existing_mapping = ust_registry_map_find_key_for_token(map, tracer_token); + if (existing_mapping != NULL) { + assert(existing_mapping == key); + ret = 0; + goto end; + } + + key_entry = zmalloc(sizeof(struct ust_registry_map_key_ht_entry)); + if (!key_entry) { + ret = -ENOMEM; + goto end; + } + key_entry->key = key; + + /* Ensure the lifetime of the lttng_map_key object. */ + lttng_map_key_get(key); + + rcu_read_lock(); + + lttng_ht_node_init_u64(&key_entry->node, tracer_token); + lttng_ht_add_unique_u64(map->tracer_token_to_map_key_ht, + &key_entry->node); + + rcu_read_unlock(); + + + ret = 0; +end: + return ret; + +} + +static +int ust_registry_map_find_or_create_index_for_key(struct ust_registry_map *map, + const char *formated_key, uint64_t *index) +{ + int ret; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *index_node; + struct ust_registry_map_index_ht_entry *index_entry; + + assert(map); + assert(formated_key); + + /* + * First try to check if we already mapped this formated key to an + * index. + */ + lttng_ht_lookup(map->key_string_to_bucket_index_ht, + (void *) formated_key, &iter); + + index_node = lttng_ht_iter_get_node_str(&iter); + if (index_node) { + /* + * It's already mapped. Return the index we allocated already. + */ + index_entry = caa_container_of(index_node, + struct ust_registry_map_index_ht_entry, node); + assert(index_entry); + + *index = index_entry->index; + + DBG("Returning an already allocated index for formated key: key = '%s', index = %"PRIu64, + formated_key, *index); + } else { + /* + * It's not mapped. Create a new mapping, add it to the + * hashtable and return it. + */ + index_entry = zmalloc(sizeof(struct ust_registry_map_index_ht_entry)); + if (!index_entry) { + ret = -1; + goto end; + } + + index_entry->index = ust_registry_map_get_next_event_id(map); + index_entry->formated_key = strdup(formated_key); + lttng_ht_node_init_str(&index_entry->node, index_entry->formated_key); + + lttng_ht_add_unique_str(map->key_string_to_bucket_index_ht, + &index_entry->node); + + *index = index_entry->index; + DBG("Allocated counter index for new formated_key: key = '%s', index = %"PRIu64, + formated_key, *index); + } + + ret = 0; +end: + return ret; +} + +/* + * Create a ust_registry_event from the given parameters and add it to the + * registry hash table. If event_id is valid, it is set with the newly created + * event id. + * + * On success, return 0 else a negative value. The created event MUST be unique + * so on duplicate entry -EINVAL is returned. On error, event_id is untouched. + * + * Should be called with session registry mutex held. + */ +int ust_registry_map_create_event(struct ust_registry_session *session, + uint64_t map_key, int session_objd, int map_objd, char *name, + char *sig, size_t nr_fields, struct ustctl_field *fields, + int loglevel_value, char *model_emf_uri, int buffer_type, + uint64_t tracer_token, uint64_t *counter_index_p, + struct ust_app *app) +{ + int ret; + uint64_t counter_index; + struct ust_registry_map *map; + char *formated_key; + const struct lttng_map_key *key; + + assert(session); + assert(name); + assert(sig); + assert(counter_index_p); + + rcu_read_lock(); + + /* + * This should not happen but since it comes from the UST tracer, an + * external party, don't assert and simply validate values. + */ + if (session_objd < 0 || map_objd < 0) { + ret = -EINVAL; + goto error_free; + } + + map = ust_registry_map_find(session, map_key); + if (!map) { + ret = -EINVAL; + goto error_free; + } + + /* Check if we've reached the maximum possible id. */ + if (ust_registry_is_max_id(map->used_event_id)) { + ret = -ENOENT; + goto error_free; + } + + key = ust_registry_map_find_key_for_token(map, tracer_token); + if (!key) { + ERR("Tracer token %"PRIu64" not found for map id = %"PRIu32, + tracer_token, map->map_id); + ret = -EINVAL; + goto error_unlock; + } + + ret = format_event_key(key, name, &formated_key); + if (ret) { + ERR("Error formating key"); + ret = -EINVAL; + goto error_unlock; + } + + ret = ust_registry_map_find_or_create_index_for_key(map, formated_key, + &counter_index); + if (ret) { + ERR("Error finding or creating index for formated_key = '%s'", + formated_key); + free(formated_key); + ret = -EINVAL; + goto error_unlock; + } + + DBG3("UST registry allocating counter index %"PRIu64 " to event: %s, " + "signature: %s, sess_objd: %u, map_objd: %u, map_id: %u", + counter_index, name, sig, session_objd, map_objd, map->map_id); + + *counter_index_p = counter_index; + + rcu_read_unlock(); + return 0; + +error_free: + free(sig); + free(fields); + free(model_emf_uri); +error_unlock: + rcu_read_unlock(); + return ret; +} + + /* * For a given event in a registry, delete the entry and destroy the event. * This MUST be called within a RCU read side lock section. */ -void ust_registry_destroy_event(struct ust_registry_channel *chan, +void ust_registry_chan_destroy_event(struct ust_registry_channel *chan, struct ust_registry_event *event) { int ret; @@ -529,6 +882,73 @@ void ust_registry_destroy_event(struct ust_registry_channel *chan, return; } +/* + * This MUST be called within a RCU read side lock section. + */ +static void ust_registry_map_key_entry_destroy(struct lttng_ht *ht, + struct ust_registry_map_key_ht_entry *entry) +{ + int ret; + struct lttng_ht_iter iter; + + assert(ht); + assert(entry); + + /* Delete the node first. */ + iter.iter.node = &entry->node.node; + ret = lttng_ht_del(ht, &iter); + assert(!ret); + + call_rcu(&entry->node.head, destroy_ust_registry_map_key_ht_entry); + + return; +} + +/* + * This MUST be called within a RCU read side lock section. + */ +static void ust_registry_map_index_ht_entry_destroy(struct lttng_ht *ht, + struct ust_registry_map_index_ht_entry *entry) +{ + int ret; + struct lttng_ht_iter iter; + + assert(ht); + assert(entry); + + /* Delete the node first. */ + iter.iter.node = &entry->node.node; + ret = lttng_ht_del(ht, &iter); + assert(!ret); + + call_rcu(&entry->node.head, destroy_ust_registry_map_index_ht_entry); + + return; +} + +/* + * For a given event in a registry, delete the entry and destroy the event. + * This MUST be called within a RCU read side lock section. + */ +void ust_registry_map_destroy_event(struct ust_registry_map *map, + struct ust_registry_event *event) +{ + int ret; + struct lttng_ht_iter iter; + + assert(map); + assert(event); + + /* Delete the node first. */ + iter.iter.node = &event->node.node; + ret = lttng_ht_del(map->events_ht, &iter); + assert(!ret); + + call_rcu(&event->node.head, destroy_event_rcu); + + return; +} + static void destroy_enum(struct ust_registry_enum *reg_enum) { if (!reg_enum) { @@ -717,6 +1137,33 @@ void destroy_channel_rcu(struct rcu_head *head) free(chan); } +/* + * We need to execute ht_destroy outside of RCU read-side critical + * section and outside of call_rcu thread, so we postpone its execution + * using ht_cleanup_push. It is simpler than to mapge the semantic of + * the many callers of delete_ust_app_session(). + */ +static +void destroy_map_rcu(struct rcu_head *head) +{ + struct ust_registry_map *map = + caa_container_of(head, struct ust_registry_map, rcu_head); + + if (map->events_ht) { + ht_cleanup_push(map->events_ht); + } + + if (map->tracer_token_to_map_key_ht) { + ht_cleanup_push(map->tracer_token_to_map_key_ht); + } + + if (map->key_string_to_bucket_index_ht) { + ht_cleanup_push(map->key_string_to_bucket_index_ht); + } + + free(map); +} + /* * Destroy every element of the registry and free the memory. This does NOT * free the registry pointer since it might not have been allocated before so @@ -745,13 +1192,55 @@ static void destroy_channel(struct ust_registry_channel *chan, bool notif) cds_lfht_for_each_entry( chan->ht->ht, &iter.iter, event, node.node) { /* Delete the node from the ht and free it. */ - ust_registry_destroy_event(chan, event); + ust_registry_chan_destroy_event(chan, event); } rcu_read_unlock(); } call_rcu(&chan->rcu_head, destroy_channel_rcu); } +/* + * Destroy every element of the registry and free the memory. This does NOT + * free the registry pointer since it might not have been allocated before so + * it's the caller responsability. + */ +static void destroy_map(struct ust_registry_map *map) +{ + struct lttng_ht_iter iter; + struct ust_registry_event *event; + struct ust_registry_map_key_ht_entry *key_entry; + struct ust_registry_map_index_ht_entry *index_entry; + + assert(map); + + rcu_read_lock(); + if (map->events_ht) { + /* Destroy all event associated with this registry. */ + cds_lfht_for_each_entry(map->events_ht->ht, &iter.iter, event, node.node) { + /* Delete the node from the ht and free it. */ + ust_registry_map_destroy_event(map, event); + } + } + + /* Destroy all map_key entries associated with this registry. */ + cds_lfht_for_each_entry (map->tracer_token_to_map_key_ht->ht, + &iter.iter, key_entry, node.node) { + ust_registry_map_key_entry_destroy( + map->tracer_token_to_map_key_ht, + key_entry); + } + + /* Destroy all index entry associated with this registry. */ + cds_lfht_for_each_entry(map->key_string_to_bucket_index_ht->ht, + &iter.iter, index_entry, node.node) { + ust_registry_map_index_ht_entry_destroy( + map->key_string_to_bucket_index_ht, + index_entry); + } + rcu_read_unlock(); + call_rcu(&map->rcu_head, destroy_map_rcu); +} + /* * Initialize registry with default values. */ @@ -804,6 +1293,71 @@ error_alloc: return ret; } +/* + * Initialize registry map entry with default values. + */ +int ust_registry_map_add(struct ust_registry_session *session, + uint64_t key) +{ + int ret = 0; + struct ust_registry_map *map; + + assert(session); + + map = zmalloc(sizeof(*map)); + if (!map) { + PERROR("zmalloc ust registry map"); + ret = -ENOMEM; + goto error_alloc; + } + + map->events_ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!map->events_ht) { + ret = -ENOMEM; + goto error; + } + + /* Set custom match function. */ + map->events_ht->match_fct = ht_match_event; + map->events_ht->hash_fct = ht_hash_event; + + map->tracer_token_to_map_key_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!map->tracer_token_to_map_key_ht) { + ret = -ENOMEM; + goto error; + } + + map->key_string_to_bucket_index_ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!map->key_string_to_bucket_index_ht) { + ret = -ENOMEM; + goto error; + } + + /* + * FIXME frdeso: fix this comment + * Assign a map ID right now since the event notification comes + * *before* the map notify so the ID needs to be set at this point so + * the metadata can be dumped for that event. + */ + if (ust_registry_is_max_id(session->used_map_id)) { + ret = -1; + goto error; + } + map->map_id = ust_registry_get_next_map_id(session); + + rcu_read_lock(); + lttng_ht_node_init_u64(&map->node, key); + lttng_ht_add_unique_u64(session->maps, &map->node); + rcu_read_unlock(); + + return 0; + +error: + destroy_map(map); +error_alloc: + return ret; +} + /* * Find a channel in the given registry. RCU read side lock MUST be acquired * before calling this function and as long as the event reference is kept by @@ -834,6 +1388,36 @@ end: return chan; } +/* + * Find a map in the given registry. RCU read side lock MUST be acquired + * before calling this function and as long as the event reference is kept by + * the caller. + * + * On success, the pointer is returned else NULL. + */ +struct ust_registry_map *ust_registry_map_find( + struct ust_registry_session *session, uint64_t key) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + struct ust_registry_map *map = NULL; + + assert(session); + assert(session->maps); + + DBG3("UST registry map finding key %" PRIu64, key); + + lttng_ht_lookup(session->maps, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (!node) { + goto end; + } + map = caa_container_of(node, struct ust_registry_map, node); + +end: + return map; +} + /* * Remove channel using key from registry and free memory. */ @@ -863,6 +1447,35 @@ end: return; } +/* + * Remove map using key from registry and free memory. + */ +void ust_registry_map_del_free(struct ust_registry_session *session, + uint64_t key) +{ + struct lttng_ht_iter iter; + struct ust_registry_map *map; + int ret; + + assert(session); + + rcu_read_lock(); + map = ust_registry_map_find(session, key); + if (!map) { + rcu_read_unlock(); + goto end; + } + + iter.iter.node = &map->node.node; + ret = lttng_ht_del(session->maps, &iter); + assert(!ret); + rcu_read_unlock(); + destroy_map(map); + +end: + return; +} + /* * Initialize registry with default values and set the newly allocated session * pointer to sessionp. @@ -962,6 +1575,12 @@ int ust_registry_session_init(struct ust_registry_session **sessionp, goto error; } + session->maps = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + if (!session->maps) { + lttng_ht_destroy(session->channels); + goto error; + } + ret = lttng_uuid_generate(session->uuid); if (ret) { ERR("Failed to generate UST uuid (errno = %d)", ret); diff --git a/src/bin/lttng-sessiond/ust-registry.h b/src/bin/lttng-sessiond/ust-registry.h index 12614c5d5..1e3d2fc6b 100644 --- a/src/bin/lttng-sessiond/ust-registry.h +++ b/src/bin/lttng-sessiond/ust-registry.h @@ -35,6 +35,10 @@ struct ust_registry_session { uint32_t next_channel_id; /* Once this value reaches UINT32_MAX, no more id can be allocated. */ uint32_t used_channel_id; + /* Next map ID available for a newly registered map. */ + uint32_t next_map_id; + /* Once this value reaches UINT32_MAX, no more id can be allocated. */ + uint32_t used_map_id; /* Next enumeration ID available. */ uint64_t next_enum_id; /* Universal unique identifier used by the tracer. */ @@ -97,6 +101,12 @@ struct ust_registry_session { * be accessed with a RCU read side lock acquired. */ struct lttng_ht *channels; + + /* + * Hash table containing maps sent by the UST tracer. MUST + * be accessed with a RCU read side lock acquired. + */ + struct lttng_ht *maps; /* * Unique key to identify the metadata on the consumer side. */ @@ -162,6 +172,45 @@ struct ust_registry_channel { struct rcu_head rcu_head; }; +struct ust_registry_map_key_ht_entry { + struct lttng_map_key *key; + struct lttng_ht_node_u64 node; +}; + +struct ust_registry_map_index_ht_entry { + uint64_t index; + char *formated_key; + struct lttng_ht_node_str node; +}; + +struct ust_registry_map { + uint64_t key; + /* Id set when replying to a register map. */ + uint32_t map_id; + + /* Indicates if this map registry has already been registered. */ + unsigned int register_done; + + /* + * Hash table containing events sent by the UST tracer. MUST be accessed + * with a RCU read side lock acquired. + */ + struct lttng_ht *events_ht; + /* Next event ID available for a newly registered event. */ + uint32_t next_event_id; + /* Once this value reaches UINT32_MAX, no more id can be allocated. */ + uint32_t used_event_id; + + /* tracer_token -> ust_registry_map_key_ht_entry */ + struct lttng_ht *tracer_token_to_map_key_ht; + /* format key -> ust_registry_map_index_ht_entry */ + struct lttng_ht *key_string_to_bucket_index_ht; + + struct lttng_ht_node_u64 node; + /* For delayed reclaim */ + struct rcu_head rcu_head; +}; + /* * Event registered from a UST tracer sent to the session daemon. This is * indexed and matched by . @@ -170,7 +219,7 @@ struct ust_registry_event { int id; /* Both objd are set by the tracer. */ int session_objd; - int channel_objd; + int container_objd; /* Name of the event returned by the tracer. */ char name[LTTNG_UST_SYM_NAME_LEN]; char *signature; @@ -220,7 +269,7 @@ static inline int ust_registry_is_max_id(uint32_t id) * Return a unique channel ID. If max is reached, the used_event_id counter is * returned. */ -static inline uint32_t ust_registry_get_next_event_id( +static inline uint32_t ust_registry_channel_get_next_event_id( struct ust_registry_channel *r) { if (ust_registry_is_max_id(r->used_event_id)) { @@ -231,6 +280,26 @@ static inline uint32_t ust_registry_get_next_event_id( return r->next_event_id++; } +/* + * Return next available event id and increment the used counter. The + * ust_registry_is_max_id function MUST be called before in order to validate + * if the maximum number of IDs have been reached. If not, it is safe to call + * this function. + * + * Return a unique map ID. If max is reached, the used_event_id counter is + * returned. + */ +static inline uint32_t ust_registry_map_get_next_event_id( + struct ust_registry_map *r) +{ + if (ust_registry_is_max_id(r->used_event_id)) { + return r->used_event_id; + } + + r->used_event_id++; + return r->next_event_id++; +} + /* * Return next available channel id and increment the used counter. The * ust_registry_is_max_id function MUST be called before in order to validate @@ -251,6 +320,26 @@ static inline uint32_t ust_registry_get_next_chan_id( return r->next_channel_id++; } +/* + * Return next available map id and increment the used counter. The + * ust_registry_is_max_id function MUST be called before in order to validate + * if the maximum number of IDs have been reached. If not, it is safe to call + * this function. + * + * Return a unique map ID. If max is reached, the used_map_id counter + * is returned. + */ +static inline uint32_t ust_registry_get_next_map_id( + struct ust_registry_session *r) +{ + if (ust_registry_is_max_id(r->used_map_id)) { + return r->used_map_id; + } + + r->used_map_id++; + return r->next_map_id++; +} + /* * Return registry event count. This is read atomically. */ @@ -262,6 +351,7 @@ static inline uint32_t ust_registry_get_event_count( #ifdef HAVE_LIBLTTNG_UST_CTL +/* Channels */ void ust_registry_channel_destroy(struct ust_registry_session *session, struct ust_registry_channel *chan); struct ust_registry_channel *ust_registry_channel_find( @@ -271,6 +361,19 @@ int ust_registry_channel_add(struct ust_registry_session *session, void ust_registry_channel_del_free(struct ust_registry_session *session, uint64_t key, bool notif); +/* Maps */ +void ust_registry_map_destroy(struct ust_registry_session *session, + struct ust_registry_map *map); +struct ust_registry_map *ust_registry_map_find( + struct ust_registry_session *session, uint64_t key); +int ust_registry_map_add(struct ust_registry_session *session, + uint64_t key); +void ust_registry_map_del_free(struct ust_registry_session *session, + uint64_t key); +int ust_registry_map_add_token_key_mapping(struct ust_registry_session *session, + uint64_t map_key, uint64_t tracer_token, + struct lttng_map_key *key); + int ust_registry_session_init(struct ust_registry_session **sessionp, struct ust_app *app, uint32_t bits_per_long, @@ -290,14 +393,24 @@ int ust_registry_session_init(struct ust_registry_session **sessionp, uid_t tracing_uid); void ust_registry_session_destroy(struct ust_registry_session *session); -int ust_registry_create_event(struct ust_registry_session *session, +int ust_registry_chan_create_event(struct ust_registry_session *session, uint64_t chan_key, int session_objd, int channel_objd, char *name, char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel_value, char *model_emf_uri, int buffer_type, uint32_t *event_id_p, struct ust_app *app); -struct ust_registry_event *ust_registry_find_event( +struct ust_registry_event *ust_registry_chan_find_event( struct ust_registry_channel *chan, char *name, char *sig); -void ust_registry_destroy_event(struct ust_registry_channel *chan, +void ust_registry_chan_destroy_event(struct ust_registry_channel *chan, + struct ust_registry_event *event); + +int ust_registry_map_create_event(struct ust_registry_session *session, + uint64_t map_key, int session_objd, int map_objd, char *name, + char *sig, size_t nr_fields, struct ustctl_field *fields, + int loglevel_value, char *model_emf_uri, int buffer_type, + uint64_t tracer_token, uint64_t *counter_index_p, struct ust_app *app); +struct ust_registry_event *ust_registry_map_find_event( + struct ust_registry_map *map, char *name, char *sig); +void ust_registry_map_destroy_event(struct ust_registry_map *map, struct ust_registry_event *event); /* app can be NULL for registry shared across applications. */ diff --git a/src/bin/lttng/Makefile.am b/src/bin/lttng/Makefile.am index 50ab92988..6c262622f 100644 --- a/src/bin/lttng/Makefile.am +++ b/src/bin/lttng/Makefile.am @@ -14,9 +14,13 @@ lttng_SOURCES = command.h conf.c conf.h commands/start.c \ commands/list.c commands/create.c commands/destroy.c \ commands/stop.c commands/enable_events.c \ commands/disable_events.c commands/enable_channels.c \ + commands/add_map.c \ + commands/enable_map.c \ + commands/disable_map.c \ commands/disable_channels.c commands/add_context.c \ commands/set_session.c commands/version.c \ commands/view.c \ + commands/view_map.c \ commands/snapshot.c \ commands/save.c \ commands/load.c \ @@ -44,4 +48,5 @@ lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ $(top_builddir)/src/common/string-utils/libstring-utils.la \ $(top_builddir)/src/common/filter/libfilter.la \ $(top_builddir)/src/common/argpar/libargpar.la \ - $(POPT_LIBS) + $(POPT_LIBS) \ + -lm diff --git a/src/bin/lttng/command.h b/src/bin/lttng/command.h index bf0045210..6b8c5c504 100644 --- a/src/bin/lttng/command.h +++ b/src/bin/lttng/command.h @@ -60,10 +60,14 @@ DECL_COMMAND(enable_events); DECL_COMMAND(disable_events); DECL_COMMAND(enable_channels); DECL_COMMAND(disable_channels); +DECL_COMMAND(add_map); +DECL_COMMAND(enable_map); +DECL_COMMAND(disable_map); DECL_COMMAND(add_context); DECL_COMMAND(set_session); DECL_COMMAND(version); DECL_COMMAND(view); +DECL_COMMAND(view_map); DECL_COMMAND(enable_consumer); DECL_COMMAND(disable_consumer); DECL_COMMAND(snapshot); diff --git a/src/bin/lttng/commands/add_map.c b/src/bin/lttng/commands/add_map.c new file mode 100644 index 000000000..d9e4ff4c9 --- /dev/null +++ b/src/bin/lttng/commands/add_map.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include + +#include + +#include "common/argpar/argpar.h" +#include "common/utils.h" + +#include "../command.h" +#include "../utils.h" + +#define LTTNG_MAP_DEFAULT_SIZE 4096 + +enum { + OPT_HELP, + OPT_SESSION, + OPT_USERSPACE, + OPT_KERNEL, + OPT_MAX_KEY_COUNT, + OPT_PER_PID, + OPT_PER_UID, + OPT_OVERFLOW, + OPT_BITNESS, + OPT_COALESCE_HITS, +}; + +static const struct argpar_opt_descr add_map_opt_descrs[] = { + + { OPT_HELP, 'h', "help", false }, + { OPT_SESSION, 's', "session", true }, + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + { OPT_MAX_KEY_COUNT, '\0', "max-key-count", true}, + { OPT_PER_PID, '\0', "per-pid", false}, + { OPT_PER_UID, '\0', "per-uid", false}, + { OPT_OVERFLOW, '\0', "overflow", false}, + { OPT_BITNESS, '\0', "bitness", true}, + { OPT_COALESCE_HITS, '\0', "coalesce-hits", false}, + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +int cmd_add_map(int argc, const char **argv) +{ + int ret, i; + enum lttng_error_code error_code_ret; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + bool opt_userspace = false, opt_kernel = false, opt_buffers_uid = false, + opt_buffers_pid = false, opt_overflow = false, opt_coalesce_hits = false; + char *opt_session_name = NULL, *session_name = NULL, *opt_max_key_count = NULL, *opt_bitness = NULL; + const char *opt_map_name = NULL;; + enum lttng_map_bitness bitness_type; + enum lttng_map_boundary_policy boundary_policy; + enum lttng_map_status status; + uint64_t dimensions_sizes[1] = {0}; + unsigned long long bitness; + struct lttng_map *map; + struct lttng_domain dom = {0}; + struct lttng_handle *handle = NULL; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + add_map_opt_descrs, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = CMD_SUCCESS; + goto end; + case OPT_SESSION: + if (!assign_string(&opt_session_name, item_opt->arg, + "-s/--session")) { + goto error; + } + break; + case OPT_USERSPACE: + opt_userspace = true; + break; + case OPT_KERNEL: + opt_kernel = true; + break; + case OPT_MAX_KEY_COUNT: + if (!assign_string(&opt_max_key_count, item_opt->arg, + "--max-key-count")) { + goto error; + } + break; + case OPT_PER_PID: + opt_buffers_pid = true; + break; + case OPT_PER_UID: + opt_buffers_uid = true; + break; + case OPT_OVERFLOW: + opt_overflow = true; + break; + case OPT_BITNESS: + if (!assign_string(&opt_bitness, item_opt->arg, + "--bitness")) { + goto error; + } + break; + case OPT_COALESCE_HITS: + opt_coalesce_hits = true; + break; + default: + abort(); + } + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (opt_map_name) { + ERR("Unexpected argument: %s", item_non_opt->arg); + goto error; + } + + opt_map_name = item_non_opt->arg; + } + } + + if (!opt_map_name) { + ERR("Missing map name"); + goto error; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + } else { + session_name = opt_session_name; + } + + /* Check that one and only one domain option was provided. */ + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace, false); + if (ret) { + goto error; + } + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + if (opt_buffers_uid || opt_buffers_pid) { + ERR("Buffer type not supported for kernel domain"); + goto error; + } + dom.buf_type = LTTNG_BUFFER_GLOBAL; + } else { + dom.type = LTTNG_DOMAIN_UST; + + if (opt_buffers_uid && opt_buffers_pid) { + ERR("Only one domain can be specified"); + goto error; + } + if (opt_buffers_pid) { + dom.buf_type = LTTNG_BUFFER_PER_PID; + } else { + /* Defaults to per UID */ + dom.buf_type = LTTNG_BUFFER_PER_UID; + } + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + if (opt_max_key_count) { + unsigned long long max_key_count; + if (utils_parse_unsigned_long_long(opt_max_key_count, &max_key_count) != 0) { + ERR("Failed to parse `%s` as an integer.", opt_max_key_count); + goto error; + } + + dimensions_sizes[0] = max_key_count; + } else { + dimensions_sizes[0] = LTTNG_MAP_DEFAULT_SIZE; + } + + if (opt_bitness) { + if (utils_parse_unsigned_long_long(opt_bitness, &bitness) != 0) { + ERR("Failed to parse `%s` as an integer.", opt_bitness); + goto error; + } + switch (bitness) { + case 32: + bitness_type = LTTNG_MAP_BITNESS_32BITS; + break; + case 64: + bitness_type = LTTNG_MAP_BITNESS_64BITS; + break; + default: + ERR("Bitness %llu not supported", bitness); + goto error; + } + + } else { + bitness_type = LTTNG_MAP_BITNESS_64BITS; + } + + + if (opt_overflow) { + boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + } else { + boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + } + + status = lttng_map_create(opt_map_name, 1, dimensions_sizes, dom.type, + dom.buf_type, bitness_type, boundary_policy, + opt_coalesce_hits, &map); + if (status != LTTNG_MAP_STATUS_OK) { + ERR("Creating map \"%s\"", opt_map_name); + goto error; + } + + error_code_ret = lttng_add_map(handle, map); + if (error_code_ret != LTTNG_OK) { + ERR("Adding map \"%s\": %s", opt_map_name, + lttng_strerror(error_code_ret)); + lttng_map_destroy(map); + goto error; + } + + MSG("Map %s created.", opt_map_name); + ret = CMD_SUCCESS; + + lttng_map_destroy(map); + + goto end; + +error: + ret = CMD_ERROR; +end: + argpar_parse_ret_fini(&argpar_parse_ret); + free(opt_session_name); + free(opt_max_key_count); + free(opt_bitness); + lttng_destroy_handle(handle); + return ret; +} diff --git a/src/bin/lttng/commands/add_trigger.c b/src/bin/lttng/commands/add_trigger.c index b3b1dade1..f42720df6 100644 --- a/src/bin/lttng/commands/add_trigger.c +++ b/src/bin/lttng/commands/add_trigger.c @@ -19,6 +19,15 @@ /* For lttng_event_rule_type_str(). */ #include #include +#include "lttng/event-rule/kernel-function.h" +#include "lttng/event-rule/kernel-probe.h" +#include "lttng/event-rule/syscall.h" +#include +#include "lttng/event-rule/userspace-probe.h" +#include "lttng/kernel-function.h" +#include "lttng/kernel-probe.h" +#include "lttng/log-level-rule.h" +#include "lttng/map-key-internal.h" #include "common/filter/filter-ast.h" #include "common/filter/filter-ir.h" #include "common/dynamic-array.h" @@ -69,6 +78,10 @@ enum { OPT_URL, OPT_PATH, + OPT_SESSION_NAME, + OPT_MAP_NAME, + OPT_KEY, + OPT_CAPTURE, }; @@ -299,6 +312,98 @@ end: return ret; } +static int parse_kernel_function_opts(const char *source, + struct lttng_kernel_function_location **location) +{ + int ret = 0; + int match; + char s_hex[19]; + char name[LTTNG_SYMBOL_NAME_LEN]; + char *symbol_name = NULL; + uint64_t offset; + + /* Check for symbol+offset. */ + match = sscanf(source, + "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "[^'+']+%18s", + name, s_hex); + if (match == 2) { + if (*s_hex == '\0') { + ERR("Kernel function symbol offset is missing."); + goto error; + } + + symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!symbol_name) { + PERROR("Failed to copy kernel function location symbol name."); + goto error; + } + offset = strtoul(s_hex, NULL, 0); + + *location = lttng_kernel_function_location_symbol_create( + symbol_name, offset); + if (!*location) { + ERR("Failed to create symbol kernel function location."); + goto error; + } + + goto end; + } + + /* Check for symbol. */ + if (isalpha(name[0]) || name[0] == '_') { + match = sscanf(source, + "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "s", + name); + if (match == 1) { + symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN); + if (!symbol_name) { + ERR("Failed to copy kernel function location symbol name."); + goto error; + } + + *location = lttng_kernel_function_location_symbol_create( + symbol_name, 0); + if (!*location) { + ERR("Failed to create symbol kernel function location."); + goto error; + } + + goto end; + } + } + + /* Check for address. */ + match = sscanf(source, "%18s", s_hex); + if (match > 0) { + uint64_t address; + + if (*s_hex == '\0') { + ERR("Invalid kernel function location address."); + goto error; + } + + address = strtoul(s_hex, NULL, 0); + *location = lttng_kernel_function_location_address_create(address); + if (!*location) { + ERR("Failed to create symbol kernel function location."); + goto error; + } + + goto end; + } + +error: + /* No match */ + ret = -1; + *location = NULL; + +end: + free(symbol_name); + return ret; +} + static struct lttng_event_expr *ir_op_load_expr_to_event_expr( const struct ir_load_expression *load_expr, @@ -537,10 +642,12 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) char *error = NULL; int consumed_args = -1; struct lttng_kernel_probe_location *kernel_probe_location = NULL; + struct lttng_kernel_function_location *kernel_function_location = NULL; struct lttng_userspace_probe_location *userspace_probe_location = NULL; struct parse_event_rule_res res = { 0 }; struct lttng_event_expr *event_expr = NULL; struct filter_parser_ctx *parser_ctx = NULL; + struct lttng_log_level_rule *log_level_rule = NULL; /* Was the -a/--all flag provided? */ bool all_events = false; @@ -627,14 +734,18 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) /* Event rule types */ case OPT_FUNCTION: if (!assign_event_rule_type(&event_rule_type, - LTTNG_EVENT_RULE_TYPE_KRETPROBE)) { + LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION)) { + goto error; + } + + if (!assign_string(&source, item_opt->arg, "source")) { goto error; } break; case OPT_PROBE: if (!assign_event_rule_type(&event_rule_type, - LTTNG_EVENT_RULE_TYPE_KPROBE)) { + LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE)) { goto error; } @@ -645,7 +756,7 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) break; case OPT_USERSPACE_PROBE: if (!assign_event_rule_type(&event_rule_type, - LTTNG_EVENT_RULE_TYPE_UPROBE)) { + LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE)) { goto error; } @@ -817,9 +928,9 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) /* 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_KERNEL_PROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: case LTTNG_EVENT_RULE_TYPE_SYSCALL: if (domain_type != LTTNG_DOMAIN_KERNEL) { ERR("Event type not available for user-space tracing."); @@ -938,15 +1049,20 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) } if (loglevel_only) { - event_rule_status = lttng_event_rule_tracepoint_set_log_level( - res.er, - loglevel); + log_level_rule = lttng_log_level_rule_exactly_create(loglevel); } else { - event_rule_status = lttng_event_rule_tracepoint_set_log_level_range_lower_bound( - res.er, - loglevel); + log_level_rule = lttng_log_level_rule_at_least_as_severe_as_create(loglevel); + } + + if (log_level_rule == NULL) { + ERR("Failed to create log level rule object."); + goto error; } + event_rule_status = + lttng_event_rule_tracepoint_set_log_level_rule( + res.er, log_level_rule); + if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to set log level on event fule."); goto error; @@ -955,16 +1071,11 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) break; } - case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: { int ret; enum lttng_event_rule_status event_rule_status; - res.er = lttng_event_rule_kprobe_create(); - if (!res.er) { - ERR("Failed to create kprobe event rule."); - goto error; - } ret = parse_kernel_probe_opts(source, &kernel_probe_location); if (ret) { @@ -972,22 +1083,49 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) goto error; } - event_rule_status = lttng_event_rule_kprobe_set_name(res.er, tracepoint_name); + assert(kernel_probe_location); + res.er = lttng_event_rule_kernel_probe_create(kernel_probe_location); + if (!res.er) { + ERR("Failed to create kprobe event rule."); + goto error; + } + + event_rule_status = lttng_event_rule_kernel_probe_set_event_name(res.er, tracepoint_name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to set kprobe event rule's name to '%s'.", tracepoint_name); goto error; } - assert(kernel_probe_location); - event_rule_status = lttng_event_rule_kprobe_set_location(res.er, kernel_probe_location); + break; + } + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + { + int ret; + enum lttng_event_rule_status event_rule_status; + + + ret = parse_kernel_function_opts(source, &kernel_function_location); + if (ret) { + ERR("Failed to parse kernel function location."); + goto error; + } + + assert(kernel_function_location); + res.er = lttng_event_rule_kernel_function_create(kernel_function_location); + if (!res.er) { + ERR("Failed to create kfunction event rule."); + goto error; + } + + event_rule_status = lttng_event_rule_kernel_function_set_event_name(res.er, tracepoint_name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set kprobe event rule's location."); + ERR("Failed to set kfunction event rule's name to '%s'.", tracepoint_name); goto error; } break; } - case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: { int ret; enum lttng_event_rule_status event_rule_status; @@ -999,20 +1137,13 @@ struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv) goto error; } - res.er = lttng_event_rule_uprobe_create(); + res.er = lttng_event_rule_userspace_probe_create(userspace_probe_location); if (!res.er) { ERR("Failed to create userspace probe event rule."); goto error; } - event_rule_status = lttng_event_rule_uprobe_set_location( - res.er, userspace_probe_location); - if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { - ERR("Failed to set user space probe event rule's location."); - goto error; - } - - event_rule_status = lttng_event_rule_uprobe_set_name( + event_rule_status = lttng_event_rule_userspace_probe_set_event_name( res.er, tracepoint_name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to set user space probe event rule's name to '%s'.", @@ -1080,6 +1211,7 @@ end: strutils_free_null_terminated_array_of_strings(exclusion_list); lttng_kernel_probe_location_destroy(kernel_probe_location); lttng_userspace_probe_location_destroy(userspace_probe_location); + lttng_log_level_rule_destroy(log_level_rule); return res; } @@ -1096,7 +1228,7 @@ struct lttng_condition *handle_condition_event(int *argc, const char ***argv) goto error; } - c = lttng_condition_event_rule_create(res.er); + c = lttng_condition_on_event_create(res.er); lttng_event_rule_destroy(res.er); res.er = NULL; if (!c) { @@ -1112,9 +1244,12 @@ struct lttng_condition *handle_condition_event(int *argc, const char ***argv) assert(expr); assert(*expr); - status = lttng_condition_event_rule_append_capture_descriptor( + status = lttng_condition_on_event_append_capture_descriptor( c, *expr); if (status != LTTNG_CONDITION_STATUS_OK) { + if (status == LTTNG_CONDITION_STATUS_UNSUPPORTED) { + ERR("The capture feature is unsupported by the event-rule type"); + } goto error; } @@ -1764,6 +1899,139 @@ end: return action; } +static const struct argpar_opt_descr incr_value_action_opt_descrs[] = { + { OPT_SESSION_NAME, 's', "session", true }, + { OPT_MAP_NAME, 'm', "map", true }, + { OPT_KEY, '\0', "key", true }, + ARGPAR_OPT_DESCR_SENTINEL +}; + +static +struct lttng_action *handle_action_incr_value(int *argc, + const char ***argv) +{ + struct lttng_action *action = NULL; + struct argpar_state *state = NULL; + struct argpar_item *item = NULL; + struct lttng_map_key *key = NULL; + char *session_name_arg = NULL, *map_name_arg = NULL; + char *key_arg = NULL; + char *error = NULL; + enum lttng_action_status action_status; + + state = argpar_state_create(*argc, *argv, incr_value_action_opt_descrs); + if (!state) { + ERR("Failed to allocate an argpar state."); + goto error; + } + + while (true) { + enum argpar_state_parse_next_status status; + + ARGPAR_ITEM_DESTROY_AND_RESET(item); + status = argpar_state_parse_next(state, &item, &error); + if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) { + ERR("%s", error); + goto error; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) { + /* Just stop parsing here. */ + break; + } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) { + break; + } + + assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK); + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_SESSION_NAME: + if (!assign_string(&session_name_arg, item_opt->arg, "--session/-s")) { + goto error; + } + break; + case OPT_MAP_NAME: + if (!assign_string(&map_name_arg, item_opt->arg, "--map/-m")) { + goto error; + } + break; + case OPT_KEY: + if (!assign_string(&key_arg, item_opt->arg, "--key")) { + goto error; + } + break; + default: + abort(); + } + } + } + + *argc -= argpar_state_get_ingested_orig_args(state); + *argv += argpar_state_get_ingested_orig_args(state); + + if (!session_name_arg) { + ERR("Missing session name."); + goto error; + } + + if (!map_name_arg) { + ERR("Missing map name."); + goto error; + } + + if (!key_arg) { + ERR("Missing key"); + goto error; + } + + key = lttng_map_key_parse_from_string(key_arg); + if (!key) { + ERR("Error parsing key argument"); + goto error; + } + + action = lttng_action_incr_value_create(); + if (!action) { + ERR("Failed to allocate incr-value action."); + goto error; + } + + action_status = lttng_action_incr_value_set_session_name(action, + session_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set action incr-value's session name."); + goto error; + } + + action_status = lttng_action_incr_value_set_map_name(action, + map_name_arg); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set action incr-value's map name."); + goto error; + } + + action_status = lttng_action_incr_value_set_key(action, key); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Failed to set action incr-value's key"); + goto error; + } + + goto end; + +error: + lttng_action_destroy(action); + action = NULL; + +end: + lttng_map_key_destroy(key); + free(session_name_arg); + free(map_name_arg); + free(key_arg); + return action; +} + struct action_descr { const char *name; struct lttng_action *(*handler) (int *argc, const char ***argv); @@ -1776,6 +2044,7 @@ struct action_descr action_descrs[] = { { "stop-session", handle_action_stop_session }, { "rotate-session", handle_action_rotate_session }, { "snapshot-session", handle_action_snapshot_session }, + { "incr-value", handle_action_incr_value }, }; static @@ -1834,6 +2103,30 @@ struct argpar_opt_descr add_trigger_options[] = { ARGPAR_OPT_DESCR_SENTINEL, }; +static +bool action_is_tracer_executed(const struct lttng_action *action) +{ + bool is_tracer_executed; + 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: + is_tracer_executed = false; + goto end; + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + is_tracer_executed = true; + goto end; + case LTTNG_ACTION_TYPE_GROUP: + default: + abort(); + } + +end: + return is_tracer_executed; +} + static void lttng_actions_destructor(void *p) { @@ -2021,6 +2314,18 @@ int cmd_add_trigger(int argc, const char **argv) enum lttng_action_status status; action = lttng_dynamic_pointer_array_steal_pointer(&actions, i); + if (action_is_tracer_executed(action)) { + if (fire_every_str || fire_once_after_str) { + /* + * Firing policy with tracer-executed actions + * (`incr-value`) is not supported at the + * moment. It's not clear how the tracers will + * handle the different policies efficiently. + */ + ERR("Can't use --fire-once-after or --fire-every with tracer executed action (incr-value)"); + goto error; + } + } status = lttng_action_group_add_action(action_group, action); if (status != LTTNG_ACTION_STATUS_OK) { diff --git a/src/bin/lttng/commands/disable_map.c b/src/bin/lttng/commands/disable_map.c new file mode 100644 index 000000000..5985b539c --- /dev/null +++ b/src/bin/lttng/commands/disable_map.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include + +#include + +#include "common/argpar/argpar.h" + +#include "../command.h" +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_KERNEL, + OPT_SESSION, + OPT_USERSPACE, +}; + +static const +struct argpar_opt_descr disable_map_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_SESSION, 's', "session", true }, + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +int cmd_disable_map(int argc, const char **argv) +{ + int ret, i; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + const char *opt_map_name = NULL; + enum lttng_error_code error_code_ret; + bool opt_userspace = false, opt_kernel = false; + char *opt_session_name = NULL, *session_name = NULL; + struct lttng_domain dom = {0}; + struct lttng_handle *handle; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + disable_map_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + case OPT_SESSION: + if (!assign_string(&opt_session_name, item_opt->arg, + "-s/--session")) { + goto error; + } + break; + case OPT_USERSPACE: + opt_userspace = true; + break; + case OPT_KERNEL: + opt_kernel = true; + break; + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (opt_map_name) { + ERR("Unexpected argument: %s", item_non_opt->arg); + goto error; + } + + opt_map_name = item_non_opt->arg; + } + } + + if (!opt_map_name) { + ERR("Missing `name` argument."); + goto error; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + } else { + session_name = opt_session_name; + } + + /* Check that one and only one domain option was provided. */ + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace, false); + if (ret) { + goto error; + } + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + dom.buf_type = LTTNG_BUFFER_GLOBAL; + } else { + dom.type=LTTNG_DOMAIN_UST; + dom.buf_type = LTTNG_BUFFER_PER_UID; + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + error_code_ret = lttng_disable_map(handle, opt_map_name); + if (error_code_ret != LTTNG_OK) { + ERR("Error disabling map \"%s\"", opt_map_name); + goto error; + } + + MSG("Disabled map `%s`.", opt_map_name); + + ret = 0; + goto end; + +error: + ret = 1; + +end: + argpar_parse_ret_fini(&argpar_parse_ret); + + return ret; +} diff --git a/src/bin/lttng/commands/enable_map.c b/src/bin/lttng/commands/enable_map.c new file mode 100644 index 000000000..f246fb99a --- /dev/null +++ b/src/bin/lttng/commands/enable_map.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include + +#include + +#include "common/argpar/argpar.h" + +#include "../command.h" +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; +#endif + +enum { + OPT_HELP, + OPT_KERNEL, + OPT_SESSION, + OPT_USERSPACE, +}; + +static const +struct argpar_opt_descr enable_map_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_SESSION, 's', "session", true }, + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +int cmd_enable_map(int argc, const char **argv) +{ + int ret, i; + struct argpar_parse_ret argpar_parse_ret = { 0 }; + const char *opt_map_name = NULL; + enum lttng_error_code error_code_ret; + bool opt_userspace = false, opt_kernel = false; + char *opt_session_name = NULL, *session_name = NULL; + struct lttng_domain dom = {0}; + struct lttng_handle *handle; + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + enable_map_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = 0; + goto end; + case OPT_SESSION: + if (!assign_string(&opt_session_name, item_opt->arg, + "-s/--session")) { + goto error; + } + break; + case OPT_USERSPACE: + opt_userspace = true; + break; + case OPT_KERNEL: + opt_kernel = true; + break; + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (opt_map_name) { + ERR("Unexpected argument: %s", item_non_opt->arg); + goto error; + } + + opt_map_name = item_non_opt->arg; + } + } + + if (!opt_map_name) { + ERR("Missing `name` argument."); + goto error; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + } else { + session_name = opt_session_name; + } + + /* Check that one and only one domain option was provided. */ + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace, false); + if (ret) { + goto error; + } + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + dom.buf_type = LTTNG_BUFFER_GLOBAL; + } else { + dom.type=LTTNG_DOMAIN_UST; + dom.buf_type = LTTNG_BUFFER_PER_UID; + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + error_code_ret = lttng_enable_map(handle, opt_map_name); + if (error_code_ret != LTTNG_OK) { + ERR("Error enabling map \"%s\"", opt_map_name); + goto error; + } + + MSG("Enabled map `%s`.", opt_map_name); + + ret = 0; + goto end; + +error: + ret = 1; + +end: + argpar_parse_ret_fini(&argpar_parse_ret); + + return ret; +} diff --git a/src/bin/lttng/commands/list.c b/src/bin/lttng/commands/list.c index 454017794..8b4d466f1 100644 --- a/src/bin/lttng/commands/list.c +++ b/src/bin/lttng/commands/list.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../command.h" @@ -28,6 +29,7 @@ static int opt_jul; static int opt_log4j; static int opt_python; static char *opt_channel; +static char *opt_map; static int opt_domain; static int opt_fields; static int opt_syscall; @@ -63,6 +65,7 @@ static struct poptOption long_options[] = { {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, {"channel", 'c', POPT_ARG_STRING, &opt_channel, 0, 0, 0}, + {"map", 'm', POPT_ARG_STRING, &opt_map, 0, 0, 0}, {"domain", 'd', POPT_ARG_VAL, &opt_domain, 1, 0, 0}, {"fields", 'f', POPT_ARG_VAL, &opt_fields, 1, 0, 0}, {"syscall", 'S', POPT_ARG_VAL, &opt_syscall, 1, 0, 0}, @@ -1367,6 +1370,60 @@ skip_stats_printing: return; } +static void print_map(const struct lttng_map *map) +{ + const char *map_name; + enum lttng_map_status map_status; + enum lttng_buffer_type buffer_type; + enum lttng_map_bitness bitness; + enum lttng_map_boundary_policy boundary_policy; + uint64_t bucket_count; + bool is_enabled; + bool coalesces_hits; + + bitness = lttng_map_get_bitness(map); + is_enabled = lttng_map_get_is_enabled(map); + buffer_type = lttng_map_get_buffer_type(map); + coalesces_hits = lttng_map_get_coalesce_hits(map); + boundary_policy = lttng_map_get_boundary_policy(map); + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to get map name"); + goto end; + } + + map_status = lttng_map_get_dimension_length(map, 0, &bucket_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to bucket count"); + goto end; + } + + MSG("- %s (%s)", map_name, is_enabled ? "enabled": "disabled"); + MSG("%sAttributes:", indent4); + MSG("%sBitness: %s", indent6, bitness == LTTNG_MAP_BITNESS_32BITS ? "32" : "64"); + _MSG("%sCounter type: ", indent6); + switch (buffer_type) { + case LTTNG_BUFFER_PER_PID: + MSG("per-pid"); + break; + case LTTNG_BUFFER_PER_UID: + MSG("per-uid"); + break; + case LTTNG_BUFFER_GLOBAL: + MSG("global"); + break; + default: + abort(); + } + MSG("%sBoundary policy: %s", indent6, + boundary_policy == LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW ? "OVERFLOW" : ""); + MSG("%sBucket count: %"PRIu64, indent6, bucket_count); + MSG("%sCoalesces hits: %s", indent6, coalesces_hits ? "TRUE":"FALSE"); +end: + return; +} + /* * Machine interface * Print a list of channel @@ -1508,6 +1565,67 @@ error_channels: return ret; } +/* + * List map(s) of session and domain. + * + * If map_name is NULL, all maps are listed. + */ +static int list_maps(const char *desired_map_name) +{ + struct lttng_map_list *map_list = NULL; + enum lttng_map_status map_status; + enum lttng_error_code ret_code; + unsigned int i, map_count; + int ret = CMD_SUCCESS; + + DBG("Listing map(s) (%s)", desired_map_name ? : ""); + + ret_code = lttng_list_maps(handle, &map_list); + if (ret_code != LTTNG_OK) { + ERR("Error getting map list"); + ret = CMD_ERROR; + goto end; + } + + map_status = lttng_map_list_get_count(map_list, &map_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting map list element count"); + ret = CMD_ERROR; + goto end; + } + + MSG("Maps:\n-------------"); + for (i = 0; i < map_count; i++) { + const struct lttng_map *map = NULL; + const char *map_name = NULL; + map = lttng_map_list_get_at_index(map_list, i); + if (!map) { + ret = CMD_ERROR; + ERR("Error getting map from list: index = %u", i); + goto end; + } + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting map name"); + ret = CMD_ERROR; + goto end; + } + + if (desired_map_name != NULL) { + if (strncmp(map_name, desired_map_name, NAME_MAX) == 0) { + print_map(map); + break; + } + } else { + print_map(map); + } + } +end: + lttng_map_list_destroy(map_list); + return ret; +} + static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr) { switch (process_attr) { @@ -2460,6 +2578,11 @@ int cmd_list(int argc, const char **argv) goto end; } + ret = list_maps(opt_map); + if (ret) { + goto end; + } + if (lttng_opt_mi) { /* Close domain and domain element */ ret = mi_lttng_close_multi_element(writer, 2); @@ -2560,6 +2683,11 @@ int cmd_list(int argc, const char **argv) goto end; } + ret = list_maps(opt_map); + if (ret) { + goto end; + } + next_domain: if (lttng_opt_mi) { /* Close domain element */ diff --git a/src/bin/lttng/commands/list_triggers.c b/src/bin/lttng/commands/list_triggers.c index a6f021193..e9ad3d661 100644 --- a/src/bin/lttng/commands/list_triggers.c +++ b/src/bin/lttng/commands/list_triggers.c @@ -14,8 +14,20 @@ #include "common/mi-lttng.h" /* For lttng_condition_type_str(). */ #include "lttng/condition/condition-internal.h" +#include "lttng/condition/on-event.h" +#include "lttng/condition/on-event-internal.h" /* For lttng_domain_type_str(). */ #include "lttng/domain-internal.h" +#include "lttng/event-rule/event-rule-internal.h" +#include "lttng/event-rule/kernel-probe.h" +#include "lttng/event-rule/kernel-probe-internal.h" +#include "lttng/event-rule/syscall.h" +#include "lttng/event-rule/tracepoint.h" +#include "lttng/event-rule/userspace-probe.h" +#include "lttng/map-key.h" +#include "lttng/map-key-internal.h" +#include "lttng/trigger/trigger-internal.h" +#include "lttng/kernel-probe.h" #ifdef LTTNG_EMBED_HELP static const char help_msg[] = @@ -43,6 +55,7 @@ void print_event_rule_tracepoint(const struct lttng_event_rule *event_rule) const char *pattern; const char *filter; int log_level; + const struct lttng_log_level_rule *log_level_rule = NULL; unsigned int exclusions_count; int i; @@ -65,20 +78,33 @@ void print_event_rule_tracepoint(const struct lttng_event_rule *event_rule) assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET); } - event_rule_status = lttng_event_rule_tracepoint_get_log_level( - event_rule, &log_level); + event_rule_status = lttng_event_rule_tracepoint_get_log_level_rule( + event_rule, &log_level_rule); if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) { - enum lttng_loglevel_type log_level_type; + enum lttng_log_level_rule_status llr_status; const char *log_level_op; - event_rule_status = lttng_event_rule_tracepoint_get_log_level_type( - event_rule, &log_level_type); - assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK); - assert(log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE || - log_level_type == LTTNG_EVENT_LOGLEVEL_SINGLE); + switch (lttng_log_level_rule_get_type(log_level_rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &log_level); + log_level_op = "=="; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + log_level_op = "<="; + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &log_level); + break; + default: + abort(); + } - log_level_op = (log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE ? "<=" : "=="); + assert(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + /* TODO: here the raw level value must be printed since the + * value could have no known string equivalent, the string + * representation is only a "convenience". + */ _MSG(", log level %s %s", log_level_op, mi_lttng_loglevel_string( log_level, domain_type)); @@ -160,21 +186,21 @@ end: } static -void print_event_rule_kprobe(const struct lttng_event_rule *event_rule) +void print_event_rule_kernel_probe(const struct lttng_event_rule *event_rule) { enum lttng_event_rule_status event_rule_status; const char *name; const struct lttng_kernel_probe_location *location; - assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KPROBE); + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE); - event_rule_status = lttng_event_rule_kprobe_get_name(event_rule, &name); + event_rule_status = lttng_event_rule_kernel_probe_get_event_name(event_rule, &name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to get kprobe event rule's name."); goto end; } - event_rule_status = lttng_event_rule_kprobe_get_location( + event_rule_status = lttng_event_rule_kernel_probe_get_location( event_rule, &location); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to get kprobe event rule's location."); @@ -192,22 +218,22 @@ end: } static -void print_event_rule_uprobe(const struct lttng_event_rule *event_rule) +void print_event_rule_userspace_probe(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); + assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE); - event_rule_status = lttng_event_rule_uprobe_get_name(event_rule, &name); + event_rule_status = lttng_event_rule_userspace_probe_get_event_name(event_rule, &name); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to get uprobe event rule's name."); goto end; } - event_rule_status = lttng_event_rule_uprobe_get_location( + event_rule_status = lttng_event_rule_userspace_probe_get_location( event_rule, &location); if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to get uprobe event rule's location."); @@ -280,11 +306,11 @@ void print_event_rule(const struct lttng_event_rule *event_rule) 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); + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: + print_event_rule_kernel_probe(event_rule); break; - case LTTNG_EVENT_RULE_TYPE_UPROBE: - print_event_rule_uprobe(event_rule); + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: + print_event_rule_userspace_probe(event_rule); break; case LTTNG_EVENT_RULE_TYPE_SYSCALL: print_event_rule_syscall(event_rule); @@ -295,16 +321,180 @@ void print_event_rule(const struct lttng_event_rule *event_rule) } static -void print_condition_event_rule_hit(const struct lttng_condition *condition) +void print_one_event_expr(const struct lttng_event_expr *event_expr) +{ + enum lttng_event_expr_type type; + + type = lttng_event_expr_get_type(event_expr); + + switch (type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: { + const char *name; + + name = lttng_event_expr_event_payload_field_get_name(event_expr); + _MSG("%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: { + const char *name; + + name = lttng_event_expr_channel_context_field_get_name(event_expr); + _MSG("$ctx.%s", name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: { + const char *provider_name; + const char *type_name; + + provider_name = + lttng_event_expr_app_specific_context_field_get_provider_name( + event_expr); + type_name = + lttng_event_expr_app_specific_context_field_get_type_name( + event_expr); + + _MSG("$app.%s:%s", provider_name, type_name); + + break; + } + + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: { + unsigned int index; + const struct lttng_event_expr *parent_expr; + enum lttng_event_expr_status status; + + parent_expr = lttng_event_expr_array_field_element_get_parent_expr( + event_expr); + assert(parent_expr != NULL); + + print_one_event_expr(parent_expr); + + status = lttng_event_expr_array_field_element_get_index( + event_expr, &index); + assert(status == LTTNG_EVENT_EXPR_STATUS_OK); + + _MSG("[%u]", index); + + break; + } + + default: + abort(); + } +} + +static +void print_condition_on_event(const struct lttng_condition *condition) { const struct lttng_event_rule *event_rule; enum lttng_condition_status condition_status; + unsigned int cap_desc_count, i; + uint64_t error_count; condition_status = - lttng_condition_event_rule_get_rule(condition, &event_rule); + lttng_condition_on_event_get_rule(condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); print_event_rule(event_rule); + + condition_status + = lttng_condition_on_event_get_capture_descriptor_count( + condition, &cap_desc_count); + assert(condition_status == LTTNG_CONDITION_STATUS_OK); + + error_count = lttng_condition_on_event_get_error_count(condition); + MSG(" tracer notifications discarded: %ld", error_count); + + if (cap_desc_count > 0) { + MSG(" captures:"); + + for (i = 0; i < cap_desc_count; i++) { + const struct lttng_event_expr *cap_desc = + lttng_condition_on_event_get_capture_descriptor_at_index( + condition, i); + + _MSG(" - "); + print_one_event_expr(cap_desc); + MSG(""); + } + } +} + +static +void print_map_key(const struct lttng_map_key *key) +{ + unsigned int i, token_count; + enum lttng_map_key_status key_status; + + _MSG(" key: `"); + key_status = lttng_map_key_get_token_count(key, &token_count); + assert(key_status == LTTNG_MAP_KEY_STATUS_OK); + + for (i = 0; i < token_count; i++) { + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + assert(token); + + switch (token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + const struct lttng_map_key_token_string *str_token; + str_token = (typeof(str_token)) token; + _MSG("%s", str_token->string); + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + const struct lttng_map_key_token_variable *var_token; + var_token = (typeof(var_token)) token; + + switch (var_token->type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + _MSG("${EVENT_NAME}"); + break; + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + _MSG("${PROVIDER_NAME}"); + break; + default: + abort(); + } + + break; + } + default: + abort(); + } + } + MSG("`"); +} + +static +void print_one_incr_value_action(const struct lttng_action *action) +{ + enum lttng_action_status action_status; + const char *session_name, *map_name; + const struct lttng_map_key *key; + + action_status = lttng_action_incr_value_get_session_name( + action, &session_name); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + action_status = lttng_action_incr_value_get_map_name( + action, &map_name); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + action_status = lttng_action_incr_value_get_key( + action, &key); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + MSG("increment value:"); + MSG(" session: `%s`", session_name); + MSG(" map: `%s`", map_name); + print_map_key(key); } static @@ -318,6 +508,9 @@ void print_one_action(const struct lttng_action *action) assert(action_type != LTTNG_ACTION_TYPE_GROUP); switch (action_type) { + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + print_one_incr_value_action(action); + break; case LTTNG_ACTION_TYPE_NOTIFY: MSG("notify"); break; @@ -447,8 +640,8 @@ void print_one_trigger(const struct lttng_trigger *trigger) condition_type = lttng_condition_get_type(condition); MSG(" condition: %s", lttng_condition_type_str(condition_type)); switch (condition_type) { - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: - print_condition_event_rule_hit(condition); + case LTTNG_CONDITION_TYPE_ON_EVENT: + print_condition_on_event(condition); break; default: MSG(" (condition type not handled in %s)", __func__); diff --git a/src/bin/lttng/commands/view_map.c b/src/bin/lttng/commands/view_map.c new file mode 100644 index 000000000..c142fc383 --- /dev/null +++ b/src/bin/lttng/commands/view_map.c @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "../command.h" + +#include "common/argpar/argpar.h" + +enum { + OPT_HELP, + OPT_SESSION, + OPT_LIST_OPTIONS, + OPT_USERSPACE, + OPT_KERNEL, + OPT_KEY, +}; + +static const +struct argpar_opt_descr view_map_options[] = { + { OPT_HELP, 'h', "help", false }, + { OPT_SESSION, 's', "session", true }, + { OPT_LIST_OPTIONS, '\0', "list-options", false }, + /* Domains */ + { OPT_USERSPACE, 'u', "userspace", false }, + { OPT_KERNEL, 'k', "kernel", false }, + { OPT_KEY, '\0', "key", true }, + ARGPAR_OPT_DESCR_SENTINEL, +}; + +static +bool assign_domain_type(enum lttng_domain_type *dest, + enum lttng_domain_type src) +{ + bool ret; + + if (*dest == LTTNG_DOMAIN_NONE || *dest == src) { + *dest = src; + ret = true; + } else { + ERR("Multiple domains specified."); + ret = false; + } + + return ret; +} + +static +bool assign_string(char **dest, const char *src, const char *opt_name) +{ + bool ret; + + if (*dest) { + ERR("Duplicate %s given.", opt_name); + goto error; + } + + *dest = strdup(src); + if (!*dest) { + ERR("Failed to allocate %s string.", opt_name); + goto error; + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +static +int compare_key_value_by_key(const void *a, const void *b) +{ + const struct lttng_map_key_value_pair *kv_a = + *((const struct lttng_map_key_value_pair **) a); + const struct lttng_map_key_value_pair *kv_b = + *((const struct lttng_map_key_value_pair **) b); + const char *key_a, *key_b; + enum lttng_map_status map_status; + + map_status = lttng_map_key_value_pair_get_key(kv_a, &key_a); + assert(map_status == LTTNG_MAP_STATUS_OK); + + map_status = lttng_map_key_value_pair_get_key(kv_b, &key_b); + assert(map_status == LTTNG_MAP_STATUS_OK); + + return strcmp(key_a, key_b); +} + +static +void print_one_map_key_value_pair(const struct lttng_map_key_value_pair *kv_pair, + size_t key_len, size_t val_len) +{ + const char *key = NULL; + int64_t value; + enum lttng_map_status status; + bool has_overflowed, has_underflowed; + + status = lttng_map_key_value_pair_get_key(kv_pair, &key); + if (status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to get key-value pair's key."); + goto end; + } + + status = lttng_map_key_value_pair_get_value(kv_pair, &value); + if (status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to get value-value pair's value."); + goto end; + } + + has_overflowed = lttng_map_key_value_pair_get_has_overflowed(kv_pair); + has_underflowed = lttng_map_key_value_pair_get_has_underflowed(kv_pair); + + /* Ensure the padding is nice using the `%*s` delimiter. */ + MSG("| %*s | %*"PRId64" | %d | %d |", (int) -key_len, key, + (int) val_len, value, has_underflowed, has_overflowed); + +end: + return; +} + +static +void print_line(size_t key_len, size_t val_len) +{ + int i; + + _MSG("+"); + for (i = 0; i < (int) key_len + 2; i++) { + _MSG("-"); + } + _MSG("+"); + for (i = 0; i < (int) val_len + 2; i++) { + _MSG("-"); + } + MSG("+----+----+"); +} + +static +size_t number_of_digit(int64_t val) +{ + size_t ret = 0; + + if (val == 0) { + ret = 1; + goto end; + } + + if (val < 0) { + /* Account for the minus sign. */ + ret++; + val = llabs(val); + } + + /* + * SOURCE: + * https://stackoverflow.com/questions/1068849/how-do-i-determine-the-number-of-digits-of-an-integer-in-c + * If the log10() call becomes too expensive, we could use a + * recursive approach to count the digits. + */ + ret += floor(log10(val)) + 1; + +end: + return ret; +} + +static +void print_map_section_identifier(const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + switch (lttng_map_key_value_pair_list_get_type(kv_pair_list)) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL: + _MSG("Kernel global map"); + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED: + _MSG("Per-PID dead app aggregated map"); + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + _MSG("PID: %"PRIu64, lttng_map_key_value_pair_list_get_identifer( + kv_pair_list)); + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + _MSG("UID: %"PRIu64, lttng_map_key_value_pair_list_get_identifer( + kv_pair_list)); + break; + default: + break; + } + _MSG(", CPU: "); + if (lttng_map_key_value_pair_list_get_summed_all_cpu(kv_pair_list)) { + MSG("ALL"); + } else { + MSG("%"PRIu64, lttng_map_key_value_pair_list_get_cpu(kv_pair_list)); + } +} + +static +void print_map_table_header(size_t max_key_len, size_t max_val_len) +{ + print_line(max_key_len, max_val_len); + /* Ensure the padding is nice using the `%*s` delimiter. */ + MSG("| %*s | %*s | %s | %s |", (int) -max_key_len, "key", + (int) max_val_len, "val", "uf", "of"); +} + +static +enum lttng_error_code print_one_map_section( + const struct lttng_map_key_value_pair_list *kv_pair_list, + enum lttng_buffer_type buffer_type) +{ + enum lttng_error_code ret; + enum lttng_map_status map_status; + size_t longest_key_len = strlen("key"), longest_val_len = strlen("val"); + unsigned int i, key_value_pair_count; + struct lttng_dynamic_pointer_array sorted_kv_pair_list; + + lttng_dynamic_pointer_array_init(&sorted_kv_pair_list, NULL); + + map_status = lttng_map_key_value_pair_list_get_count(kv_pair_list, + &key_value_pair_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to get key-value pair count."); + goto error; + } + + for (i = 0; i < key_value_pair_count; i++) { + const char *cur_key; + int64_t cur_val; + const struct lttng_map_key_value_pair *pair = + lttng_map_key_value_pair_list_get_at_index( + kv_pair_list, i); + + /* Add all key value pairs to the sorting array. */ + lttng_dynamic_pointer_array_add_pointer(&sorted_kv_pair_list, + (void *) pair); + + /* Keep track of the longest key. */ + lttng_map_key_value_pair_get_key(pair, &cur_key); + longest_key_len = max(longest_key_len, strlen(cur_key)); + + /* Keep track of the longest value. */ + lttng_map_key_value_pair_get_value(pair, &cur_val); + longest_val_len = max(longest_val_len, number_of_digit(cur_val)); + } + + qsort(sorted_kv_pair_list.array.buffer.data, + lttng_dynamic_pointer_array_get_count(&sorted_kv_pair_list), + sizeof(struct lttng_map_key_value_pair *), + compare_key_value_by_key); + + print_map_section_identifier(kv_pair_list); + if (key_value_pair_count == 0) { + MSG(" No value in the map"); + ret = LTTNG_OK; + goto end; + } else { + print_map_table_header(longest_key_len, longest_val_len); + + for (i = 0; i < key_value_pair_count; i++) { + print_line(longest_key_len, longest_val_len); + + print_one_map_key_value_pair( + lttng_dynamic_pointer_array_get_pointer( + &sorted_kv_pair_list, i), + longest_key_len, longest_val_len); + } + + print_line(longest_key_len, longest_val_len); + } + + ret = LTTNG_OK; + goto end; + +error: + ret = LTTNG_ERR_MAP_VALUES_LIST_FAIL; +end: + lttng_dynamic_pointer_array_reset(&sorted_kv_pair_list); + + return ret; +} + +static +enum lttng_error_code print_one_map(struct lttng_handle *handle, + const struct lttng_map *map, const char *key_filter) +{ + enum lttng_error_code ret; + enum lttng_map_status map_status; + struct lttng_map_content *map_content = NULL; + unsigned int i, map_content_section_count; + enum lttng_buffer_type buffer_type; + struct lttng_map_query *map_query = NULL; + enum lttng_map_query_config_buffer query_buffer_config; + enum lttng_map_query_config_app_bitness query_bitness_config; + enum lttng_map_query_status query_status; + const char *map_name = NULL; + enum lttng_map_bitness map_bitness; + + map_status = lttng_map_get_name(map, &map_name); + assert(map_status == LTTNG_MAP_STATUS_OK); + + map_bitness = lttng_map_get_bitness(map); + + switch (lttng_map_get_buffer_type(map)) { + case LTTNG_BUFFER_GLOBAL: + query_buffer_config = LTTNG_MAP_QUERY_CONFIG_BUFFER_KERNEL_GLOBAL; + query_bitness_config = LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_KERNEL; + break; + case LTTNG_BUFFER_PER_UID: + query_buffer_config = LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL; + query_bitness_config = LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + break; + case LTTNG_BUFFER_PER_PID: + query_buffer_config = LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL; + query_bitness_config = LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + break; + default: + ret = LTTNG_ERR_INVALID; + goto end; + } + + map_query = lttng_map_query_create(LTTNG_MAP_QUERY_CONFIG_CPU_ALL, + query_buffer_config, query_bitness_config); + if (!map_query) { + ret = LTTNG_ERR_NOMEM; + ERR("Creating map query"); + goto end; + } + + if (key_filter) { + query_status = lttng_map_query_add_key_filter(map_query, key_filter); + assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + query_status = lttng_map_query_set_sum_by_cpu(map_query, true); + assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + + /* + * We don't want to aggregate all uid (or pid) together for the lttng + * view-map command. + */ + if (query_buffer_config == LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL) { + //query_status = lttng_map_query_set_sum_by_uid(map_query, false); + //assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + } else if (query_buffer_config == LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL) { + query_status = lttng_map_query_set_sum_by_pid(map_query, false); + assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + /* + * If we have 32bits and 64bits maps, we want to aggregate both maps in + * a single table in the lttng view-map command. + */ + if (query_bitness_config == LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL) { + //query_status = lttng_map_query_set_sum_by_app_bitness(map_query, true); + //assert(query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + /* Fetch the key value pair_list from the sessiond */ + ret = lttng_list_map_content(handle, map, map_query, &map_content); + if (ret != LTTNG_OK) { + ERR("Error listing map key value pair_list: %s.", + lttng_strerror(-ret)); + goto end; + } + + map_status = lttng_map_content_get_count(map_content, + &map_content_section_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + ERR("Failed to get map content section count."); + goto end; + } + + if (map_content_section_count == 0) { + DBG("Map %s is not found", map_name); + goto end; + } + + MSG("Session: '%s', map: '%s', map bitness: %d", handle->session_name, + map_name, map_bitness); + + buffer_type = lttng_map_content_get_buffer_type(map_content); + + for (i = 0; i < map_content_section_count; i++) { + const struct lttng_map_key_value_pair_list *kv_pair_list = + lttng_map_content_get_at_index(map_content, i); + + assert(kv_pair_list); + ret = print_one_map_section(kv_pair_list, buffer_type); + if (ret != LTTNG_OK) { + ERR("Error printing map section"); + goto end; + } + } + + + ret = LTTNG_OK; + goto end; +end: + lttng_map_content_destroy(map_content); + return ret; +} + +static +int view_map(struct lttng_handle *handle, const char *desired_map_name, + const char *key_filter) +{ + enum lttng_error_code ret; + struct lttng_map_list *map_list = NULL; + enum lttng_map_status map_status; + bool desired_map_found = false; + unsigned int i, map_count; + + DBG("Listing map(s) (%s)", desired_map_name ? : ""); + /* + * Query the sessiond for a list of all the maps that match the + * provided map name and domain (if any). + */ + ret = lttng_list_maps(handle, &map_list); + if (ret != LTTNG_OK) { + ERR("Error getting map list"); + goto end; + } + + map_status = lttng_map_list_get_count(map_list, &map_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting map list element count"); + ret = -1; + goto end; + } + + for (i = 0; i < map_count; i++) { + const struct lttng_map *map = NULL; + const char *map_name = NULL; + map = lttng_map_list_get_at_index(map_list, i); + if (!map) { + ERR("Error getting map from list: index = %u", i); + goto end; + } + + map_status = lttng_map_get_name(map, &map_name); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error getting map name"); + ret = -1; + goto end; + } + + + if (desired_map_name != NULL) { + if (strncmp(map_name, desired_map_name, NAME_MAX) == 0) { + desired_map_found = true; + ret = print_one_map(handle, map, key_filter); + if (ret != LTTNG_OK) { + ret = -1; + goto end; + } + } + } + } + + if (desired_map_name && !desired_map_found) { + DBG("Map %s in domain: %s (session %s)", desired_map_name, + lttng_strerror(-ret), handle->session_name); + ret = LTTNG_ERR_MAP_NOT_FOUND; + goto end; + } + +end: + lttng_map_list_destroy(map_list); + return ret; +} + +int cmd_view_map(int argc, const char **argv) +{ + struct argpar_parse_ret argpar_parse_ret = { 0 }; + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + const char *opt_map_name = NULL;; + char *opt_session_name = NULL, *session_name = NULL; + char *opt_key_filter = NULL; + struct lttng_domain domain; + struct lttng_domain *domains = NULL; + struct lttng_handle *handle; + + int ret, i; + + memset(&domain, 0, sizeof(domain)); + + argpar_parse_ret = argpar_parse(argc - 1, argv + 1, + view_map_options, true); + if (!argpar_parse_ret.items) { + ERR("%s", argpar_parse_ret.error); + goto error; + } + + for (i = 0; i < argpar_parse_ret.items->n_items; i++) { + struct argpar_item *item = argpar_parse_ret.items->items[i]; + + if (item->type == ARGPAR_ITEM_TYPE_OPT) { + struct argpar_item_opt *item_opt = + (struct argpar_item_opt *) item; + + switch (item_opt->descr->id) { + case OPT_HELP: + SHOW_HELP(); + ret = CMD_SUCCESS; + goto end; + case OPT_SESSION: + if (!assign_string(&opt_session_name, item_opt->arg, + "-s/--session")) { + goto error; + } + break; + case OPT_LIST_OPTIONS: + list_cmd_options_argpar(stdout, + view_map_options); + ret = CMD_SUCCESS; + goto end; + 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_KEY: + if (!assign_string(&opt_key_filter, item_opt->arg, + "--key")) { + goto error; + } + break; + + default: + abort(); + } + + } else { + struct argpar_item_non_opt *item_non_opt = + (struct argpar_item_non_opt *) item; + + if (opt_map_name) { + ERR("Unexpected argument: %s", item_non_opt->arg); + goto error; + } + opt_map_name = item_non_opt->arg; + } + } + + if (!opt_map_name) { + ERR("Map name must be provided"); + goto error; + } + + if (!opt_session_name) { + DBG("No session name provided, print maps of the default session"); + session_name = get_session_name(); + if (session_name == NULL) { + goto error; + } + } else { + session_name = opt_session_name; + } + + domain.type = domain_type; + handle = lttng_create_handle(session_name, &domain); + if (handle == NULL) { + ret = CMD_FATAL; + goto end; + } + + if (domain.type != LTTNG_DOMAIN_NONE) { + /* Print maps of the given domain. */ + ret = view_map(handle, opt_map_name, opt_key_filter); + if (ret != LTTNG_OK) { + goto error; + } + } else { + int domain_idx, nb_domain; + bool found_one_map = false; + + /* We want all domain(s) */ + nb_domain = lttng_list_domains(session_name, &domains); + if (nb_domain < 0) { + ret = CMD_ERROR; + ERR("%s", lttng_strerror(nb_domain)); + goto end; + } + + for (domain_idx = 0; domain_idx < nb_domain; domain_idx++) { + /* Clean handle before creating a new one */ + if (handle) { + lttng_destroy_handle(handle); + } + + handle = lttng_create_handle(session_name, &domains[domain_idx]); + if (handle == NULL) { + ret = CMD_FATAL; + goto end; + } + + ret = view_map(handle, opt_map_name, opt_key_filter); + + if (ret == LTTNG_OK) { + found_one_map = true; + } else if (ret == LTTNG_ERR_MAP_NOT_FOUND) { + DBG("Map not found in the current domain"); + continue; + } else { + goto error; + } + } + + if (!found_one_map) { + ERR("Map %s not found on any of the domains", opt_map_name); + goto error; + + } + } + + ret = CMD_SUCCESS; + goto end; +error: + ret = CMD_ERROR; + +end: + if (!opt_session_name && session_name) { + free(session_name); + } + + if (opt_key_filter) { + free(opt_key_filter); + } + + argpar_parse_ret_fini(&argpar_parse_ret); + return ret; +} diff --git a/src/bin/lttng/lttng.c b/src/bin/lttng/lttng.c index 437a9cc4c..61ed24773 100644 --- a/src/bin/lttng/lttng.c +++ b/src/bin/lttng/lttng.c @@ -73,6 +73,9 @@ static struct cmd_struct commands[] = { { "disable-event", cmd_disable_events}, { "enable-channel", cmd_enable_channels}, { "enable-event", cmd_enable_events}, + { "add-map", cmd_add_map}, + { "enable-map", cmd_enable_map}, + { "disable-map", cmd_disable_map}, { "help", NULL}, { "list", cmd_list}, { "list-triggers", cmd_list_triggers}, @@ -93,6 +96,7 @@ static struct cmd_struct commands[] = { { "untrack", cmd_untrack}, { "version", cmd_version}, { "view", cmd_view}, + { "view-map", cmd_view_map}, { NULL, NULL} /* Array closure */ }; @@ -286,6 +290,10 @@ static void show_basic_help(void) puts(" disable-channel " CONFIG_CMD_DESCR_DISABLE_CHANNEL); puts(" enable-channel " CONFIG_CMD_DESCR_ENABLE_CHANNEL); puts(""); + puts("Maps:"); + puts(" add-map " CONFIG_CMD_DESCR_ADD_MAP); + puts(" remove-map " CONFIG_CMD_DESCR_ADD_MAP); + puts(""); puts("Event rules:"); puts(" disable-event " CONFIG_CMD_DESCR_DISABLE_EVENT); puts(" enable-event " CONFIG_CMD_DESCR_ENABLE_EVENT); diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 4b549aa08..339a55492 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -36,6 +36,7 @@ EXTRA_DIST = mi-lttng-4.0.xsd libcommon_la_SOURCES = \ actions/action.c \ actions/group.c \ + actions/incr-value.c \ actions/notify.c \ actions/rotate-session.c \ actions/snapshot-session.c \ @@ -45,7 +46,7 @@ libcommon_la_SOURCES = \ common.h \ conditions/buffer-usage.c \ conditions/condition.c \ - conditions/event-rule.c \ + conditions/on-event.c \ conditions/session-consumed-size.c \ conditions/session-rotation.c \ context.c context.h \ @@ -62,16 +63,23 @@ libcommon_la_SOURCES = \ event-expr-to-bytecode.c event-expr-to-bytecode.h \ event-field-value.c \ event-rule/event-rule.c \ - event-rule/kprobe.c \ + event-rule/kernel-function.c \ + event-rule/kernel-probe.c \ event-rule/syscall.c \ - event-rule/uprobe.c \ + event-rule/userspace-probe.c \ event-rule/tracepoint.c \ filter.c filter.h \ fd-handle.c fd-handle.h \ fs-handle.c fs-handle.h fs-handle-internal.h \ futex.c futex.h \ + kernel-function.c \ kernel-probe.c \ + index-allocator.c index-allocator.h \ location.c \ + log-level-rule.c \ + map.c \ + map-query.c \ + map-key/map-key.c \ mi-lttng.c mi-lttng.h \ notification.c \ optional.h \ @@ -80,6 +88,7 @@ libcommon_la_SOURCES = \ pipe.c pipe.h \ readwrite.c readwrite.h \ runas.c runas.h \ + shm.c shm.h \ session-descriptor.c \ snapshot.c snapshot.h \ spawn-viewer.c spawn-viewer.h \ diff --git a/src/common/actions/action.c b/src/common/actions/action.c index 95a0c0f4d..2bf5e4877 100644 --- a/src/common/actions/action.c +++ b/src/common/actions/action.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ const char *lttng_action_type_string(enum lttng_action_type action_type) return "START_SESSION"; case LTTNG_ACTION_TYPE_STOP_SESSION: return "STOP_SESSION"; + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + return "INCREMENT_VALUE"; default: return "???"; } @@ -182,6 +185,10 @@ ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view, create_from_payload_cb = lttng_action_stop_session_create_from_payload; break; + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + create_from_payload_cb = + lttng_action_incr_value_create_from_payload; + break; case LTTNG_ACTION_TYPE_GROUP: create_from_payload_cb = lttng_action_group_create_from_payload; break; diff --git a/src/common/actions/group.c b/src/common/actions/group.c index 4ac239c9d..f7bbfd5a9 100644 --- a/src/common/actions/group.c +++ b/src/common/actions/group.c @@ -353,3 +353,11 @@ const struct lttng_action *lttng_action_group_get_at_index( end: return action; } + +struct lttng_action *lttng_action_group_get_mutable_at_index( + struct lttng_action *group, + unsigned int index) +{ + return (struct lttng_action *) lttng_action_group_get_at_index( + (const struct lttng_action *) group, index); +} diff --git a/src/common/actions/incr-value.c b/src/common/actions/incr-value.c new file mode 100644 index 000000000..0c4ddc2d3 --- /dev/null +++ b/src/common/actions/incr-value.c @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define IS_INCR_VALUE_ACTION(action) \ + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_INCREMENT_VALUE) + +struct lttng_action_incr_value { + struct lttng_action parent; + + /* Owned by this. */ + struct lttng_map_key *key; + /* Owned by this. */ + char *session_name; + /* Owned by this. */ + char *map_name; + + LTTNG_OPTIONAL(uint64_t) action_tracer_token; +}; + +struct lttng_action_incr_value_comm { + /* Includes the trailing \0. */ + uint32_t session_name_len; + /* Includes the trailing \0. */ + uint32_t map_name_len; + + /* + * Variable data: + * + * - struct lttng_map_key object with variable data + * - session name (null terminated) + * - map name (null terminated) + */ + char data[]; +} LTTNG_PACKED; + +static struct lttng_action_incr_value *action_incr_value_from_action( + struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_incr_value, parent); +} + +static const struct lttng_action_incr_value * +action_incr_value_from_action_const(const struct lttng_action *action) +{ + assert(action); + + return container_of(action, struct lttng_action_incr_value, parent); +} + +static bool lttng_action_incr_value_validate(struct lttng_action *action) +{ + bool valid; + struct lttng_action_incr_value *action_incr_value; + + if (!action) { + valid = false; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + /* Non null key is mandatory. */ + if (!action_incr_value->key) { + valid = false; + goto end; + } + + /* A non-empty session name is mandatory. */ + if (!action_incr_value->session_name || + strlen(action_incr_value->session_name) == 0) { + valid = false; + goto end; + } + + /* A non-empty map name is mandatory. */ + if (!action_incr_value->map_name || + strlen(action_incr_value->map_name) == 0) { + valid = false; + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_incr_value_is_equal( + const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + const struct lttng_action_incr_value *a, *b; + + a = action_incr_value_from_action_const(_a); + b = action_incr_value_from_action_const(_b); + + /* Action is not valid if this is not true. */ + assert(a->key); + assert(b->key); + assert(a->session_name); + assert(b->session_name); + assert(a->map_name); + assert(b->map_name); + + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + if (strcmp(a->map_name, b->map_name)) { + goto end; + } + + is_equal = lttng_map_key_is_equal(a->key, b->key); + +end: + return is_equal; +} + +static int lttng_action_incr_value_serialize( + struct lttng_action *action, struct lttng_payload *payload) +{ + struct lttng_action_incr_value *action_incr_value; + struct lttng_action_incr_value_comm comm; + size_t session_name_len, map_name_len; + int ret; + + assert(action); + assert(payload); + + action_incr_value = action_incr_value_from_action(action); + + DBG("Serializing increment value action"); + + session_name_len = strlen(action_incr_value->session_name) + 1; + comm.session_name_len = session_name_len; + + map_name_len = strlen(action_incr_value->map_name) + 1; + comm.map_name_len = map_name_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_map_key_serialize(action_incr_value->key, payload); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + action_incr_value->session_name, session_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + action_incr_value->map_name, map_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +static void lttng_action_incr_value_destroy(struct lttng_action *action) +{ + struct lttng_action_incr_value *action_incr_value; + + if (!action) { + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + lttng_map_key_destroy(action_incr_value->key); + + free(action_incr_value->session_name); + free(action_incr_value->map_name); + free(action_incr_value); + +end: + return; +} + +ssize_t lttng_action_incr_value_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len, consumed_key_len, ret; + struct lttng_map_key *key = NULL; + const struct lttng_action_incr_value_comm *comm; + const char *session_name, *map_name; + struct lttng_action *action; + enum lttng_action_status status; + + action = lttng_action_incr_value_create(); + if (!action) { + consumed_len = -1; + goto error; + } + + comm = (typeof(comm)) view->buffer.data; + consumed_len = sizeof(struct lttng_action_incr_value_comm); + + { + struct lttng_payload_view key_view = + lttng_payload_view_from_view(view, consumed_len, + view->buffer.size - consumed_len); + + if (!lttng_payload_view_is_valid(&key_view)) { + consumed_len = -1; + goto end; + } + ret = lttng_map_key_create_from_payload(&key_view, &key); + if (ret <= 0) { + consumed_len = -1; + goto error; + } + consumed_key_len = ret; + } + + consumed_len += consumed_key_len; + session_name = (const char *) &comm->data + consumed_key_len; + + if (!lttng_buffer_view_contains_string( + &view->buffer, session_name, comm->session_name_len)) { + consumed_len = -1; + goto error; + } + + consumed_len += comm->session_name_len; + + map_name = (const char *) &comm->data + consumed_key_len + comm->session_name_len; + + if (!lttng_buffer_view_contains_string( + &view->buffer, map_name, comm->map_name_len)) { + consumed_len = -1; + goto error; + } + + consumed_len += comm->map_name_len; + + status = lttng_action_incr_value_set_key(action, key); + /* Ownership is passed to the action. */ + lttng_map_key_put(key); + + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto error; + } + + status = lttng_action_incr_value_set_session_name( + action, session_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto error; + } + + status = lttng_action_incr_value_set_map_name( + action, map_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto error; + } + + *p_action = action; + action = NULL; + goto end; + +error: + lttng_action_incr_value_destroy(action); + consumed_len = -1; + +end: + return consumed_len; +} + +struct lttng_action *lttng_action_incr_value_create(void) +{ + struct lttng_action *action; + + action = zmalloc(sizeof(struct lttng_action_incr_value)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_INCREMENT_VALUE, + lttng_action_incr_value_validate, + lttng_action_incr_value_serialize, + lttng_action_incr_value_is_equal, + lttng_action_incr_value_destroy); + +end: + return action; +} + +enum lttng_action_status lttng_action_incr_value_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !session_name || + strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + free(action_incr_value->session_name); + + action_incr_value->session_name = strdup(session_name); + if (!action_incr_value->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_incr_value_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action_const(action); + + *session_name = action_incr_value->session_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_incr_value_set_map_name( + struct lttng_action *action, const char *map_name) +{ + struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !map_name || + strlen(map_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + free(action_incr_value->map_name); + + action_incr_value->map_name = strdup(map_name); + if (!action_incr_value->map_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_incr_value_get_map_name( + const struct lttng_action *action, const char **map_name) +{ + const struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !map_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action_const(action); + + *map_name = action_incr_value->map_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +LTTNG_HIDDEN +enum lttng_action_status lttng_action_incr_value_borrow_key_mutable( + const struct lttng_action *action, struct lttng_map_key **key) +{ + const struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !key) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action_const(action); + + *key = action_incr_value->key; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_incr_value_get_key( + const struct lttng_action *action, + const struct lttng_map_key **key) +{ + struct lttng_map_key *mutable_key = NULL; + enum lttng_action_status status = + lttng_action_incr_value_borrow_key_mutable( + action, &mutable_key); + + *key = mutable_key; + return status; +} + +enum lttng_action_status +lttng_action_incr_value_set_key( + struct lttng_action *action, + struct lttng_map_key *key) +{ + struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action) || !key) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + /* Take a reference to the key. */ + lttng_map_key_get(key); + action_incr_value->key = key; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +LTTNG_HIDDEN +enum lttng_action_status lttng_action_incr_value_set_tracer_token( + struct lttng_action *action, uint64_t token) +{ + struct lttng_action_incr_value *action_incr_value; + enum lttng_action_status status; + + if (!action || !IS_INCR_VALUE_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_incr_value = action_incr_value_from_action(action); + + LTTNG_OPTIONAL_SET(&action_incr_value->action_tracer_token, token); + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +LTTNG_HIDDEN +uint64_t lttng_action_incr_value_get_tracer_token( + const struct lttng_action *action) +{ + const struct lttng_action_incr_value *action_incr_value; + + assert(action); + assert(IS_INCR_VALUE_ACTION(action)); + + action_incr_value = action_incr_value_from_action_const(action); + + return LTTNG_OPTIONAL_GET(action_incr_value->action_tracer_token); +} + diff --git a/src/common/conditions/condition.c b/src/common/conditions/condition.c index e0b85e429..4948dbd3f 100644 --- a/src/common/conditions/condition.c +++ b/src/common/conditions/condition.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -170,8 +170,8 @@ ssize_t lttng_condition_create_from_payload( case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: create_from_payload = lttng_condition_session_rotation_completed_create_from_payload; break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: - create_from_payload = lttng_condition_event_rule_create_from_payload; + case LTTNG_CONDITION_TYPE_ON_EVENT: + create_from_payload = lttng_condition_on_event_create_from_payload; break; default: ERR("Attempted to create condition of unknown type (%i)", @@ -230,7 +230,7 @@ const char *lttng_condition_type_str(enum lttng_condition_type type) case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: return "session rotation completed"; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: return "event rule hit"; default: diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/on-event.c similarity index 54% rename from src/common/conditions/event-rule.c rename to src/common/conditions/on-event.c index dcc31ef86..699de467d 100644 --- a/src/common/conditions/event-rule.c +++ b/src/common/conditions/on-event.c @@ -10,12 +10,14 @@ #include #include #include +#include #include -#include -#include -#include +#include +#include #include #include +#include +#include #include #include #include @@ -23,38 +25,38 @@ #define IS_EVENT_RULE_CONDITION(condition) \ (lttng_condition_get_type(condition) == \ - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) + LTTNG_CONDITION_TYPE_ON_EVENT) 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; + return type == LTTNG_CONDITION_TYPE_ON_EVENT; } -static bool lttng_condition_event_rule_validate( +static bool lttng_condition_on_event_validate( const struct lttng_condition *condition); -static int lttng_condition_event_rule_serialize( +static int lttng_condition_on_event_serialize( const struct lttng_condition *condition, struct lttng_payload *payload); -static bool lttng_condition_event_rule_is_equal( +static bool lttng_condition_on_event_is_equal( const struct lttng_condition *_a, const struct lttng_condition *_b); -static void lttng_condition_event_rule_destroy( +static void lttng_condition_on_event_destroy( struct lttng_condition *condition); -static bool lttng_condition_event_rule_validate( +static bool lttng_condition_on_event_validate( const struct lttng_condition *condition) { bool valid = false; - struct lttng_condition_event_rule *event_rule; + struct lttng_condition_on_event *event_rule; if (!condition) { goto end; } event_rule = container_of( - condition, struct lttng_condition_event_rule, parent); + condition, struct lttng_condition_on_event, parent); if (!event_rule->rule) { ERR("Invalid event rule condition: a rule must be set."); goto end; @@ -196,12 +198,12 @@ end: static struct lttng_capture_descriptor * -lttng_condition_event_rule_get_internal_capture_descriptor_at_index( +lttng_condition_on_event_get_internal_capture_descriptor_at_index( const struct lttng_condition *condition, unsigned int index) { - const struct lttng_condition_event_rule *event_rule_cond = + const struct lttng_condition_on_event *event_rule_cond = container_of(condition, - const struct lttng_condition_event_rule, + const struct lttng_condition_on_event, parent); struct lttng_capture_descriptor *desc = NULL; unsigned int count; @@ -211,7 +213,7 @@ lttng_condition_event_rule_get_internal_capture_descriptor_at_index( goto end; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition, &count); if (status != LTTNG_CONDITION_STATUS_OK) { goto end; @@ -227,13 +229,14 @@ end: return desc; } -static int lttng_condition_event_rule_serialize( +static int lttng_condition_on_event_serialize( const struct lttng_condition *condition, struct lttng_payload *payload) { int ret; - struct lttng_condition_event_rule *event_rule; + struct lttng_condition_on_event *event_rule; enum lttng_condition_status status; + uint64_t error_count, error_counter_index; /* Used for iteration and communication (size matters). */ uint32_t i, capture_descr_count; @@ -244,7 +247,7 @@ static int lttng_condition_event_rule_serialize( DBG("Serializing event rule condition"); event_rule = container_of( - condition, struct lttng_condition_event_rule, parent); + condition, struct lttng_condition_on_event, parent); DBG("Serializing event rule condition's event rule"); ret = lttng_event_rule_serialize(event_rule->rule, payload); @@ -252,7 +255,27 @@ static int lttng_condition_event_rule_serialize( goto end; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + error_counter_index = lttng_condition_on_event_get_error_counter_index( + condition); + DBG("Serializing event rule condition's error counter index: %" PRIu64, + error_counter_index); + ret = lttng_dynamic_buffer_append(&payload->buffer, &error_counter_index, + sizeof(error_counter_index)); + if (ret) { + goto end; + } + + error_count = lttng_condition_on_event_get_error_count( + condition); + DBG("Serializing event rule condition's error count: %" PRIu64, + error_count); + ret = lttng_dynamic_buffer_append(&payload->buffer, &error_count, + sizeof(error_count)); + if (ret) { + goto end; + } + + status = lttng_condition_on_event_get_capture_descriptor_count( condition, &capture_descr_count); if (status != LTTNG_CONDITION_STATUS_OK) { ret = -1; @@ -269,7 +292,7 @@ static int lttng_condition_event_rule_serialize( for (i = 0; i < capture_descr_count; i++) { const struct lttng_capture_descriptor *desc = - lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + lttng_condition_on_event_get_internal_capture_descriptor_at_index( condition, i); DBG("Serializing event rule condition's capture descriptor %" PRIu32, @@ -295,13 +318,13 @@ bool capture_descriptors_are_equal( size_t i; enum lttng_condition_status status; - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition_a, &capture_descr_count_a); if (status != LTTNG_CONDITION_STATUS_OK) { goto not_equal; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition_b, &capture_descr_count_b); if (status != LTTNG_CONDITION_STATUS_OK) { goto not_equal; @@ -313,11 +336,11 @@ bool capture_descriptors_are_equal( for (i = 0; i < capture_descr_count_a; i++) { const struct lttng_event_expr *expr_a = - lttng_condition_event_rule_get_capture_descriptor_at_index( + lttng_condition_on_event_get_capture_descriptor_at_index( condition_a, i); const struct lttng_event_expr *expr_b = - lttng_condition_event_rule_get_capture_descriptor_at_index( + lttng_condition_on_event_get_capture_descriptor_at_index( condition_b, i); @@ -335,15 +358,15 @@ end: return is_equal; } -static bool lttng_condition_event_rule_is_equal( +static bool lttng_condition_on_event_is_equal( const struct lttng_condition *_a, const struct lttng_condition *_b) { bool is_equal = false; - struct lttng_condition_event_rule *a, *b; + struct lttng_condition_on_event *a, *b; - a = container_of(_a, struct lttng_condition_event_rule, parent); - b = container_of(_b, struct lttng_condition_event_rule, parent); + a = container_of(_a, struct lttng_condition_on_event, parent); + b = container_of(_b, struct lttng_condition_on_event, parent); /* Both event rules must be set or both must be unset. */ if ((a->rule && !b->rule) || (!a->rule && b->rule)) { @@ -362,13 +385,13 @@ end: return is_equal; } -static void lttng_condition_event_rule_destroy( +static void lttng_condition_on_event_destroy( struct lttng_condition *condition) { - struct lttng_condition_event_rule *event_rule; + struct lttng_condition_on_event *event_rule; event_rule = container_of( - condition, struct lttng_condition_event_rule, parent); + condition, struct lttng_condition_on_event, parent); lttng_event_rule_put(event_rule->rule); lttng_dynamic_pointer_array_reset(&event_rule->capture_descriptors); @@ -386,32 +409,35 @@ void destroy_capture_descriptor(void *ptr) free(desc); } -struct lttng_condition *lttng_condition_event_rule_create( +struct lttng_condition *lttng_condition_on_event_create( struct lttng_event_rule *rule) { struct lttng_condition *parent = NULL; - struct lttng_condition_event_rule *condition = NULL; + struct lttng_condition_on_event *condition = NULL; if (!rule) { goto end; } - condition = zmalloc(sizeof(struct lttng_condition_event_rule)); + condition = zmalloc(sizeof(struct lttng_condition_on_event)); if (!condition) { return NULL; } lttng_condition_init(&condition->parent, - LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); - condition->parent.validate = lttng_condition_event_rule_validate, - condition->parent.serialize = lttng_condition_event_rule_serialize, - condition->parent.equal = lttng_condition_event_rule_is_equal, - condition->parent.destroy = lttng_condition_event_rule_destroy, + LTTNG_CONDITION_TYPE_ON_EVENT); + condition->parent.validate = lttng_condition_on_event_validate, + condition->parent.serialize = lttng_condition_on_event_serialize, + condition->parent.equal = lttng_condition_on_event_is_equal, + condition->parent.destroy = lttng_condition_on_event_destroy, lttng_event_rule_get(rule); condition->rule = rule; rule = NULL; + LTTNG_OPTIONAL_SET(&condition->error_count, 0); + LTTNG_OPTIONAL_SET(&condition->error_counter_index, 0); + lttng_dynamic_pointer_array_init(&condition->capture_descriptors, destroy_capture_descriptor); @@ -576,7 +602,7 @@ end: } LTTNG_HIDDEN -ssize_t lttng_condition_event_rule_create_from_payload( +ssize_t lttng_condition_on_event_create_from_payload( struct lttng_payload_view *view, struct lttng_condition **_condition) { @@ -584,6 +610,7 @@ ssize_t lttng_condition_event_rule_create_from_payload( size_t offset = 0; ssize_t event_rule_length; uint32_t i, capture_descr_count; + uint64_t error_counter_index, error_count; struct lttng_condition *condition = NULL; struct lttng_event_rule *event_rule = NULL; @@ -604,16 +631,33 @@ ssize_t lttng_condition_event_rule_create_from_payload( goto error; } - /* Create condition (no capture descriptors yet) at this point. */ - condition = lttng_condition_event_rule_create(event_rule); + offset += event_rule_length; + + /* Error counter index. */ + error_counter_index = uint_from_buffer(&view->buffer, sizeof(uint64_t), + &offset); + if (error_counter_index == UINT64_C(-1)) { + goto error; + } + + /* Error count. */ + error_count = uint_from_buffer(&view->buffer, sizeof(uint64_t), &offset); + if (error_count == UINT64_C(-1)) { + goto error; + } + + /* Create condition (no capture descriptors yet) at this point */ + condition = lttng_condition_on_event_create(event_rule); if (!condition) { goto error; } + lttng_condition_on_event_set_error_count(condition, error_count); + lttng_condition_on_event_set_error_counter_index(condition, + error_counter_index); /* Capture descriptor count. */ assert(event_rule_length >= 0); - offset += (size_t) event_rule_length; capture_descr_count = uint_from_buffer(&view->buffer, sizeof(uint32_t), &offset); if (capture_descr_count == UINT32_C(-1)) { goto error; @@ -630,7 +674,7 @@ ssize_t lttng_condition_event_rule_create_from_payload( } /* Move ownership of `expr` to `condition`. */ - status = lttng_condition_event_rule_append_capture_descriptor( + status = lttng_condition_on_event_append_capture_descriptor( condition, expr); if (status != LTTNG_CONDITION_STATUS_OK) { /* `expr` not moved: destroy it. */ @@ -654,11 +698,11 @@ end: } LTTNG_HIDDEN -enum lttng_condition_status lttng_condition_event_rule_borrow_rule_mutable( +enum lttng_condition_status lttng_condition_on_event_borrow_rule_mutable( const struct lttng_condition *condition, struct lttng_event_rule **rule) { - struct lttng_condition_event_rule *event_rule; + struct lttng_condition_on_event *event_rule; enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !rule) { @@ -667,7 +711,7 @@ enum lttng_condition_status lttng_condition_event_rule_borrow_rule_mutable( } event_rule = container_of( - condition, struct lttng_condition_event_rule, parent); + condition, struct lttng_condition_on_event, parent); if (!event_rule->rule) { status = LTTNG_CONDITION_STATUS_UNSET; goto end; @@ -678,30 +722,71 @@ end: return status; } -enum lttng_condition_status lttng_condition_event_rule_get_rule( +enum lttng_condition_status lttng_condition_on_event_get_rule( const struct lttng_condition *condition, const struct lttng_event_rule **rule) { struct lttng_event_rule *mutable_rule = NULL; const enum lttng_condition_status status = - lttng_condition_event_rule_borrow_rule_mutable( + lttng_condition_on_event_borrow_rule_mutable( condition, &mutable_rule); *rule = mutable_rule; return status; } +void lttng_condition_on_event_set_error_counter_index( + struct lttng_condition *condition, uint64_t error_counter_index) +{ + struct lttng_condition_on_event *on_event_cond = + container_of(condition, + struct lttng_condition_on_event, parent); + + LTTNG_OPTIONAL_SET(&on_event_cond->error_counter_index, error_counter_index); +} + +uint64_t lttng_condition_on_event_get_error_counter_index( + const struct lttng_condition *condition) +{ + struct lttng_condition_on_event *on_event_cond = + container_of(condition, + struct lttng_condition_on_event, parent); + + return LTTNG_OPTIONAL_GET(on_event_cond->error_counter_index); +} + +void lttng_condition_on_event_set_error_count(struct lttng_condition *condition, + uint64_t error_count) +{ + struct lttng_condition_on_event *on_event_cond = + container_of(condition, + struct lttng_condition_on_event, parent); + + LTTNG_OPTIONAL_SET(&on_event_cond->error_count, error_count); +} + +uint64_t lttng_condition_on_event_get_error_count( + const struct lttng_condition *condition) +{ + struct lttng_condition_on_event *on_event_cond = + container_of(condition, + struct lttng_condition_on_event, parent); + + return LTTNG_OPTIONAL_GET(on_event_cond->error_count); +} + enum lttng_condition_status -lttng_condition_event_rule_append_capture_descriptor( +lttng_condition_on_event_append_capture_descriptor( struct lttng_condition *condition, struct lttng_event_expr *expr) { int ret; enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - struct lttng_condition_event_rule *event_rule_cond = + struct lttng_condition_on_event *on_event_cond = container_of(condition, - struct lttng_condition_event_rule, parent); + struct lttng_condition_on_event, parent); struct lttng_capture_descriptor *descriptor = NULL; + const struct lttng_event_rule *rule = NULL; /* Only accept l-values. */ if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !expr || @@ -710,6 +795,29 @@ lttng_condition_event_rule_append_capture_descriptor( goto end; } + status = lttng_condition_on_event_get_rule(condition, &rule); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + switch(lttng_event_rule_get_type(rule)) { + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + /* Supported */ + status = LTTNG_CONDITION_STATUS_OK; + break; + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + status = LTTNG_CONDITION_STATUS_INVALID; + break; + default: + status = LTTNG_CONDITION_STATUS_UNSUPPORTED; + break; + } + + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + descriptor = malloc(sizeof(*descriptor)); if (descriptor == NULL) { status = LTTNG_CONDITION_STATUS_ERROR; @@ -720,7 +828,7 @@ lttng_condition_event_rule_append_capture_descriptor( descriptor->bytecode = NULL; ret = lttng_dynamic_pointer_array_add_pointer( - &event_rule_cond->capture_descriptors, descriptor); + &on_event_cond->capture_descriptors, descriptor); if (ret) { status = LTTNG_CONDITION_STATUS_ERROR; goto end; @@ -734,13 +842,13 @@ end: } enum lttng_condition_status -lttng_condition_event_rule_get_capture_descriptor_count( +lttng_condition_on_event_get_capture_descriptor_count( const struct lttng_condition *condition, unsigned int *count) { enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - const struct lttng_condition_event_rule *event_rule_cond = + const struct lttng_condition_on_event *event_rule_cond = container_of(condition, - const struct lttng_condition_event_rule, + const struct lttng_condition_on_event, parent); if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !count) { @@ -756,13 +864,13 @@ end: } const struct lttng_event_expr * -lttng_condition_event_rule_get_capture_descriptor_at_index( +lttng_condition_on_event_get_capture_descriptor_at_index( const struct lttng_condition *condition, unsigned int index) { const struct lttng_event_expr *expr = NULL; const struct lttng_capture_descriptor *desc = NULL; - desc = lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + desc = lttng_condition_on_event_get_internal_capture_descriptor_at_index( condition, index); if (desc == NULL) { goto end; @@ -775,6 +883,7 @@ end: LTTNG_HIDDEN ssize_t lttng_evaluation_event_rule_create_from_payload( + const struct lttng_condition_on_event *condition, struct lttng_payload_view *view, struct lttng_evaluation **_evaluation) { @@ -785,6 +894,8 @@ ssize_t lttng_evaluation_event_rule_create_from_payload( const struct lttng_payload_view header_view = lttng_payload_view_from_view( view, 0, sizeof(*header)); + uint32_t capture_payload_size; + const char *capture_payload = NULL; if (!_evaluation) { ret = -1; @@ -802,7 +913,7 @@ ssize_t lttng_evaluation_event_rule_create_from_payload( /* Map the originating trigger's name. */ offset += sizeof(*header); { - struct lttng_payload_view current_view = + const struct lttng_payload_view current_view = lttng_payload_view_from_view(view, offset, header->trigger_name_length); @@ -822,13 +933,40 @@ ssize_t lttng_evaluation_event_rule_create_from_payload( } offset += header->trigger_name_length; + { + const struct lttng_payload_view current_view = + lttng_payload_view_from_view(view, offset, -1); + + if (current_view.buffer.size < sizeof(capture_payload_size)) { + ret = -1; + goto error; + } - evaluation = lttng_evaluation_event_rule_create(trigger_name); + memcpy(&capture_payload_size, current_view.buffer.data, + sizeof(capture_payload_size)); + } + offset += sizeof(capture_payload_size); + + if (capture_payload_size > 0) { + const struct lttng_payload_view current_view = + lttng_payload_view_from_view(view, offset, -1); + + if (current_view.buffer.size < capture_payload_size) { + ret = -1; + goto error; + } + + capture_payload = current_view.buffer.data; + } + + evaluation = lttng_evaluation_event_rule_create(condition, trigger_name, + capture_payload, capture_payload_size, true); if (!evaluation) { ret = -1; goto error; } + offset += capture_payload_size; *_evaluation = evaluation; evaluation = NULL; ret = offset; @@ -845,6 +983,7 @@ static int lttng_evaluation_event_rule_serialize( int ret = 0; struct lttng_evaluation_event_rule *hit; struct lttng_evaluation_event_rule_comm comm; + uint32_t capture_payload_size; hit = container_of( evaluation, struct lttng_evaluation_event_rule, parent); @@ -860,6 +999,68 @@ static int lttng_evaluation_event_rule_serialize( ret = lttng_dynamic_buffer_append( &payload->buffer, hit->name, comm.trigger_name_length); + if (ret) { + goto end; + } + + capture_payload_size = (uint32_t) hit->capture_payload.size; + ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_payload_size, + sizeof(capture_payload_size)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, hit->capture_payload.data, + hit->capture_payload.size); + if (ret) { + goto end; + } + +end: + return ret; +} + +static +bool msgpack_str_is_equal(const struct msgpack_object *obj, const char *str) +{ + bool is_equal = true; + + assert(obj->type == MSGPACK_OBJECT_STR); + + if (obj->via.str.size != strlen(str)) { + is_equal = false; + goto end; + } + + if (strncmp(obj->via.str.ptr, str, obj->via.str.size) != 0) { + is_equal = false; + goto end; + } + +end: + return is_equal; +} + +static +const msgpack_object *get_msgpack_map_obj(const struct msgpack_object *map_obj, + const char *name) +{ + const msgpack_object *ret = NULL; + size_t i; + + assert(map_obj->type == MSGPACK_OBJECT_MAP); + + for (i = 0; i < map_obj->via.map.size; i++) { + const struct msgpack_object_kv *kv = &map_obj->via.map.ptr[i]; + + assert(kv->key.type == MSGPACK_OBJECT_STR); + + if (msgpack_str_is_equal(&kv->key, name)) { + ret = &kv->val; + goto end; + } + } + end: return ret; } @@ -872,34 +1073,342 @@ static void lttng_evaluation_event_rule_destroy( hit = container_of( evaluation, struct lttng_evaluation_event_rule, parent); free(hit->name); + lttng_dynamic_buffer_reset(&hit->capture_payload); + lttng_event_field_value_destroy(hit->captured_values); free(hit); } +static +int event_field_value_from_obj(const msgpack_object *obj, + struct lttng_event_field_value **field_val) +{ + int ret = 0; + + assert(obj); + assert(field_val); + + switch (obj->type) { + case MSGPACK_OBJECT_NIL: + /* Unavailable. */ + *field_val = NULL; + goto end; + case MSGPACK_OBJECT_POSITIVE_INTEGER: + *field_val = lttng_event_field_value_uint_create( + obj->via.u64); + break; + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + *field_val = lttng_event_field_value_int_create( + obj->via.i64); + break; + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: + *field_val = lttng_event_field_value_real_create( + obj->via.f64); + break; + case MSGPACK_OBJECT_STR: + *field_val = lttng_event_field_value_string_create_with_size( + obj->via.str.ptr, obj->via.str.size); + break; + case MSGPACK_OBJECT_ARRAY: + { + size_t i; + + *field_val = lttng_event_field_value_array_create(); + if (!*field_val) { + goto error; + } + + for (i = 0; i < obj->via.array.size; i++) { + const msgpack_object *elem_obj = &obj->via.array.ptr[i]; + struct lttng_event_field_value *elem_field_val; + + ret = event_field_value_from_obj(elem_obj, + &elem_field_val); + if (ret) { + goto error; + } + + if (elem_field_val) { + ret = lttng_event_field_value_array_append( + *field_val, elem_field_val); + } else { + ret = lttng_event_field_value_array_append_unavailable( + *field_val); + } + + if (ret) { + lttng_event_field_value_destroy(elem_field_val); + goto error; + } + } + + break; + } + case MSGPACK_OBJECT_MAP: + { + /* + * As of this version, the only valid map object is + * for an enumeration value, for example: + * + * type: enum + * value: 177 + * labels: + * - Labatt 50 + * - Molson Dry + * - Carling Black Label + */ + const msgpack_object *inner_obj; + size_t label_i; + + inner_obj = get_msgpack_map_obj(obj, "type"); + if (!inner_obj) { + ERR("Missing `type` entry in map object."); + goto error; + } + + if (inner_obj->type != MSGPACK_OBJECT_STR) { + ERR("Map object's `type` entry is not a string (it's a %d).", + inner_obj->type); + goto error; + } + + if (!msgpack_str_is_equal(inner_obj, "enum")) { + ERR("Map object's `type` entry: expecting `enum`."); + goto error; + } + + inner_obj = get_msgpack_map_obj(obj, "value"); + if (!inner_obj) { + ERR("Missing `value` entry in map object."); + goto error; + } + + if (inner_obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + *field_val = lttng_event_field_value_enum_uint_create( + inner_obj->via.u64); + } else if (inner_obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) { + *field_val = lttng_event_field_value_enum_int_create( + inner_obj->via.i64); + } else { + ERR("Map object's `value` entry is not an integer (it's a %d).", + inner_obj->type); + goto error; + } + + if (!*field_val) { + goto error; + } + + inner_obj = get_msgpack_map_obj(obj, "labels"); + if (!inner_obj) { + /* No labels */ + goto end; + } + + if (inner_obj->type != MSGPACK_OBJECT_ARRAY) { + ERR("Map object's `labels` entry is not an array (it's a %d).", + inner_obj->type); + goto error; + } + + for (label_i = 0; label_i < inner_obj->via.array.size; + label_i++) { + int iret; + const msgpack_object *elem_obj = + &inner_obj->via.array.ptr[label_i]; + + if (elem_obj->type != MSGPACK_OBJECT_STR) { + ERR("Map object's `labels` entry's type is not a string (it's a %d).", + elem_obj->type); + goto error; + } + + iret = lttng_event_field_value_enum_append_label_with_size( + *field_val, elem_obj->via.str.ptr, + elem_obj->via.str.size); + if (iret) { + goto error; + } + } + + break; + } + default: + ERR("Unexpected object type %d.", obj->type); + goto error; + } + + if (!*field_val) { + goto error; + } + + goto end; + +error: + lttng_event_field_value_destroy(*field_val); + *field_val = NULL; + ret = -1; + +end: + return ret; +} + +static +struct lttng_event_field_value *event_field_value_from_capture_payload( + const struct lttng_condition_on_event *condition, + const char *capture_payload, size_t capture_payload_size) +{ + struct lttng_event_field_value *ret = NULL; + msgpack_unpacked unpacked; + msgpack_unpack_return unpack_return; + const msgpack_object *root_obj; + const msgpack_object_array *root_array_obj; + size_t i; + size_t count; + + assert(condition); + assert(capture_payload); + + /* Initialize value. */ + msgpack_unpacked_init(&unpacked); + + /* Decode. */ + unpack_return = msgpack_unpack_next(&unpacked, capture_payload, + capture_payload_size, NULL); + if (unpack_return != MSGPACK_UNPACK_SUCCESS) { + ERR("msgpack_unpack_next() failed to decode the " + "MessagePack-encoded capture payload " + "(size %zu); returned %d.", + capture_payload_size, unpack_return); + goto error; + } + + /* Get root array. */ + root_obj = &unpacked.data; + + if (root_obj->type != MSGPACK_OBJECT_ARRAY) { + ERR("Expecting an array as the root object; got type %d.", + root_obj->type); + goto error; + } + + root_array_obj = &root_obj->via.array; + + /* Create an empty root array event field value. */ + ret = lttng_event_field_value_array_create(); + if (!ret) { + goto error; + } + + /* + * For each capture descriptor in the condition object: + * + * 1. Get its corresponding captured field value MessagePack + * object. + * + * 2. Create a corresponding event field value. + * + * 3. Append it to `ret` (the root array event field value). + */ + count = lttng_dynamic_pointer_array_get_count( + &condition->capture_descriptors); + assert(count > 0); + + for (i = 0; i < count; i++) { + const struct lttng_capture_descriptor *capture_descriptor = + lttng_condition_on_event_get_internal_capture_descriptor_at_index( + &condition->parent, i); + const msgpack_object *elem_obj; + struct lttng_event_field_value *elem_field_val; + int iret; + + assert(capture_descriptor); + + elem_obj = &root_array_obj->ptr[i]; + iret = event_field_value_from_obj(elem_obj, + &elem_field_val); + if (iret) { + goto error; + } + + if (elem_field_val) { + iret = lttng_event_field_value_array_append(ret, + elem_field_val); + } else { + iret = lttng_event_field_value_array_append_unavailable( + ret); + } + + if (iret) { + lttng_event_field_value_destroy(elem_field_val); + goto error; + } + } + + goto end; + +error: + lttng_event_field_value_destroy(ret); + ret = NULL; + +end: + msgpack_unpacked_destroy(&unpacked); + return ret; +} + LTTNG_HIDDEN struct lttng_evaluation *lttng_evaluation_event_rule_create( - const char *trigger_name) + const struct lttng_condition_on_event *condition, + const char *trigger_name, + const char *capture_payload, size_t capture_payload_size, + bool decode_capture_payload) { struct lttng_evaluation_event_rule *hit; struct lttng_evaluation *evaluation = NULL; hit = zmalloc(sizeof(struct lttng_evaluation_event_rule)); if (!hit) { - goto end; + goto error; } hit->name = strdup(trigger_name); if (!hit->name) { - goto end; + goto error; } - hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; + lttng_dynamic_buffer_init(&hit->capture_payload); + + if (capture_payload) { + const int ret = lttng_dynamic_buffer_append( + &hit->capture_payload, capture_payload, + capture_payload_size); + if (ret) { + ERR("Failed to initialize capture payload of event rule evaluation"); + goto error; + } + + if (decode_capture_payload) { + hit->captured_values = + event_field_value_from_capture_payload( + condition, + capture_payload, + capture_payload_size); + if (!hit->captured_values) { + ERR("Failed to decode the capture payload: size = %zu", + capture_payload_size); + goto error; + } + } + } + + hit->parent.type = LTTNG_CONDITION_TYPE_ON_EVENT; hit->parent.serialize = lttng_evaluation_event_rule_serialize; hit->parent.destroy = lttng_evaluation_event_rule_destroy; evaluation = &hit->parent; hit = NULL; -end: +error: if (hit) { lttng_evaluation_event_rule_destroy(&hit->parent); } @@ -907,6 +1416,36 @@ end: return evaluation; } +enum lttng_evaluation_status lttng_evaluation_get_captured_values( + const struct lttng_evaluation *evaluation, + const struct lttng_event_field_value **field_val) +{ + struct lttng_evaluation_event_rule *hit; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + /* + * Event rule is currently the only type that can provide captured + * values. + */ + if (!evaluation || !is_event_rule_evaluation(evaluation) || + !field_val) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + hit = container_of(evaluation, struct lttng_evaluation_event_rule, + parent); + if (!hit->captured_values) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + *field_val = hit->captured_values; + +end: + return status; +} + enum lttng_evaluation_status lttng_evaluation_event_rule_get_trigger_name( const struct lttng_evaluation *evaluation, const char **name) { @@ -927,7 +1466,7 @@ end: LTTNG_HIDDEN enum lttng_error_code -lttng_condition_event_rule_generate_capture_descriptor_bytecode( +lttng_condition_on_event_generate_capture_descriptor_bytecode( struct lttng_condition *condition) { enum lttng_error_code ret; @@ -939,7 +1478,7 @@ lttng_condition_event_rule_generate_capture_descriptor_bytecode( goto end; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition, &capture_count); if (status != LTTNG_CONDITION_STATUS_OK) { ret = LTTNG_ERR_FATAL; @@ -948,7 +1487,7 @@ lttng_condition_event_rule_generate_capture_descriptor_bytecode( for (i = 0; i < capture_count; i++) { struct lttng_capture_descriptor *local_capture_desc = - lttng_condition_event_rule_get_internal_capture_descriptor_at_index( + lttng_condition_on_event_get_internal_capture_descriptor_at_index( condition, i); if (local_capture_desc == NULL) { @@ -975,12 +1514,12 @@ end: LTTNG_HIDDEN const struct lttng_bytecode * -lttng_condition_event_rule_get_capture_bytecode_at_index( +lttng_condition_on_event_get_capture_bytecode_at_index( const struct lttng_condition *condition, unsigned int index) { - const struct lttng_condition_event_rule *event_rule_cond = + const struct lttng_condition_on_event *on_event_cond = container_of(condition, - const struct lttng_condition_event_rule, + const struct lttng_condition_on_event, parent); struct lttng_capture_descriptor *desc = NULL; struct lttng_bytecode *bytecode = NULL; @@ -991,7 +1530,7 @@ lttng_condition_event_rule_get_capture_bytecode_at_index( goto end; } - status = lttng_condition_event_rule_get_capture_descriptor_count( + status = lttng_condition_on_event_get_capture_descriptor_count( condition, &count); if (status != LTTNG_CONDITION_STATUS_OK) { goto end; @@ -1002,7 +1541,7 @@ lttng_condition_event_rule_get_capture_bytecode_at_index( } desc = lttng_dynamic_pointer_array_get_pointer( - &event_rule_cond->capture_descriptors, index); + &on_event_cond->capture_descriptors, index); if (desc == NULL) { goto end; } diff --git a/src/common/config/config-session-abi.h b/src/common/config/config-session-abi.h index 75ff303d4..85ff4ed14 100644 --- a/src/common/config/config-session-abi.h +++ b/src/common/config/config-session-abi.h @@ -11,6 +11,8 @@ extern const char * const config_element_all; extern const char * const config_element_channel; extern const char * const config_element_channels; +extern const char * const config_element_map; +extern const char * const config_element_maps; extern const char * const config_element_domain; extern const char * const config_element_domains; extern const char * const config_element_event; @@ -99,6 +101,15 @@ extern const char * const config_element_rotation_timer_interval; extern const char * const config_element_rotation_size; extern const char * const config_element_rotation_schedule; +extern const char * const config_element_bitness; +extern const char * const config_element_boundary_policy; +extern const char * const config_element_coalesce_hits; +extern const char * const config_element_dimensions; +extern const char * const config_element_dimension; +extern const char * const config_element_dimension_size; + +extern const char * const config_boundary_policy_overflow; + extern const char * const config_domain_type_kernel; extern const char * const config_domain_type_ust; extern const char * const config_domain_type_jul; diff --git a/src/common/config/session-config.c b/src/common/config/session-config.c index 49c06a353..70668470c 100644 --- a/src/common/config/session-config.c +++ b/src/common/config/session-config.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,8 @@ const char * const config_xml_false = "false"; const char * const config_element_channel = "channel"; const char * const config_element_channels = "channels"; +const char * const config_element_map = "map"; +const char * const config_element_maps = "maps"; const char * const config_element_domain = "domain"; const char * const config_element_domains = "domains"; const char * const config_element_event = "event"; @@ -183,6 +186,15 @@ const char * const config_overwrite_mode_overwrite = "OVERWRITE"; const char * const config_output_type_splice = "SPLICE"; const char * const config_output_type_mmap = "MMAP"; +LTTNG_HIDDEN const char * const config_element_bitness = "bitness"; +LTTNG_HIDDEN const char * const config_element_boundary_policy = "boundary_policy"; +LTTNG_HIDDEN const char * const config_element_coalesce_hits = "coalesce_hits"; +LTTNG_HIDDEN const char * const config_element_dimensions = "dimensions"; +LTTNG_HIDDEN const char * const config_element_dimension = "dimension"; +LTTNG_HIDDEN const char * const config_element_dimension_size = "size"; + +LTTNG_HIDDEN const char * const config_boundary_policy_overflow = "OVERFLOW"; + const char * const config_loglevel_type_all = "ALL"; const char * const config_loglevel_type_range = "RANGE"; const char * const config_loglevel_type_single = "SINGLE"; @@ -3054,14 +3066,299 @@ end: return ret; } +static +int process_channel_node(xmlNodePtr channel_node, struct lttng_handle *handle, + struct lttng_domain *domain, + enum lttng_domain_type original_domain) +{ + int ret; + struct lttng_channel *channel = NULL; + xmlNodePtr contexts_node = NULL; + xmlNodePtr events_node = NULL; + xmlNodePtr channel_attr_node; + + /* + * Channels of the "agent" types cannot be created directly. + * They are meant to be created implicitly through the + * activation of events in their domain. However, a user + * can override the default channel configuration attributes + * by creating the underlying UST channel _before_ enabling + * an agent domain event. + * + * Hence, the channel's type is substituted before the creation + * and restored by the time the events are created. + */ + switch (domain->type) { + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + domain->type = LTTNG_DOMAIN_UST; + default: + break; + } + + channel = lttng_channel_create(domain); + if (!channel) { + ret = -1; + goto end; + } + + for (channel_attr_node = xmlFirstElementChild(channel_node); + channel_attr_node; channel_attr_node = + xmlNextElementSibling(channel_attr_node)) { + ret = process_channel_attr_node(channel_attr_node, + channel, &contexts_node, &events_node); + if (ret) { + goto end; + } + } + + ret = lttng_enable_channel(handle, channel); + if (ret < 0) { + goto end; + } + + /* Restore the original channel domain-> */ + domain->type = original_domain; + + ret = process_events_node(events_node, handle, channel->name); + if (ret) { + goto end; + } + + ret = process_contexts_node(contexts_node, handle, + channel->name); + if (ret) { + goto end; + } + +end: + lttng_channel_destroy(channel); + return ret; +} + +static int process_dimension_node(xmlNodePtr dimension_node, + struct lttng_dynamic_array *dimension_sizes) +{ + int ret; + xmlNodePtr node; + xmlChar *size_str = NULL; + uint64_t size; + + assert(strcmp((const char *) dimension_node->name, + config_element_dimension) == 0); + assert(dimension_sizes->element_size == sizeof(uint64_t)); + + for (node = xmlFirstElementChild(dimension_node); node; + node = xmlNextElementSibling(node)) { + if (strcmp((const char *) node->name, + config_element_dimension_size) == 0) { + assert(!size_str); + size_str = xmlNodeGetContent(node); + if (!size_str) { + ERR("Failed to get dimension size node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(size_str, &size); + assert(!ret); + + ret = lttng_dynamic_array_add_element(dimension_sizes, &size); + if (ret) { + goto end; + } + } else { + assert(false); + } + } + + ret = 0; + +end: + xmlFree(size_str); + return ret; +} + +/* `dimensions_sizes` must be initialized to hold uint64_t elements. */ + +static int process_dimensions_node(xmlNodePtr dimensions_node, + struct lttng_dynamic_array *dimension_sizes) +{ + xmlNodePtr dimension_node; + int ret = 0; + + assert(strcmp((const char *) dimensions_node->name, + config_element_dimensions) == 0); + assert(dimension_sizes->element_size == sizeof(uint64_t)); + assert(lttng_dynamic_array_get_count(dimension_sizes) == 0); + + for (dimension_node = xmlFirstElementChild(dimensions_node); + dimension_node; dimension_node = xmlNextElementSibling( + dimension_node)) { + ret = process_dimension_node(dimension_node, dimension_sizes); + if (ret) { + goto end; + } + } + + assert(lttng_dynamic_array_get_count(dimension_sizes) > 0); + +end: + return ret; +} + +static int process_map_node(xmlNodePtr map_node, struct lttng_handle *handle) +{ + int ret; + xmlNodePtr node; + xmlChar *name = NULL; + xmlChar *enabled_str = NULL; + int enabled; + xmlChar *bitness_str = NULL; + enum lttng_map_bitness bitness = LTTNG_MAP_BITNESS_32BITS; + xmlChar *boundary_policy_str = NULL; + enum lttng_map_boundary_policy boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + xmlChar *coalesce_hits_str = NULL; + int coalesce_hits; + enum lttng_map_status map_status; + struct lttng_map *map = NULL; + struct lttng_dynamic_array dimension_sizes; + enum lttng_error_code error_code; + + assert(strcmp((const char *) map_node->name, config_element_map) == 0); + + lttng_dynamic_array_init(&dimension_sizes, sizeof(uint64_t), NULL); + + for (node = xmlFirstElementChild(map_node); node; + node = xmlNextElementSibling(node)) { + if (strcmp((const char *) node->name, config_element_name) == + 0) { + assert(!name); + name = xmlNodeGetContent(node); + if (!name) { + ERR("Failed to get map name node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + } else if (strcmp((const char *) node->name, + config_element_enabled) == 0) { + assert(!enabled_str); + enabled_str = xmlNodeGetContent(node); + if (!enabled_str) { + ERR("Failed to get map enabled node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_bool(enabled_str, &enabled); + assert(!ret); + } else if (strcmp((const char *) node->name, + config_element_bitness) == 0) { + assert(!bitness_str); + bitness_str = xmlNodeGetContent(node); + if (!bitness_str) { + ERR("Failed to get map bitness node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + if (strcmp((const char *) bitness_str, "32") == 0) { + bitness = LTTNG_MAP_BITNESS_32BITS; + } else { + assert(strcmp((const char *) bitness_str, + "64") == 0); + bitness = LTTNG_MAP_BITNESS_64BITS; + } + } else if (strcmp((const char *) node->name, + config_element_boundary_policy) == + 0) { + assert(!boundary_policy_str); + boundary_policy_str = xmlNodeGetContent(node); + if (!boundary_policy_str) { + ERR("Failed to get map boundary policy node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + assert(strcmp((const char *) boundary_policy_str, + config_boundary_policy_overflow) == + 0); + boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + } else if (strcmp((const char *) node->name, + config_element_coalesce_hits) == 0) { + assert(!coalesce_hits_str); + coalesce_hits_str = xmlNodeGetContent(node); + if (!coalesce_hits_str) { + ERR("Failed to get map coalesce hits node content."); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_bool(coalesce_hits_str, &coalesce_hits); + assert(!ret); + } else if (strcmp((const char *) node->name, + config_element_dimensions) == 0) { + ret = process_dimensions_node(node, &dimension_sizes); + if (ret) { + goto end; + } + } else { + assert(false); + } + } + + assert(name); + map_status = lttng_map_create((const char *) name, + lttng_dynamic_array_get_count(&dimension_sizes), + (uint64_t *) dimension_sizes.buffer.data, + handle->domain.type, handle->domain.buf_type, bitness, + boundary_policy, coalesce_hits, &map); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Failed to create map."); + ret = -LTTNG_ERR_UNK; + goto end; + } + + error_code = lttng_add_map(handle, map); + if (error_code != LTTNG_OK) { + ERR("Adding map \"%s\": %s", (const char *) name, + lttng_strerror(error_code)); + ret = error_code; + goto end; + } + + // FIXME: disabling the map after creating leaves a window of time + // where it is enabled, does it matter? + if (!enabled) { + ret = lttng_disable_map(handle, (const char *) name); + if (ret) { + goto end; + } + } + + ret = 0; + +end: + xmlFree(name); + xmlFree(enabled_str); + xmlFree(bitness_str); + xmlFree(boundary_policy_str); + xmlFree(coalesce_hits_str); + lttng_dynamic_array_reset(&dimension_sizes); + lttng_map_destroy(map); + + return ret; +} + static int process_domain_node(xmlNodePtr domain_node, const char *session_name) { int ret; struct lttng_domain domain = { 0 }; struct lttng_handle *handle = NULL; - struct lttng_channel *channel = NULL; xmlNodePtr channels_node = NULL; + xmlNodePtr maps_node = NULL; xmlNodePtr trackers_node = NULL; xmlNodePtr pid_tracker_node = NULL; xmlNodePtr vpid_tracker_node = NULL; @@ -3094,76 +3391,39 @@ int process_domain_node(xmlNodePtr domain_node, const char *session_name) } } - if (!channels_node) { - goto end; - } - - /* create all channels */ - for (node = xmlFirstElementChild(channels_node); node; - node = xmlNextElementSibling(node)) { - const enum lttng_domain_type original_domain = domain.type; - xmlNodePtr contexts_node = NULL; - xmlNodePtr events_node = NULL; - xmlNodePtr channel_attr_node; - - /* - * Channels of the "agent" types cannot be created directly. - * They are meant to be created implicitly through the - * activation of events in their domain. However, a user - * can override the default channel configuration attributes - * by creating the underlying UST channel _before_ enabling - * an agent domain event. - * - * Hence, the channel's type is substituted before the creation - * and restored by the time the events are created. - */ - switch (domain.type) { - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_PYTHON: - domain.type = LTTNG_DOMAIN_UST; - default: - break; - } - - channel = lttng_channel_create(&domain); - if (!channel) { - ret = -1; - goto end; - } + if (channels_node) { + /* create all channels */ + for (node = xmlFirstElementChild(channels_node); node; + node = xmlNextElementSibling(node)) { + const enum lttng_domain_type original_domain = domain.type; - for (channel_attr_node = xmlFirstElementChild(node); - channel_attr_node; channel_attr_node = - xmlNextElementSibling(channel_attr_node)) { - ret = process_channel_attr_node(channel_attr_node, - channel, &contexts_node, &events_node); + ret = process_channel_node(node, handle, &domain, + original_domain); if (ret) { goto end; } } + } - ret = lttng_enable_channel(handle, channel); - if (ret < 0) { - goto end; - } - - /* Restore the original channel domain. */ - domain.type = original_domain; - - ret = process_events_node(events_node, handle, channel->name); - if (ret) { - goto end; + /* get the maps node */ + for (node = xmlFirstElementChild(domain_node); node; + node = xmlNextElementSibling(node)) { + if (!strcmp((const char *) node->name, config_element_maps)) { + maps_node = node; + break; } + } - ret = process_contexts_node(contexts_node, handle, - channel->name); - if (ret) { - goto end; + if (maps_node) { + /* create all maps */ + for (node = xmlFirstElementChild(maps_node); node; + node = xmlNextElementSibling(node)) { + ret = process_map_node(node, handle); + if (ret) { + goto end; + } } - - lttng_channel_destroy(channel); } - channel = NULL; /* get the trackers node */ for (node = xmlFirstElementChild(domain_node); node; @@ -3254,7 +3514,6 @@ int process_domain_node(xmlNodePtr domain_node, const char *session_name) } end: - lttng_channel_destroy(channel); lttng_destroy_handle(handle); return ret; } diff --git a/src/common/config/session.xsd b/src/common/config/session.xsd index 986fb2dda..c8f8ea0fb 100644 --- a/src/common/config/session.xsd +++ b/src/common/config/session.xsd @@ -254,6 +254,48 @@ by its signed 32-bit representation when converted to msec. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -429,6 +471,7 @@ by its signed 32-bit representation when converted to msec. + diff --git a/src/common/error.c b/src/common/error.c index e03bee350..66859670a 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -241,6 +241,20 @@ static const char *error_string_array[] = { [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) ] = "Operation does not apply to the process attribute tracker's tracking policy", [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD) ] = "Failed to create an event notifier group notification file descriptor", [ ERROR_INDEX(LTTNG_ERR_INVALID_CAPTURE_EXPRESSION) ] = "Invalid capture expression", + [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION) ] = "Failed to create event notifier", + [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING) ] = "Failed to initialize event notifier error accounting", + [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL) ] = "No index available in event notifier error accounting", + [ ERROR_INDEX(LTTNG_ERR_INVALID_MAP) ] = "Invalid map", + [ ERROR_INDEX(LTTNG_ERR_MAP_NOT_FOUND) ] = "Map name not found", + [ ERROR_INDEX(LTTNG_ERR_UST_MAP_ENABLE_FAIL) ] = "Enable UST map failed", + [ ERROR_INDEX(LTTNG_ERR_UST_MAP_DISABLE_FAIL) ] = "Disable UST map failed", + [ ERROR_INDEX(LTTNG_ERR_UST_MAP_NOT_FOUND) ] = "UST map not found", + [ ERROR_INDEX(LTTNG_ERR_UST_MAP_EXIST) ] = "UST map already exists", + [ ERROR_INDEX(LTTNG_ERR_KERNEL_MAP_ENABLE_FAIL) ] = "Enable Kernel map failed", + [ ERROR_INDEX(LTTNG_ERR_KERNEL_MAP_DISABLE_FAIL) ] = "Disable Kernel map failed", + [ ERROR_INDEX(LTTNG_ERR_KERNEL_MAP_NOT_FOUND) ] = "Kernel map not found", + [ ERROR_INDEX(LTTNG_ERR_KERNEL_MAP_EXIST) ] = "Kernel map already exists", + [ ERROR_INDEX(LTTNG_ERR_MAP_VALUES_LIST_FAIL) ] = "Listing map values failed", /* Last element */ [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code" diff --git a/src/common/evaluation.c b/src/common/evaluation.c index d8a68a784..c51ec2bd9 100644 --- a/src/common/evaluation.c +++ b/src/common/evaluation.c @@ -5,11 +5,12 @@ * */ +#include #include #include #include #include -#include +#include #include #include #include @@ -49,6 +50,7 @@ end: LTTNG_HIDDEN ssize_t lttng_evaluation_create_from_payload( + const struct lttng_condition *condition, struct lttng_payload_view *src_view, struct lttng_evaluation **evaluation) { @@ -115,8 +117,14 @@ ssize_t lttng_evaluation_create_from_payload( } evaluation_size += ret; break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: - ret = lttng_evaluation_event_rule_create_from_payload(&evaluation_view, evaluation); + case LTTNG_CONDITION_TYPE_ON_EVENT: + assert(condition); + assert(condition->type == LTTNG_CONDITION_TYPE_ON_EVENT); + ret = lttng_evaluation_event_rule_create_from_payload( + container_of(condition, + const struct lttng_condition_on_event, + parent), + &evaluation_view, evaluation); if (ret < 0) { goto end; } diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c index 3a5be731b..6071bb8dc 100644 --- a/src/common/event-rule/event-rule.c +++ b/src/common/event-rule/event-rule.c @@ -14,10 +14,11 @@ #include #include #include -#include +#include +#include #include #include -#include +#include #include enum lttng_event_rule_type lttng_event_rule_get_type( @@ -41,9 +42,9 @@ enum lttng_domain_type lttng_event_rule_get_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: + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: domain_type = LTTNG_DOMAIN_KERNEL; break; case LTTNG_EVENT_RULE_TYPE_UNKNOWN: @@ -172,14 +173,14 @@ ssize_t lttng_event_rule_create_from_payload( create_from_payload = lttng_event_rule_tracepoint_create_from_payload; break; - case LTTNG_EVENT_RULE_TYPE_KPROBE: - create_from_payload = lttng_event_rule_kprobe_create_from_payload; + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: + create_from_payload = lttng_event_rule_kernel_probe_create_from_payload; break; - case LTTNG_EVENT_RULE_TYPE_KRETPROBE: - /* TODO */ + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: + create_from_payload = lttng_event_rule_kernel_function_create_from_payload; break; - case LTTNG_EVENT_RULE_TYPE_UPROBE: - create_from_payload = lttng_event_rule_uprobe_create_from_payload; + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: + create_from_payload = lttng_event_rule_userspace_probe_create_from_payload; break; case LTTNG_EVENT_RULE_TYPE_SYSCALL: create_from_payload = @@ -315,11 +316,11 @@ const char *lttng_event_rule_type_str(enum lttng_event_rule_type type) return "tracepoint"; case LTTNG_EVENT_RULE_TYPE_SYSCALL: return "syscall"; - case LTTNG_EVENT_RULE_TYPE_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE: return "probe"; - case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION: return "function"; - case LTTNG_EVENT_RULE_TYPE_UPROBE: + case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE: return "userspace-probe"; default: abort(); diff --git a/src/common/event-rule/kernel-function.c b/src/common/event-rule/kernel-function.c new file mode 100644 index 000000000..c5dd61938 --- /dev/null +++ b/src/common/event-rule/kernel-function.c @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_KERNEL_FUNCTION_EVENT_RULE(rule) \ + (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION) + +#if (LTTNG_SYMBOL_NAME_LEN == 256) +#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" +#endif + +static void lttng_event_rule_kernel_function_destroy(struct lttng_event_rule *rule) +{ + struct lttng_event_rule_kernel_function *kfunction; + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + + lttng_kernel_function_location_destroy(kfunction->location); + free(kfunction->name); + free(kfunction); +} + +static bool lttng_event_rule_kernel_function_validate( + const struct lttng_event_rule *rule) +{ + bool valid = false; + struct lttng_event_rule_kernel_function *kfunction; + + if (!rule) { + goto end; + } + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + + /* Required field. */ + if (!kfunction->name) { + ERR("Invalid name event rule: a name must be set."); + goto end; + } + + /* Required field. */ + if(!kfunction->location) { + ERR("Invalid name event rule: a location must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static int lttng_event_rule_kernel_function_serialize( + const struct lttng_event_rule *rule, + struct lttng_payload *payload) +{ + int ret; + size_t name_len, header_offset, size_before_location; + struct lttng_event_rule_kernel_function *kfunction; + struct lttng_event_rule_kernel_function_comm kfunction_comm; + struct lttng_event_rule_kernel_function_comm *header; + + if (!rule || !IS_KERNEL_FUNCTION_EVENT_RULE(rule)) { + ret = -1; + goto end; + } + + header_offset = payload->buffer.size; + + DBG("Serializing kfunction event rule."); + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + + name_len = strlen(kfunction->name) + 1; + kfunction_comm.name_len = name_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &kfunction_comm, sizeof(kfunction_comm)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, kfunction->name, name_len); + if (ret) { + goto end; + } + + size_before_location = payload->buffer.size; + + ret = lttng_kernel_function_location_serialize(kfunction->location, payload); + if (ret < 0) { + goto end; + } + + /* Update the header regarding the function size. */ + header = (struct lttng_event_rule_kernel_function_comm*) ( + (char *) payload->buffer.data + header_offset); + header->location_len = payload->buffer.size - size_before_location; + + ret = 0; + +end: + return ret; +} + +static bool lttng_event_rule_kernel_function_is_equal(const struct lttng_event_rule *_a, + const struct lttng_event_rule *_b) +{ + bool is_equal = false; + struct lttng_event_rule_kernel_function *a, *b; + + a = container_of(_a, struct lttng_event_rule_kernel_function, parent); + b = container_of(_b, struct lttng_event_rule_kernel_function, parent); + + /* Quick checks */ + if (!!a->name != !!b->name) { + goto end; + } + + /* Long check */ + assert(a->name); + assert(b->name); + if (strcmp(a->name, b->name)) { + goto end; + } + + is_equal = lttng_kernel_function_location_is_equal( + a->location, b->location); +end: + return is_equal; +} + +static enum lttng_error_code lttng_event_rule_kernel_function_generate_filter_bytecode( + struct lttng_event_rule *rule, + const struct lttng_credentials *creds) +{ + /* Nothing to do. */ + return LTTNG_OK; +} + +static const char *lttng_event_rule_kernel_function_get_filter( + const struct lttng_event_rule *rule) +{ + /* Not supported. */ + return NULL; +} + +static const struct lttng_bytecode * +lttng_event_rule_kernel_function_get_filter_bytecode(const struct lttng_event_rule *rule) +{ + /* Not supported. */ + return NULL; +} + +static enum lttng_event_rule_generate_exclusions_status +lttng_event_rule_kernel_function_generate_exclusions(const struct lttng_event_rule *rule, + struct lttng_event_exclusion **exclusions) +{ + /* Not supported. */ + *exclusions = NULL; + return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE; +} + +static unsigned long +lttng_event_rule_kernel_function_hash( + const struct lttng_event_rule *rule) +{ + unsigned long hash; + struct lttng_event_rule_kernel_function *krule = + container_of(rule, typeof(*krule), parent); + + hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION, + lttng_ht_seed); + hash ^= hash_key_str(krule->name, lttng_ht_seed); + hash ^= lttng_kernel_function_location_hash(krule->location); + + return hash; +} + +static +int kernel_function_set_location( + struct lttng_event_rule_kernel_function *kfunction, + const struct lttng_kernel_function_location *location) +{ + int ret; + struct lttng_kernel_function_location *location_copy = NULL; + + if (!kfunction || !location || kfunction->location) { + ret = -1; + goto end; + } + + location_copy = lttng_kernel_function_location_copy(location); + if (!location_copy) { + ret = -1; + goto end; + } + + kfunction->location = location_copy; + location_copy = NULL; + ret = 0; +end: + lttng_kernel_function_location_destroy(location_copy); + return ret; +} + +struct lttng_event_rule *lttng_event_rule_kernel_function_create( + const struct lttng_kernel_function_location *location) +{ + struct lttng_event_rule *rule = NULL; + struct lttng_event_rule_kernel_function *krule; + + krule = zmalloc(sizeof(struct lttng_event_rule_kernel_function)); + if (!krule) { + goto end; + } + + rule = &krule->parent; + lttng_event_rule_init(&krule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION); + krule->parent.validate = lttng_event_rule_kernel_function_validate; + krule->parent.serialize = lttng_event_rule_kernel_function_serialize; + krule->parent.equal = lttng_event_rule_kernel_function_is_equal; + krule->parent.destroy = lttng_event_rule_kernel_function_destroy; + krule->parent.generate_filter_bytecode = + lttng_event_rule_kernel_function_generate_filter_bytecode; + krule->parent.get_filter = lttng_event_rule_kernel_function_get_filter; + krule->parent.get_filter_bytecode = + lttng_event_rule_kernel_function_get_filter_bytecode; + krule->parent.generate_exclusions = + lttng_event_rule_kernel_function_generate_exclusions; + krule->parent.hash = lttng_event_rule_kernel_function_hash; + + if (kernel_function_set_location(krule, location)) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + +end: + return rule; +} + +LTTNG_HIDDEN +ssize_t lttng_event_rule_kernel_function_create_from_payload( + struct lttng_payload_view *view, + struct lttng_event_rule **_event_rule) +{ + ssize_t ret, offset = 0; + enum lttng_event_rule_status status; + const struct lttng_event_rule_kernel_function_comm *kfunction_comm; + const char *name; + struct lttng_buffer_view current_buffer_view; + struct lttng_event_rule *rule = NULL; + struct lttng_kernel_function_location *location = NULL; + + if (!_event_rule) { + ret = -1; + goto end; + } + + current_buffer_view = lttng_buffer_view_from_view( + &view->buffer, offset, sizeof(*kfunction_comm)); + if (!lttng_buffer_view_is_valid(¤t_buffer_view)) { + ERR("Failed to initialize from malformed event rule kfunction: buffer too short to contain header."); + ret = -1; + goto end; + } + + kfunction_comm = (typeof(kfunction_comm)) current_buffer_view.data; + + /* Skip to payload */ + offset += current_buffer_view.size; + + { + /* Map the name. */ + struct lttng_payload_view current_payload_view = + lttng_payload_view_from_view(view, offset, + kfunction_comm->name_len); + + if (!lttng_payload_view_is_valid(¤t_payload_view)) { + ret = -1; + goto end; + } + + name = current_payload_view.buffer.data; + if (!lttng_buffer_view_contains_string( + ¤t_payload_view.buffer, name, + kfunction_comm->name_len)) { + ret = -1; + goto end; + } + } + + /* Skip after the name. */ + offset += kfunction_comm->name_len; + + /* Map the kernel function location. */ + { + struct lttng_payload_view current_payload_view = + lttng_payload_view_from_view(view, offset, + kfunction_comm->location_len); + + if (!lttng_payload_view_is_valid(¤t_payload_view)) { + ret = -1; + goto end; + } + + ret = lttng_kernel_function_location_create_from_payload( + ¤t_payload_view, &location); + if (ret < 0) { + ret = -1; + goto end; + } + } + + if (ret != kfunction_comm->location_len) { + ret = -1; + goto end; + } + + /* Skip after the location */ + offset += kfunction_comm->location_len; + + rule = lttng_event_rule_kernel_function_create(location); + if (!rule) { + ERR("Failed to create event rule kfunction."); + ret = -1; + goto end; + } + + status = lttng_event_rule_kernel_function_set_event_name(rule, name); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule kfunction name."); + ret = -1; + goto end; + } + + *_event_rule = rule; + rule = NULL; + ret = offset; +end: + lttng_kernel_function_location_destroy(location); + lttng_event_rule_destroy(rule); + return ret; +} + +enum lttng_event_rule_status lttng_event_rule_kernel_function_get_location( + const struct lttng_event_rule *rule, + const struct lttng_kernel_function_location **location) +{ + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + struct lttng_event_rule_kernel_function *kfunction; + + if (!rule || !IS_KERNEL_FUNCTION_EVENT_RULE(rule) || !location) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + *location = kfunction->location; + + if (!*location) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_kernel_function_set_event_name( + struct lttng_event_rule *rule, const char *name) +{ + char *name_copy = NULL; + struct lttng_event_rule_kernel_function *kfunction; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_KERNEL_FUNCTION_EVENT_RULE(rule) || !name || + strlen(name) == 0) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; + goto end; + } + + free(kfunction->name); + + kfunction->name = name_copy; + name_copy = NULL; +end: + return status; +} + +enum lttng_event_rule_status lttng_event_rule_kernel_function_get_event_name( + const struct lttng_event_rule *rule, const char **name) +{ + struct lttng_event_rule_kernel_function *kfunction; + enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + + if (!rule || !IS_KERNEL_FUNCTION_EVENT_RULE(rule) || !name) { + status = LTTNG_EVENT_RULE_STATUS_INVALID; + goto end; + } + + kfunction = container_of(rule, struct lttng_event_rule_kernel_function, parent); + if (!kfunction->name) { + status = LTTNG_EVENT_RULE_STATUS_UNSET; + goto end; + } + + *name = kfunction->name; +end: + return status; +} diff --git a/src/common/event-rule/kprobe.c b/src/common/event-rule/kernel-probe.c similarity index 66% rename from src/common/event-rule/kprobe.c rename to src/common/event-rule/kernel-probe.c index 12e6010ac..f53e9245b 100644 --- a/src/common/event-rule/kprobe.c +++ b/src/common/event-rule/kernel-probe.c @@ -16,41 +16,42 @@ #include #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) + (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE) #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) +static void lttng_event_rule_kernel_probe_destroy(struct lttng_event_rule *rule) { - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); lttng_kernel_probe_location_destroy(kprobe->location); free(kprobe->name); free(kprobe); } -static bool lttng_event_rule_kprobe_validate( +static bool lttng_event_rule_kernel_probe_validate( const struct lttng_event_rule *rule) { bool valid = false; - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; if (!rule) { goto end; } - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); /* Required field. */ if (!kprobe->name) { @@ -69,15 +70,15 @@ end: return valid; } -static int lttng_event_rule_kprobe_serialize( +static int lttng_event_rule_kernel_probe_serialize( const struct lttng_event_rule *rule, struct lttng_payload *payload) { int ret; size_t name_len, header_offset, size_before_location; - struct lttng_event_rule_kprobe *kprobe; - struct lttng_event_rule_kprobe_comm kprobe_comm; - struct lttng_event_rule_kprobe_comm *header; + struct lttng_event_rule_kernel_probe *kprobe; + struct lttng_event_rule_kernel_probe_comm kprobe_comm; + struct lttng_event_rule_kernel_probe_comm *header; if (!rule || !IS_KPROBE_EVENT_RULE(rule)) { ret = -1; @@ -87,7 +88,7 @@ static int lttng_event_rule_kprobe_serialize( header_offset = payload->buffer.size; DBG("Serializing kprobe event rule."); - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); name_len = strlen(kprobe->name) + 1; kprobe_comm.name_len = name_len; @@ -111,7 +112,7 @@ static int lttng_event_rule_kprobe_serialize( } /* Update the header regarding the probe size. */ - header = (struct lttng_event_rule_kprobe_comm*) ( + header = (struct lttng_event_rule_kernel_probe_comm*) ( (char *) payload->buffer.data + header_offset); header->location_len = payload->buffer.size - size_before_location; @@ -121,14 +122,14 @@ end: return ret; } -static bool lttng_event_rule_kprobe_is_equal(const struct lttng_event_rule *_a, +static bool lttng_event_rule_kernel_probe_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; + struct lttng_event_rule_kernel_probe *a, *b; - a = container_of(_a, struct lttng_event_rule_kprobe, parent); - b = container_of(_b, struct lttng_event_rule_kprobe, parent); + a = container_of(_a, struct lttng_event_rule_kernel_probe, parent); + b = container_of(_b, struct lttng_event_rule_kernel_probe, parent); /* Quick checks */ if (!!a->name != !!b->name) { @@ -148,7 +149,7 @@ end: return is_equal; } -static enum lttng_error_code lttng_event_rule_kprobe_generate_filter_bytecode( +static enum lttng_error_code lttng_event_rule_kernel_probe_generate_filter_bytecode( struct lttng_event_rule *rule, const struct lttng_credentials *creds) { @@ -156,7 +157,7 @@ static enum lttng_error_code lttng_event_rule_kprobe_generate_filter_bytecode( return LTTNG_OK; } -static const char *lttng_event_rule_kprobe_get_filter( +static const char *lttng_event_rule_kernel_probe_get_filter( const struct lttng_event_rule *rule) { /* Not supported. */ @@ -164,14 +165,14 @@ static const char *lttng_event_rule_kprobe_get_filter( } static const struct lttng_bytecode * -lttng_event_rule_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule) +lttng_event_rule_kernel_probe_get_filter_bytecode(const struct lttng_event_rule *rule) { /* Not supported. */ return NULL; } static enum lttng_event_rule_generate_exclusions_status -lttng_event_rule_kprobe_generate_exclusions(const struct lttng_event_rule *rule, +lttng_event_rule_kernel_probe_generate_exclusions(const struct lttng_event_rule *rule, struct lttng_event_exclusion **exclusions) { /* Not supported. */ @@ -180,14 +181,14 @@ lttng_event_rule_kprobe_generate_exclusions(const struct lttng_event_rule *rule, } static unsigned long -lttng_event_rule_kprobe_hash( +lttng_event_rule_kernel_probe_hash( const struct lttng_event_rule *rule) { unsigned long hash; - struct lttng_event_rule_kprobe *krule = + struct lttng_event_rule_kernel_probe *krule = container_of(rule, typeof(*krule), parent); - hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KPROBE, + hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE, lttng_ht_seed); hash ^= hash_key_str(krule->name, lttng_ht_seed); hash ^= lttng_kernel_probe_location_hash(krule->location); @@ -195,47 +196,80 @@ lttng_event_rule_kprobe_hash( return hash; } -struct lttng_event_rule *lttng_event_rule_kprobe_create(void) +static +int kernel_probe_set_location( + struct lttng_event_rule_kernel_probe *kprobe, + const struct lttng_kernel_probe_location *location) +{ + int ret; + struct lttng_kernel_probe_location *location_copy = NULL; + + if (!kprobe || !location || kprobe->location) { + ret = -1; + goto end; + } + + location_copy = lttng_kernel_probe_location_copy(location); + if (!location_copy) { + ret = -1; + goto end; + } + + kprobe->location = location_copy; + location_copy = NULL; + ret = 0; +end: + lttng_kernel_probe_location_destroy(location_copy); + return ret; +} + +struct lttng_event_rule *lttng_event_rule_kernel_probe_create( + const struct lttng_kernel_probe_location *location) { struct lttng_event_rule *rule = NULL; - struct lttng_event_rule_kprobe *krule; + struct lttng_event_rule_kernel_probe *krule; - krule = zmalloc(sizeof(struct lttng_event_rule_kprobe)); + krule = zmalloc(sizeof(struct lttng_event_rule_kernel_probe)); if (!krule) { goto end; } rule = &krule->parent; - lttng_event_rule_init(&krule->parent, LTTNG_EVENT_RULE_TYPE_KPROBE); - krule->parent.validate = lttng_event_rule_kprobe_validate; - krule->parent.serialize = lttng_event_rule_kprobe_serialize; - krule->parent.equal = lttng_event_rule_kprobe_is_equal; - krule->parent.destroy = lttng_event_rule_kprobe_destroy; + lttng_event_rule_init(&krule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE); + krule->parent.validate = lttng_event_rule_kernel_probe_validate; + krule->parent.serialize = lttng_event_rule_kernel_probe_serialize; + krule->parent.equal = lttng_event_rule_kernel_probe_is_equal; + krule->parent.destroy = lttng_event_rule_kernel_probe_destroy; krule->parent.generate_filter_bytecode = - lttng_event_rule_kprobe_generate_filter_bytecode; - krule->parent.get_filter = lttng_event_rule_kprobe_get_filter; + lttng_event_rule_kernel_probe_generate_filter_bytecode; + krule->parent.get_filter = lttng_event_rule_kernel_probe_get_filter; krule->parent.get_filter_bytecode = - lttng_event_rule_kprobe_get_filter_bytecode; + lttng_event_rule_kernel_probe_get_filter_bytecode; krule->parent.generate_exclusions = - lttng_event_rule_kprobe_generate_exclusions; - krule->parent.hash = lttng_event_rule_kprobe_hash; + lttng_event_rule_kernel_probe_generate_exclusions; + krule->parent.hash = lttng_event_rule_kernel_probe_hash; + + if (kernel_probe_set_location(krule, location)) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + end: return rule; } LTTNG_HIDDEN -ssize_t lttng_event_rule_kprobe_create_from_payload( +ssize_t lttng_event_rule_kernel_probe_create_from_payload( struct lttng_payload_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 struct lttng_event_rule_kernel_probe_comm *kprobe_comm; const char *name; struct lttng_buffer_view current_buffer_view; struct lttng_event_rule *rule = NULL; - struct lttng_event_rule_kprobe *kprobe = NULL; - struct lttng_kernel_probe_location *location; + struct lttng_kernel_probe_location *location = NULL; if (!_event_rule) { ret = -1; @@ -252,15 +286,6 @@ ssize_t lttng_event_rule_kprobe_create_from_payload( kprobe_comm = (typeof(kprobe_comm)) current_buffer_view.data; - 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_buffer_view.size; @@ -311,12 +336,17 @@ ssize_t lttng_event_rule_kprobe_create_from_payload( goto end; } - kprobe->location = location; - /* Skip after the location */ offset += kprobe_comm->location_len; - status = lttng_event_rule_kprobe_set_name(rule, name); + rule = lttng_event_rule_kernel_probe_create(location); + if (!rule) { + ERR("Failed to create event rule kprobe."); + ret = -1; + goto end; + } + + status = lttng_event_rule_kernel_probe_set_event_name(rule, name); if (status != LTTNG_EVENT_RULE_STATUS_OK) { ERR("Failed to set event rule kprobe name."); ret = -1; @@ -327,54 +357,24 @@ ssize_t lttng_event_rule_kprobe_create_from_payload( rule = NULL; ret = offset; end: + lttng_kernel_probe_location_destroy(location); lttng_event_rule_destroy(rule); return ret; } -enum lttng_event_rule_status lttng_event_rule_kprobe_set_location( - struct lttng_event_rule *rule, - const struct lttng_kernel_probe_location *location) -{ - struct lttng_kernel_probe_location *location_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) || !location) { - status = LTTNG_EVENT_RULE_STATUS_INVALID; - goto end; - } - - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); - location_copy = lttng_kernel_probe_location_copy(location); - if (!location_copy) { - status = LTTNG_EVENT_RULE_STATUS_ERROR; - goto end; - } - - if (kprobe->location) { - lttng_kernel_probe_location_destroy(kprobe->location); - } - - kprobe->location = location_copy; - location_copy = NULL; -end: - lttng_kernel_probe_location_destroy(location_copy); - return status; -} - -enum lttng_event_rule_status lttng_event_rule_kprobe_get_location( +enum lttng_event_rule_status lttng_event_rule_kernel_probe_get_location( const struct lttng_event_rule *rule, const struct lttng_kernel_probe_location **location) { enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !location) { status = LTTNG_EVENT_RULE_STATUS_INVALID; goto end; } - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); *location = kprobe->location; if (!*location) { @@ -386,11 +386,11 @@ end: return status; } -enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( +enum lttng_event_rule_status lttng_event_rule_kernel_probe_set_event_name( struct lttng_event_rule *rule, const char *name) { char *name_copy = NULL; - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name || @@ -399,7 +399,7 @@ enum lttng_event_rule_status lttng_event_rule_kprobe_set_name( goto end; } - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); name_copy = strdup(name); if (!name_copy) { status = LTTNG_EVENT_RULE_STATUS_ERROR; @@ -414,10 +414,10 @@ end: return status; } -enum lttng_event_rule_status lttng_event_rule_kprobe_get_name( +enum lttng_event_rule_status lttng_event_rule_kernel_probe_get_event_name( const struct lttng_event_rule *rule, const char **name) { - struct lttng_event_rule_kprobe *kprobe; + struct lttng_event_rule_kernel_probe *kprobe; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name) { @@ -425,7 +425,7 @@ enum lttng_event_rule_status lttng_event_rule_kprobe_get_name( goto end; } - kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent); + kprobe = container_of(rule, struct lttng_event_rule_kernel_probe, parent); if (!kprobe->name) { status = LTTNG_EVENT_RULE_STATUS_UNSET; goto end; diff --git a/src/common/event-rule/syscall.c b/src/common/event-rule/syscall.c index b1b556aba..22353028f 100644 --- a/src/common/event-rule/syscall.c +++ b/src/common/event-rule/syscall.c @@ -249,6 +249,7 @@ struct lttng_event_rule *lttng_event_rule_syscall_create(void) { struct lttng_event_rule *rule = NULL; struct lttng_event_rule_syscall *syscall_rule; + enum lttng_event_rule_status status; syscall_rule = zmalloc(sizeof(struct lttng_event_rule_syscall)); if (!syscall_rule) { @@ -271,6 +272,14 @@ struct lttng_event_rule *lttng_event_rule_syscall_create(void) syscall_rule->parent.generate_exclusions = lttng_event_rule_syscall_generate_exclusions; syscall_rule->parent.hash = lttng_event_rule_syscall_hash; + + /* Default pattern is '*' */ + status = lttng_event_rule_syscall_set_pattern(rule, "*"); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + end: return rule; } diff --git a/src/common/event-rule/tracepoint.c b/src/common/event-rule/tracepoint.c index c8111a372..c3bac05c2 100644 --- a/src/common/event-rule/tracepoint.c +++ b/src/common/event-rule/tracepoint.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #define IS_TRACEPOINT_EVENT_RULE(rule) \ @@ -75,18 +77,22 @@ static int lttng_event_rule_tracepoint_serialize( struct lttng_payload *payload) { int ret, i; - size_t pattern_len, filter_expression_len, exclusions_len; + size_t pattern_len, filter_expression_len, exclusions_len, header_offset; + size_t size_before_log_level_rule; struct lttng_event_rule_tracepoint *tracepoint; struct lttng_event_rule_tracepoint_comm tracepoint_comm; enum lttng_event_rule_status status; unsigned int exclusion_count; size_t exclusions_appended_len = 0; + struct lttng_event_rule_tracepoint_comm *header; if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { ret = -1; goto end; } + header_offset = payload->buffer.size; + DBG("Serializing tracepoint event rule."); tracepoint = container_of( rule, struct lttng_event_rule_tracepoint, parent); @@ -118,8 +124,6 @@ static int lttng_event_rule_tracepoint_serialize( } 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 = exclusion_count; @@ -143,6 +147,16 @@ static int lttng_event_rule_tracepoint_serialize( goto end; } + size_before_log_level_rule = payload->buffer.size; + + ret = lttng_log_level_rule_serialize(tracepoint->log_level_rule, payload); + if (ret < 0) { + goto end; + } + + header = (typeof(header)) ((char *) payload->buffer.data + header_offset); + header->log_level_rule_len = payload->buffer.size - size_before_log_level_rule; + for (i = 0; i < exclusion_count; i++) { size_t len; const char *exclusion; @@ -224,11 +238,7 @@ static bool lttng_event_rule_tracepoint_is_equal( goto end; } - if (a->loglevel.type != b->loglevel.type) { - goto end; - } - - if (a->loglevel.value != b->loglevel.value) { + if (!lttng_log_level_rule_is_equal(a->log_level_rule, b->log_level_rule)) { goto end; } @@ -266,7 +276,7 @@ static int generate_agent_filter( char *agent_filter = NULL; const char *pattern; const char *filter; - enum lttng_loglevel_type loglevel_type; + const struct lttng_log_level_rule *log_level_rule = NULL; enum lttng_event_rule_status status; assert(rule); @@ -286,12 +296,6 @@ static int generate_agent_filter( goto end; } - status = lttng_event_rule_tracepoint_get_log_level_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) { @@ -311,21 +315,32 @@ static int generate_agent_filter( } } - if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + status = lttng_event_rule_tracepoint_get_log_level_rule( + rule, &log_level_rule); + if (status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_log_level_rule_status llr_status; const char *op; - int loglevel_value; + int level; - status = lttng_event_rule_tracepoint_get_log_level( - rule, &loglevel_value); - if (status != LTTNG_EVENT_RULE_STATUS_OK) { - ret = -1; - goto end; + switch (lttng_log_level_rule_get_type(log_level_rule)) + { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &level); + op = "=="; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &level); + op = ">="; + break; + default: + abort(); } - if (loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE) { - op = ">="; - } else { - op = "=="; + if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) { + ret = -1; + goto end; } if (filter || agent_filter) { @@ -334,14 +349,14 @@ static int generate_agent_filter( err = asprintf(&new_filter, "(%s) && (int_loglevel %s %d)", agent_filter ? agent_filter : filter, - op, loglevel_value); + op, level); if (agent_filter) { free(agent_filter); } agent_filter = new_filter; } else { err = asprintf(&agent_filter, "int_loglevel %s %d", op, - loglevel_value); + level); } if (err < 0) { @@ -576,12 +591,8 @@ static unsigned long lttng_event_rule_tracepoint_hash( hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed); } - hash ^= hash_key_ulong((void *) tp_rule->loglevel.type, - lttng_ht_seed); - if (tp_rule->loglevel.type != LTTNG_EVENT_LOGLEVEL_ALL) { - hash ^= hash_key_ulong( - (void *) (unsigned long) tp_rule->loglevel.value, - lttng_ht_seed); + if (tp_rule->log_level_rule) { + hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule); } status = lttng_event_rule_tracepoint_get_exclusions_count(rule, @@ -607,6 +618,10 @@ static struct lttng_event *lttng_event_rule_tracepoint_generate_lttng_event( const struct lttng_event_rule_tracepoint *tracepoint; struct lttng_event *local_event = NULL; struct lttng_event *event = NULL; + enum lttng_loglevel_type loglevel_type; + int loglevel_value = 0; + enum lttng_event_rule_status status; + const struct lttng_log_level_rule *log_level_rule; tracepoint = container_of( rule, const struct lttng_event_rule_tracepoint, parent); @@ -625,8 +640,40 @@ static struct lttng_event *lttng_event_rule_tracepoint_generate_lttng_event( goto error; } - local_event->loglevel_type = tracepoint->loglevel.type; - local_event->loglevel = tracepoint->loglevel.value; + + /* Map the log level rule to an equivalent lttng_loglevel */ + status = lttng_event_rule_tracepoint_get_log_level_rule(rule, &log_level_rule); + if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { + loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + loglevel_value = 0; + } else if (status == LTTNG_EVENT_RULE_STATUS_OK) { + enum lttng_log_level_rule_status llr_status; + + switch (lttng_log_level_rule_get_type(log_level_rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &loglevel_value); + loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &loglevel_value); + loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; + break; + default: + abort(); + break; + } + + if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) { + goto error; + } + } else { + goto error; + } + + local_event->loglevel_type = loglevel_type; + local_event->loglevel = loglevel_value; event = local_event; local_event = NULL; @@ -640,6 +687,7 @@ struct lttng_event_rule *lttng_event_rule_tracepoint_create( { struct lttng_event_rule *rule = NULL; struct lttng_event_rule_tracepoint *tp_rule; + enum lttng_event_rule_status status; if (domain_type == LTTNG_DOMAIN_NONE) { goto end; @@ -669,10 +717,18 @@ struct lttng_event_rule *lttng_event_rule_tracepoint_create( lttng_event_rule_tracepoint_generate_lttng_event; tp_rule->domain = domain_type; - tp_rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL; + tp_rule->log_level_rule = NULL; lttng_dynamic_pointer_array_init(&tp_rule->exclusions, destroy_lttng_exclusions_element); + + /* Default pattern is '*' */ + status = lttng_event_rule_tracepoint_set_pattern(rule, "*"); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + end: return rule; } @@ -686,7 +742,6 @@ ssize_t lttng_event_rule_tracepoint_create_from_payload( int i; 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; @@ -695,6 +750,7 @@ ssize_t lttng_event_rule_tracepoint_create_from_payload( const char *exclusion; struct lttng_buffer_view current_buffer_view; struct lttng_event_rule *rule = NULL; + struct lttng_log_level_rule *log_level_rule = NULL; if (!_event_rule) { ret = -1; @@ -728,32 +784,6 @@ ssize_t lttng_event_rule_tracepoint_create_from_payload( 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_log_level_all(rule); - break; - case LTTNG_EVENT_LOGLEVEL_RANGE: - status = lttng_event_rule_tracepoint_set_log_level_range_lower_bound(rule, - (enum lttng_loglevel_type) tracepoint_comm - ->loglevel_value); - break; - case LTTNG_EVENT_LOGLEVEL_SINGLE: - status = lttng_event_rule_tracepoint_set_log_level(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_buffer_view.size; @@ -800,6 +830,29 @@ ssize_t lttng_event_rule_tracepoint_create_from_payload( offset += tracepoint_comm->filter_expression_len; skip_filter_expression: + if (!tracepoint_comm->log_level_rule_len) { + goto skip_log_level_rule; + } + + { + /* Map the log level rule. */ + struct lttng_payload_view current_payload_view = + lttng_payload_view_from_view(view, offset, + tracepoint_comm->log_level_rule_len); + ret = lttng_log_level_rule_create_from_payload( + ¤t_payload_view, &log_level_rule); + if (ret < 0) { + ret = -1; + goto end; + } + + assert(ret == tracepoint_comm->log_level_rule_len); + } + + /* Skip after the log level rule. */ + offset += tracepoint_comm->log_level_rule_len; + +skip_log_level_rule: for (i = 0; i < tracepoint_comm->exclusions_count; i++) { current_buffer_view = lttng_buffer_view_from_view( &view->buffer, offset, sizeof(*exclusion_len)); @@ -854,11 +907,22 @@ skip_filter_expression: } } + if (log_level_rule) { + status = lttng_event_rule_tracepoint_set_log_level_rule( + rule, log_level_rule); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ERR("Failed to set event rule tracepoint log level rule."); + ret = -1; + goto end; + } + } + *_event_rule = rule; rule = NULL; ret = offset; end: free(exclusions); + lttng_log_level_rule_destroy(log_level_rule); lttng_event_rule_destroy(rule); return ret; } @@ -989,10 +1053,25 @@ end: return status; } -static bool log_level_value_valid( - int level, enum lttng_domain_type domain) +static bool log_level_rule_valid( + const struct lttng_log_level_rule *rule , enum lttng_domain_type domain) { bool valid = false; + enum lttng_log_level_rule_status status; + int level; + + switch(lttng_log_level_rule_get_type(rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + status = lttng_log_level_rule_exactly_get_level(rule, &level); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + status = lttng_log_level_rule_at_least_as_severe_as_get_level(rule, &level); + break; + default: + abort(); + } + + assert(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); switch (domain) { case LTTNG_DOMAIN_KERNEL: @@ -1031,11 +1110,13 @@ end: return valid; } -enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level( - struct lttng_event_rule *rule, int level) +enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level_rule( + struct lttng_event_rule *rule, + const struct lttng_log_level_rule *log_level_rule) { struct lttng_event_rule_tracepoint *tracepoint; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + struct lttng_log_level_rule *copy = NULL; if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) { status = LTTNG_EVENT_RULE_STATUS_INVALID; @@ -1045,99 +1126,48 @@ enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level( tracepoint = container_of( rule, struct lttng_event_rule_tracepoint, parent); - if (!log_level_value_valid(level, tracepoint->domain)) { + if (!log_level_rule_valid(log_level_rule, tracepoint->domain)) { status = LTTNG_EVENT_RULE_STATUS_INVALID; goto end; } - tracepoint->loglevel.value = level; - tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_SINGLE; -end: - return status; -} - -enum lttng_event_rule_status -lttng_event_rule_tracepoint_set_log_level_range_lower_bound( - 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)) { - status = LTTNG_EVENT_RULE_STATUS_INVALID; + copy = lttng_log_level_rule_copy(log_level_rule); + if (copy == NULL) { + status = LTTNG_EVENT_RULE_STATUS_ERROR; goto end; } - tracepoint = container_of( - rule, struct lttng_event_rule_tracepoint, parent); - - if (!log_level_value_valid(level, tracepoint->domain)) { - status = LTTNG_EVENT_RULE_STATUS_INVALID; - goto end; + if (tracepoint->log_level_rule) { + lttng_log_level_rule_destroy(tracepoint->log_level_rule); } - tracepoint->loglevel.value = level; - tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_RANGE; -end: - return status; -} - -enum lttng_event_rule_status lttng_event_rule_tracepoint_set_log_level_all( - struct lttng_event_rule *rule) -{ - struct lttng_event_rule_tracepoint *tracepoint; - enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; + tracepoint->log_level_rule = copy; - 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_log_level_type( +enum lttng_event_rule_status lttng_event_rule_tracepoint_get_log_level_rule( 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_log_level( - const struct lttng_event_rule *rule, int *level) + const struct lttng_log_level_rule **log_level_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) || !level) { + if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !log_level_rule) { 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) { + if (tracepoint->log_level_rule == NULL) { status = LTTNG_EVENT_RULE_STATUS_UNSET; goto end; } - *level = tracepoint->loglevel.value; + *log_level_rule = tracepoint->log_level_rule; end: return status; } diff --git a/src/common/event-rule/uprobe.c b/src/common/event-rule/userspace-probe.c similarity index 63% rename from src/common/event-rule/uprobe.c rename to src/common/event-rule/userspace-probe.c index 67f99c482..52c7553e9 100644 --- a/src/common/event-rule/uprobe.c +++ b/src/common/event-rule/userspace-probe.c @@ -15,34 +15,34 @@ #include #include #include -#include +#include #include #define IS_UPROBE_EVENT_RULE(rule) \ - (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE) + (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE) -static void lttng_event_rule_uprobe_destroy(struct lttng_event_rule *rule) +static void lttng_event_rule_userspace_probe_destroy(struct lttng_event_rule *rule) { - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); lttng_userspace_probe_location_destroy(uprobe->location); free(uprobe->name); free(uprobe); } -static bool lttng_event_rule_uprobe_validate( +static bool lttng_event_rule_userspace_probe_validate( const struct lttng_event_rule *rule) { bool valid = false; - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; if (!rule) { goto end; } - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); /* Required field. */ if (!uprobe->name) { @@ -60,15 +60,15 @@ end: return valid; } -static int lttng_event_rule_uprobe_serialize( +static int lttng_event_rule_userspace_probe_serialize( const struct lttng_event_rule *rule, struct lttng_payload *payload) { 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 = {}; - struct lttng_event_rule_uprobe_comm *header; + struct lttng_event_rule_userspace_probe *uprobe; + struct lttng_event_rule_userspace_probe_comm uprobe_comm = {}; + struct lttng_event_rule_userspace_probe_comm *header; if (!rule || !IS_UPROBE_EVENT_RULE(rule)) { ret = -1; @@ -78,7 +78,7 @@ static int lttng_event_rule_uprobe_serialize( header_offset = payload->buffer.size; DBG("Serializing uprobe event rule."); - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); name_len = strlen(uprobe->name) + 1; @@ -105,7 +105,7 @@ static int lttng_event_rule_uprobe_serialize( } /* Update the header regarding the probe size. */ - header = (struct lttng_event_rule_uprobe_comm + header = (struct lttng_event_rule_userspace_probe_comm *) ((char *) payload->buffer.data + header_offset); header->location_len = payload->buffer.size - size_before_probe; @@ -116,14 +116,14 @@ end: return ret; } -static bool lttng_event_rule_uprobe_is_equal(const struct lttng_event_rule *_a, +static bool lttng_event_rule_userspace_probe_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; + struct lttng_event_rule_userspace_probe *a, *b; - a = container_of(_a, struct lttng_event_rule_uprobe, parent); - b = container_of(_b, struct lttng_event_rule_uprobe, parent); + a = container_of(_a, struct lttng_event_rule_userspace_probe, parent); + b = container_of(_b, struct lttng_event_rule_userspace_probe, parent); /* uprobe is invalid if this is not true. */ assert(a->name); @@ -140,7 +140,7 @@ end: return is_equal; } -static enum lttng_error_code lttng_event_rule_uprobe_generate_filter_bytecode( +static enum lttng_error_code lttng_event_rule_userspace_probe_generate_filter_bytecode( struct lttng_event_rule *rule, const struct lttng_credentials *creds) { @@ -148,7 +148,7 @@ static enum lttng_error_code lttng_event_rule_uprobe_generate_filter_bytecode( return LTTNG_OK; } -static const char *lttng_event_rule_uprobe_get_filter( +static const char *lttng_event_rule_userspace_probe_get_filter( const struct lttng_event_rule *rule) { /* Unsupported. */ @@ -156,14 +156,14 @@ static const char *lttng_event_rule_uprobe_get_filter( } static const struct lttng_bytecode * -lttng_event_rule_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule) +lttng_event_rule_userspace_probe_get_filter_bytecode(const struct lttng_event_rule *rule) { /* Unsupported. */ return NULL; } static enum lttng_event_rule_generate_exclusions_status -lttng_event_rule_uprobe_generate_exclusions(const struct lttng_event_rule *rule, +lttng_event_rule_userspace_probe_generate_exclusions(const struct lttng_event_rule *rule, struct lttng_event_exclusion **exclusions) { /* Unsupported. */ @@ -172,14 +172,14 @@ lttng_event_rule_uprobe_generate_exclusions(const struct lttng_event_rule *rule, } static unsigned long -lttng_event_rule_uprobe_hash( +lttng_event_rule_userspace_probe_hash( const struct lttng_event_rule *rule) { unsigned long hash; - struct lttng_event_rule_uprobe *urule = + struct lttng_event_rule_userspace_probe *urule = container_of(rule, typeof(*urule), parent); - hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_UPROBE, + hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE, lttng_ht_seed); hash ^= hash_key_str(urule->name, lttng_ht_seed); hash ^= lttng_userspace_probe_location_hash(urule->location); @@ -187,46 +187,79 @@ lttng_event_rule_uprobe_hash( return hash; } -struct lttng_event_rule *lttng_event_rule_uprobe_create(void) +static +int userspace_probe_set_location( + struct lttng_event_rule_userspace_probe *uprobe, + const struct lttng_userspace_probe_location *location) +{ + int ret; + struct lttng_userspace_probe_location *location_copy = NULL; + + if (!uprobe || !location || uprobe->location) { + ret = -1; + goto end; + } + + location_copy = lttng_userspace_probe_location_copy(location); + if (!location_copy) { + ret = -1; + goto end; + } + + uprobe->location = location_copy; + location_copy = NULL; + ret = 0; +end: + lttng_userspace_probe_location_destroy(location_copy); + return ret; +} + +struct lttng_event_rule *lttng_event_rule_userspace_probe_create( + const struct lttng_userspace_probe_location *location) { struct lttng_event_rule *rule = NULL; - struct lttng_event_rule_uprobe *urule; + struct lttng_event_rule_userspace_probe *urule; - urule = zmalloc(sizeof(struct lttng_event_rule_uprobe)); + urule = zmalloc(sizeof(struct lttng_event_rule_userspace_probe)); if (!urule) { goto end; } rule = &urule->parent; - lttng_event_rule_init(&urule->parent, LTTNG_EVENT_RULE_TYPE_UPROBE); - urule->parent.validate = lttng_event_rule_uprobe_validate; - urule->parent.serialize = lttng_event_rule_uprobe_serialize; - urule->parent.equal = lttng_event_rule_uprobe_is_equal; - urule->parent.destroy = lttng_event_rule_uprobe_destroy; + lttng_event_rule_init(&urule->parent, LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE); + urule->parent.validate = lttng_event_rule_userspace_probe_validate; + urule->parent.serialize = lttng_event_rule_userspace_probe_serialize; + urule->parent.equal = lttng_event_rule_userspace_probe_is_equal; + urule->parent.destroy = lttng_event_rule_userspace_probe_destroy; urule->parent.generate_filter_bytecode = - lttng_event_rule_uprobe_generate_filter_bytecode; - urule->parent.get_filter = lttng_event_rule_uprobe_get_filter; + lttng_event_rule_userspace_probe_generate_filter_bytecode; + urule->parent.get_filter = lttng_event_rule_userspace_probe_get_filter; urule->parent.get_filter_bytecode = - lttng_event_rule_uprobe_get_filter_bytecode; + lttng_event_rule_userspace_probe_get_filter_bytecode; urule->parent.generate_exclusions = - lttng_event_rule_uprobe_generate_exclusions; - urule->parent.hash = lttng_event_rule_uprobe_hash; + lttng_event_rule_userspace_probe_generate_exclusions; + urule->parent.hash = lttng_event_rule_userspace_probe_hash; + + if (userspace_probe_set_location(urule, location)) { + lttng_event_rule_destroy(rule); + rule = NULL; + } + end: return rule; } LTTNG_HIDDEN -ssize_t lttng_event_rule_uprobe_create_from_payload( +ssize_t lttng_event_rule_userspace_probe_create_from_payload( struct lttng_payload_view *view, struct lttng_event_rule **_event_rule) { ssize_t ret, offset = 0; - const struct lttng_event_rule_uprobe_comm *uprobe_comm; + const struct lttng_event_rule_userspace_probe_comm *uprobe_comm; const char *name; struct lttng_buffer_view current_buffer_view; struct lttng_event_rule *rule = NULL; - struct lttng_userspace_probe_location *location; - struct lttng_event_rule_uprobe *uprobe; + struct lttng_userspace_probe_location *location = NULL; enum lttng_event_rule_status status; if (!_event_rule) { @@ -244,13 +277,6 @@ ssize_t lttng_event_rule_uprobe_create_from_payload( uprobe_comm = (typeof(uprobe_comm)) current_buffer_view.data; - rule = lttng_event_rule_uprobe_create(); - if (!rule) { - ERR("Failed to create event rule uprobe"); - ret = -1; - goto end; - } - /* Skip to payload. */ offset += current_buffer_view.size; @@ -297,16 +323,20 @@ ssize_t lttng_event_rule_uprobe_create_from_payload( /* Skip after the location. */ offset += uprobe_comm->location_len; - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); - uprobe->location = location; + rule = lttng_event_rule_userspace_probe_create(location); + if (!rule) { + ERR("Failed to create event rule uprobe."); + ret = -1; + goto end; + } - status = lttng_event_rule_uprobe_set_name(rule, name); + status = lttng_event_rule_userspace_probe_set_event_name(rule, name); if (status != LTTNG_EVENT_RULE_STATUS_OK) { ret = -1; goto end; } - if (!lttng_event_rule_uprobe_validate(rule)) { + if (!lttng_event_rule_userspace_probe_validate(rule)) { ret = -1; goto end; } @@ -315,42 +345,13 @@ ssize_t lttng_event_rule_uprobe_create_from_payload( rule = NULL; ret = offset; end: + lttng_userspace_probe_location_destroy(location); 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( +enum lttng_event_rule_status lttng_event_rule_userspace_probe_get_location( const struct lttng_event_rule *rule, const struct lttng_userspace_probe_location **location) { @@ -361,7 +362,7 @@ enum lttng_event_rule_status lttng_event_rule_uprobe_get_location( goto end; } - *location = lttng_event_rule_uprobe_get_location_mutable(rule); + *location = lttng_event_rule_userspace_probe_get_location_mutable(rule); if (!*location) { status = LTTNG_EVENT_RULE_STATUS_UNSET; goto end; @@ -373,22 +374,22 @@ end: LTTNG_HIDDEN struct lttng_userspace_probe_location * -lttng_event_rule_uprobe_get_location_mutable( +lttng_event_rule_userspace_probe_get_location_mutable( const struct lttng_event_rule *rule) { - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; assert(rule); - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); return uprobe->location; } -enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( +enum lttng_event_rule_status lttng_event_rule_userspace_probe_set_event_name( struct lttng_event_rule *rule, const char *name) { char *name_copy = NULL; - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name || @@ -397,7 +398,7 @@ enum lttng_event_rule_status lttng_event_rule_uprobe_set_name( goto end; } - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); name_copy = strdup(name); if (!name_copy) { status = LTTNG_EVENT_RULE_STATUS_ERROR; @@ -414,10 +415,10 @@ end: return status; } -enum lttng_event_rule_status lttng_event_rule_uprobe_get_name( +enum lttng_event_rule_status lttng_event_rule_userspace_probe_get_event_name( const struct lttng_event_rule *rule, const char **name) { - struct lttng_event_rule_uprobe *uprobe; + struct lttng_event_rule_userspace_probe *uprobe; enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name) { @@ -425,7 +426,7 @@ enum lttng_event_rule_status lttng_event_rule_uprobe_get_name( goto end; } - uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent); + uprobe = container_of(rule, struct lttng_event_rule_userspace_probe, parent); if (!uprobe->name) { status = LTTNG_EVENT_RULE_STATUS_UNSET; goto end; diff --git a/src/common/hashtable/hashtable.c b/src/common/hashtable/hashtable.c index f449fab8b..af16676cc 100644 --- a/src/common/hashtable/hashtable.c +++ b/src/common/hashtable/hashtable.c @@ -76,6 +76,24 @@ static int match_two_u64(struct cds_lfht_node *node, const void *key) return hash_match_key_two_u64((void *) &match_node->key, (void *) key); } +static inline +const char *lttng_ht_type_str(enum lttng_ht_type type) +{ + switch (type) { + case LTTNG_HT_TYPE_STRING: + return "STRING"; + case LTTNG_HT_TYPE_ULONG: + return "ULONG"; + case LTTNG_HT_TYPE_U64: + return "U64"; + case LTTNG_HT_TYPE_TWO_U64: + return "TWO_U64"; + default: + ERR("Unknown lttng hashtable type %d", type); + abort(); + } +} + /* * Return an allocated lttng hashtable. */ @@ -132,7 +150,8 @@ struct lttng_ht *lttng_ht_new(unsigned long size, int type) goto error; } - DBG3("Created hashtable size %lu at %p of type %d", size, ht->ht, type); + DBG3("Created hashtable size %lu at %p of type %s", size, ht->ht, + lttng_ht_type_str(type)); return ht; diff --git a/src/common/index-allocator.c b/src/common/index-allocator.c new file mode 100644 index 000000000..ff5e2c886 --- /dev/null +++ b/src/common/index-allocator.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include + +#include +#include + +#include "macros.h" +#include "error.h" + +#include "index-allocator.h" + +struct lttng_index_allocator { + struct cds_list_head unused_list; + uint64_t size; + uint64_t position; +}; + +struct lttng_index { + uint64_t index; + struct cds_list_head head; +}; + +struct lttng_index_allocator *lttng_index_allocator_create( + uint64_t index_count) +{ + struct lttng_index_allocator *allocator = NULL; + + allocator = zmalloc(sizeof(*allocator)); + if (!allocator) { + PERROR("Failed to allocate free index queue"); + goto end; + } + allocator->size = index_count; + allocator->position = 0; + + CDS_INIT_LIST_HEAD(&allocator->unused_list); + +end: + return allocator; +} + +uint64_t lttng_index_allocator_get_index_count(struct lttng_index_allocator *allocator) +{ + return allocator->size; +} + +enum lttng_index_allocator_status lttng_index_allocator_alloc( + struct lttng_index_allocator *allocator, + uint64_t *allocated_index) +{ + enum lttng_index_allocator_status status = + LTTNG_INDEX_ALLOCATOR_STATUS_OK; + + if (cds_list_empty(&allocator->unused_list)) { + if (allocator->position >= allocator->size) { + /* No indices left. */ + status = LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY; + goto end; + } + + *allocated_index = allocator->position++; + } else { + struct lttng_index *index; + + index = cds_list_first_entry(&allocator->unused_list, + typeof(*index), head); + cds_list_del(&index->head); + *allocated_index = index->index; + free(index); + } + +end: + return status; +} + +enum lttng_index_allocator_status lttng_index_allocator_release( + struct lttng_index_allocator *allocator, uint64_t idx) +{ + struct lttng_index *index = NULL; + enum lttng_index_allocator_status status = + LTTNG_INDEX_ALLOCATOR_STATUS_OK; + + assert(idx < allocator->size); + + index = zmalloc(sizeof(*index)); + if (!index) { + PERROR("Failed to allocate free index queue"); + status = LTTNG_INDEX_ALLOCATOR_STATUS_ERROR; + goto end; + } + + index->index = idx; + cds_list_add_tail(&index->head, &allocator->unused_list); + +end: + return status; +} + +void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator) +{ + struct lttng_index *index = NULL, *tmp_index = NULL; + + if (!allocator) { + return; + } + + cds_list_for_each_entry_safe(index, tmp_index, + &allocator->unused_list, head) { + cds_list_del(&index->head); + free(index); + } + + free(allocator); +} diff --git a/src/common/index-allocator.h b/src/common/index-allocator.h new file mode 100644 index 000000000..cdc575041 --- /dev/null +++ b/src/common/index-allocator.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef _COMMON_INDEX_ALLOCATOR_H +#define _COMMON_INDEX_ALLOCATOR_H + +#include + +struct lttng_index_allocator; + +enum lttng_index_allocator_status { + LTTNG_INDEX_ALLOCATOR_STATUS_OK, + LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY, + LTTNG_INDEX_ALLOCATOR_STATUS_ERROR, +}; + +struct lttng_index_allocator *lttng_index_allocator_create( + uint64_t index_count); + +uint64_t lttng_index_allocator_get_index_count( + struct lttng_index_allocator *allocator); + +enum lttng_index_allocator_status lttng_index_allocator_alloc( + struct lttng_index_allocator *allocator, + uint64_t *index); + +enum lttng_index_allocator_status lttng_index_allocator_release( + struct lttng_index_allocator *allocator, uint64_t index); + +void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator); + +#endif /* _COMMON_INDEX_ALLOCATOR_H */ diff --git a/src/common/kernel-ctl/kernel-ctl.c b/src/common/kernel-ctl/kernel-ctl.c index bb6602e2b..7bcfd5dc2 100644 --- a/src/common/kernel-ctl/kernel-ctl.c +++ b/src/common/kernel-ctl/kernel-ctl.c @@ -155,6 +155,12 @@ int kernctl_create_channel(int fd, struct lttng_channel_attr *chops) return LTTNG_IOCTL_NO_CHECK(fd, LTTNG_KERNEL_CHANNEL, &channel); } +int kernctl_create_session_counter(int session_fd, struct lttng_kernel_counter_conf *counter_conf) +{ + return LTTNG_IOCTL_NO_CHECK(session_fd, LTTNG_KERNEL_COUNTER, counter_conf); +} + + int kernctl_syscall_mask(int fd, char **syscall_mask, uint32_t *nr_bits) { struct lttng_kernel_syscall_mask kmask_len, *kmask = NULL; @@ -424,12 +430,64 @@ int kernctl_create_event_notifier_group(int fd) LTTNG_KERNEL_EVENT_NOTIFIER_GROUP_CREATE); } +int kernctl_create_counter_event(int fd, struct lttng_kernel_counter_event *ev) +{ + return LTTNG_IOCTL_NO_CHECK(fd, LTTNG_KERNEL_COUNTER_EVENT, ev); +} + int kernctl_create_event_notifier_group_notification_fd(int group_fd) { return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD); } +int kernctl_create_event_notifier_group_error_counter(int group_fd, + struct lttng_kernel_counter_conf *error_counter_conf) +{ + return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_COUNTER, + error_counter_conf); +} + +int kernctl_counter_read_value(int counter_fd, + struct lttng_kernel_counter_read *value) +{ + return LTTNG_IOCTL_NO_CHECK(counter_fd, LTTNG_KERNEL_COUNTER_READ, + value); +} + +int kernctl_counter_get_aggregate_value(int counter_fd, + struct lttng_kernel_counter_aggregate *value) +{ + return LTTNG_IOCTL_NO_CHECK(counter_fd, LTTNG_KERNEL_COUNTER_AGGREGATE, + value); +} + +int kernctl_counter_clear(int counter_fd, + struct lttng_kernel_counter_clear *clear) +{ + return LTTNG_IOCTL_NO_CHECK(counter_fd, LTTNG_KERNEL_COUNTER_CLEAR, + clear); +} + +int kernctl_counter_map_descriptor_count(int counter_fd, uint64_t *count) +{ + struct lttng_kernel_counter_map_nr_descriptors nr_desc; + int ret; + + ret = LTTNG_IOCTL_NO_CHECK(counter_fd, + LTTNG_KERNEL_COUNTER_MAP_NR_DESCRIPTORS, &nr_desc); + *count = nr_desc.nr_descriptors; + + return ret; +} + +int kernctl_counter_map_descriptor(int counter_fd, + struct lttng_kernel_counter_map_descriptor *descriptor) +{ + return LTTNG_IOCTL_NO_CHECK(counter_fd, + LTTNG_KERNEL_COUNTER_MAP_DESCRIPTOR, descriptor); +} + int kernctl_create_event_notifier(int group_fd, const struct lttng_kernel_event_notifier *event_notifier) { @@ -437,6 +495,25 @@ int kernctl_create_event_notifier(int group_fd, LTTNG_KERNEL_EVENT_NOTIFIER_CREATE, event_notifier); } +int kernctl_capture(int fd, const struct lttng_bytecode *capture) +{ + struct lttng_kernel_capture_bytecode *kb; + uint32_t len; + int ret; + + /* Translate bytecode to kernel bytecode */ + kb = zmalloc(sizeof(*kb) + capture->len); + if (!kb) + return -ENOMEM; + kb->len = len = capture->len; + kb->reloc_offset = capture->reloc_table_offset; + kb->seqnum = capture->seqnum; + memcpy(kb->data, capture->data, len); + ret = LTTNG_IOCTL_CHECK(fd, LTTNG_KERNEL_CAPTURE, kb); + free(kb); + return ret; +} + int kernctl_filter(int fd, const struct lttng_bytecode *filter) { struct lttng_kernel_filter_bytecode *kb; diff --git a/src/common/kernel-ctl/kernel-ctl.h b/src/common/kernel-ctl/kernel-ctl.h index 3bbe69f48..537590f35 100644 --- a/src/common/kernel-ctl/kernel-ctl.h +++ b/src/common/kernel-ctl/kernel-ctl.h @@ -19,8 +19,10 @@ int kernctl_create_session(int fd); int kernctl_open_metadata(int fd, struct lttng_channel_attr *chops); int kernctl_create_channel(int fd, struct lttng_channel_attr *chops); +int kernctl_create_session_counter(int session_fd, struct lttng_kernel_counter_conf *counter_conf); int kernctl_create_stream(int fd); int kernctl_create_event(int fd, struct lttng_kernel_event *ev); +int kernctl_create_counter_event(int fd, struct lttng_kernel_counter_event *ev); int kernctl_add_context(int fd, struct lttng_kernel_context *ctx); int kernctl_enable(int fd); @@ -32,12 +34,26 @@ int kernctl_create_event_notifier_group(int fd); /* Apply on event notifier_group file descriptor. */ int kernctl_create_event_notifier_group_notification_fd(int fd); +int kernctl_create_event_notifier_group_error_counter(int fd, + struct lttng_kernel_counter_conf *error_counter_conf); int kernctl_create_event_notifier(int fd, const struct lttng_kernel_event_notifier *event_notifier); +int kernctl_counter_read_value(int counter_fd, + struct lttng_kernel_counter_read *value); +int kernctl_counter_get_aggregate_value(int counter_fd, + struct lttng_kernel_counter_aggregate *value); +int kernctl_counter_clear(int counter_fd, + struct lttng_kernel_counter_clear *clear); + +int kernctl_counter_map_descriptor_count(int counter_fd, uint64_t *count); +int kernctl_counter_map_descriptor(int counter_fd, + struct lttng_kernel_counter_map_descriptor *descriptor); + /* Apply on event file descriptor. */ int kernctl_filter(int fd, const struct lttng_bytecode *filter); int kernctl_add_callsite(int fd, struct lttng_kernel_event_callsite *callsite); +int kernctl_capture(int fd, const struct lttng_bytecode *capture); int kernctl_tracepoint_list(int fd); int kernctl_syscall_list(int fd); diff --git a/src/common/kernel-ctl/kernel-ioctl.h b/src/common/kernel-ctl/kernel-ioctl.h index 8fa0a7d10..e45359763 100644 --- a/src/common/kernel-ctl/kernel-ioctl.h +++ b/src/common/kernel-ctl/kernel-ioctl.h @@ -160,6 +160,10 @@ #define LTTNG_KERNEL_ENABLE _IO(0xF6, 0x82) #define LTTNG_KERNEL_DISABLE _IO(0xF6, 0x83) +/* Event notifier group ioctl */ +#define LTTNG_KERNEL_COUNTER \ + _IOW(0xF6, 0x84, struct lttng_kernel_counter_conf) + /* Event and event notifier FD ioctl */ #define LTTNG_KERNEL_FILTER _IO(0xF6, 0x90) #define LTTNG_KERNEL_ADD_CALLSITE _IO(0xF6, 0x91) @@ -178,4 +182,23 @@ #define LTTNG_KERNEL_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD \ _IO(0xF6, 0xB1) +/* Event notifier file descriptor ioctl */ +#define LTTNG_KERNEL_CAPTURE _IO(0xF6, 0xB8) + +/* Counter file descriptor ioctl */ +#define LTTNG_KERNEL_COUNTER_READ \ + _IOWR(0xF6, 0xC0, struct lttng_kernel_counter_read) +#define LTTNG_KERNEL_COUNTER_AGGREGATE \ + _IOWR(0xF6, 0xC1, struct lttng_kernel_counter_aggregate) +#define LTTNG_KERNEL_COUNTER_CLEAR \ + _IOW(0xF6, 0xC2, struct lttng_kernel_counter_clear) +#define LTTNG_KERNEL_COUNTER_MAP_NR_DESCRIPTORS \ + _IOR(0xF6, 0xC3, uint64_t) +#define LTTNG_KERNEL_COUNTER_MAP_DESCRIPTOR \ + _IOWR(0xF6, 0xC4, struct lttng_kernel_counter_map_descriptor) +#define LTTNG_KERNEL_COUNTER_EVENT \ + _IOW(0xF6, 0xC5, struct lttng_kernel_counter_event) + +/* LTTNG_KERNEL_EVENT also applies to counter fds. */ + #endif /* _LTT_KERNEL_IOCTL_H */ diff --git a/src/common/kernel-function.c b/src/common/kernel-function.c new file mode 100644 index 000000000..c0b1cef92 --- /dev/null +++ b/src/common/kernel-function.c @@ -0,0 +1,726 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "lttng/lttng-error.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static +int lttng_kernel_function_location_address_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload); + +static +int lttng_kernel_function_location_symbol_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload); + +static +bool lttng_kernel_function_location_address_is_equal( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); + +static +bool lttng_kernel_function_location_symbol_is_equal( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b); + +static +unsigned long lttng_kernel_function_location_address_hash( + const struct lttng_kernel_function_location *location); + +static +unsigned long lttng_kernel_function_location_symbol_hash( + const struct lttng_kernel_function_location *location); + +enum lttng_kernel_function_location_type lttng_kernel_function_location_get_type( + const struct lttng_kernel_function_location *location) +{ + return location ? location->type : + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_UNKNOWN; +} + +static +void lttng_kernel_function_location_address_destroy( + struct lttng_kernel_function_location *location) +{ + assert(location); + free(location); +} + +static +void lttng_kernel_function_location_symbol_destroy( + struct lttng_kernel_function_location *location) +{ + struct lttng_kernel_function_location_symbol *location_symbol = NULL; + + assert(location); + + location_symbol = container_of(location, + struct lttng_kernel_function_location_symbol, + parent); + + assert(location_symbol); + + free(location_symbol->symbol_name); + free(location); +} + +void lttng_kernel_function_location_destroy( + struct lttng_kernel_function_location *location) +{ + if (!location) { + return; + } + + switch (location->type) { + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS: + lttng_kernel_function_location_address_destroy(location); + break; + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET: + lttng_kernel_function_location_symbol_destroy(location); + break; + default: + abort(); + } +} + +struct lttng_kernel_function_location * +lttng_kernel_function_location_address_create(uint64_t address) +{ + struct lttng_kernel_function_location *ret = NULL; + struct lttng_kernel_function_location_address *location; + + location = zmalloc(sizeof(*location)); + if (!location) { + PERROR("Error allocating userspace function location."); + goto end; + } + + location->address = address; + + ret = &location->parent; + ret->type = LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS; + ret->equal = lttng_kernel_function_location_address_is_equal; + ret->serialize = lttng_kernel_function_location_address_serialize; + ret->hash = lttng_kernel_function_location_address_hash; + +end: + return ret; +} + +struct lttng_kernel_function_location * +lttng_kernel_function_location_symbol_create(const char *symbol_name, + uint64_t offset) +{ + char *symbol_name_copy = NULL; + struct lttng_kernel_function_location *ret = NULL; + struct lttng_kernel_function_location_symbol *location; + + if (!symbol_name || strlen(symbol_name) >= LTTNG_SYMBOL_NAME_LEN) { + goto error; + } + + symbol_name_copy = strdup(symbol_name); + if (!symbol_name_copy) { + PERROR("Failed to copy symbol name '%s'", symbol_name); + goto error; + } + + location = zmalloc(sizeof(*location)); + if (!location) { + PERROR("Failed to allocate kernel symbol function location"); + goto error; + } + + location->symbol_name = symbol_name_copy; + location->offset = offset; + + ret = &location->parent; + ret->type = LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET; + ret->equal = lttng_kernel_function_location_symbol_is_equal; + ret->serialize = lttng_kernel_function_location_symbol_serialize; + ret->hash = lttng_kernel_function_location_symbol_hash; + goto end; + +error: + free(symbol_name_copy); +end: + return ret; +} + +enum lttng_kernel_function_location_status +lttng_kernel_function_location_address_get_address( + const struct lttng_kernel_function_location *location, + uint64_t *offset) +{ + enum lttng_kernel_function_location_status ret = + LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK; + struct lttng_kernel_function_location_address *address_location; + + assert(offset); + + if (!location || lttng_kernel_function_location_get_type(location) != + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_INVALID; + goto end; + } + + address_location = container_of(location, + struct lttng_kernel_function_location_address, parent); + *offset = address_location->address; +end: + return ret; +} + +const char *lttng_kernel_function_location_symbol_get_name( + const struct lttng_kernel_function_location *location) +{ + const char *ret = NULL; + struct lttng_kernel_function_location_symbol *symbol_location; + + if (!location || lttng_kernel_function_location_get_type(location) != + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + symbol_location = container_of(location, + struct lttng_kernel_function_location_symbol, parent); + ret = symbol_location->symbol_name; +end: + return ret; +} + +enum lttng_kernel_function_location_status +lttng_kernel_function_location_symbol_get_offset( + const struct lttng_kernel_function_location *location, + uint64_t *offset) +{ + enum lttng_kernel_function_location_status ret = + LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK; + struct lttng_kernel_function_location_symbol *symbol_location; + + assert(offset); + + if (!location || lttng_kernel_function_location_get_type(location) != + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_INVALID; + goto end; + } + + symbol_location = container_of(location, + struct lttng_kernel_function_location_symbol, parent); + *offset = symbol_location->offset; +end: + return ret; +} + +static +int lttng_kernel_function_location_symbol_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t symbol_name_len; + size_t original_payload_size; + struct lttng_kernel_function_location_symbol *location_symbol; + struct lttng_kernel_function_location_symbol_comm location_symbol_comm; + + if (!location || !payload) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = -LTTNG_ERR_INVALID; + goto end; + } + + assert(lttng_kernel_function_location_get_type(location) == + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET); + + original_payload_size = payload->buffer.size; + location_symbol = container_of(location, + struct lttng_kernel_function_location_symbol, parent); + + if (!location_symbol->symbol_name) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + symbol_name_len = strlen(location_symbol->symbol_name); + if (symbol_name_len == 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_symbol_comm.symbol_len = symbol_name_len + 1; + location_symbol_comm.offset = location_symbol->offset; + + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_symbol_comm, sizeof(location_symbol_comm)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + location_symbol->symbol_name, + location_symbol_comm.symbol_len); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (int) (payload->buffer.size - original_payload_size); +end: + return ret; +} + +static +int lttng_kernel_function_location_address_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t original_payload_size; + struct lttng_kernel_function_location_address *location_address; + struct lttng_kernel_function_location_address_comm location_address_comm; + + assert(location); + assert(lttng_kernel_function_location_get_type(location) == + LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS); + + original_payload_size = payload->buffer.size; + location_address = container_of(location, + struct lttng_kernel_function_location_address, + parent); + + location_address_comm.address = location_address->address; + + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_address_comm, + sizeof(location_address_comm)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (int) (payload->buffer.size - original_payload_size); +end: + return ret; +} + +LTTNG_HIDDEN +int lttng_kernel_function_location_serialize( + const struct lttng_kernel_function_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t original_payload_size; + struct lttng_kernel_function_location_comm location_generic_comm = {}; + + if (!location || !payload) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = -LTTNG_ERR_INVALID; + goto end; + } + + original_payload_size = payload->buffer.size; + location_generic_comm.type = (int8_t) location->type; + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_generic_comm, + sizeof(location_generic_comm)); + if (ret) { + goto end; + } + + ret = location->serialize(location, payload); + if (ret < 0) { + goto end; + } + + ret = (int) (payload->buffer.size - original_payload_size); +end: + return ret; +} + +static +int lttng_kernel_function_location_symbol_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **location) +{ + struct lttng_kernel_function_location_symbol_comm *location_symbol_comm; + const char *symbol_name_src; + ssize_t ret = 0; + size_t expected_size; + + assert(location); + + if (view->buffer.size < sizeof(*location_symbol_comm)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_symbol_comm = + (typeof(location_symbol_comm)) view->buffer.data; + + expected_size = sizeof(*location_symbol_comm) + + location_symbol_comm->symbol_len; + + if (view->buffer.size < expected_size) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + symbol_name_src = view->buffer.data + sizeof(*location_symbol_comm); + + if (!lttng_buffer_view_contains_string(&view->buffer, symbol_name_src, + location_symbol_comm->symbol_len)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + *location = lttng_kernel_function_location_symbol_create( + symbol_name_src, location_symbol_comm->offset); + if (!(*location)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (ssize_t) expected_size; +end: + return ret; +} + +static +ssize_t lttng_kernel_function_location_address_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **location) +{ + struct lttng_kernel_function_location_address_comm *location_address_comm; + ssize_t ret = 0; + size_t expected_size; + + assert(location); + + expected_size = sizeof(*location_address_comm); + + if (view->buffer.size < expected_size) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_address_comm = + (typeof(location_address_comm)) view->buffer.data; + + *location = lttng_kernel_function_location_address_create(location_address_comm->address); + if (!(*location)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (size_t) expected_size; +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_kernel_function_location_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_function_location **location) +{ + enum lttng_kernel_function_location_type type; + ssize_t consumed = 0; + ssize_t ret; + const struct lttng_kernel_function_location_comm *function_location_comm; + const struct lttng_payload_view function_location_comm_view = + lttng_payload_view_from_view( + view, 0, sizeof(*function_location_comm)); + + assert(view); + assert(location); + + if (!lttng_payload_view_is_valid(&function_location_comm_view)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + function_location_comm = (typeof(function_location_comm)) function_location_comm_view.buffer.data; + type = (enum lttng_kernel_function_location_type) function_location_comm->type; + consumed += sizeof(*function_location_comm); + + switch (type) { + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view( + view, consumed, -1); + + ret = lttng_kernel_function_location_symbol_create_from_payload( + &location_view, location); + break; + } + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view(view, consumed, -1); + + ret = lttng_kernel_function_location_address_create_from_payload( + &location_view, location); + break; + } + default: + ret = -LTTNG_ERR_INVALID; + break; + } + + if (ret < 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret += consumed; + +end: + return ret; +} + +static +unsigned long lttng_kernel_function_location_address_hash( + const struct lttng_kernel_function_location *location) +{ + unsigned long hash = hash_key_ulong( + (void *) LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS, + lttng_ht_seed); + struct lttng_kernel_function_location_address *address_location = + container_of(location, typeof(*address_location), + parent); + + hash ^= hash_key_u64(&address_location->address, lttng_ht_seed); + + return hash; +} + +static +bool lttng_kernel_function_location_address_is_equal( + const struct lttng_kernel_function_location *_a, + const struct lttng_kernel_function_location *_b) +{ + bool is_equal = false; + struct lttng_kernel_function_location_address *a, *b; + + a = container_of(_a, struct lttng_kernel_function_location_address, + parent); + b = container_of(_b, struct lttng_kernel_function_location_address, + parent); + + if (a->address != b->address) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +static +unsigned long lttng_kernel_function_location_symbol_hash( + const struct lttng_kernel_function_location *location) +{ + unsigned long hash = hash_key_ulong( + (void *) LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET, + lttng_ht_seed); + struct lttng_kernel_function_location_symbol *symbol_location = + container_of(location, typeof(*symbol_location), + parent); + + hash ^= hash_key_str(symbol_location->symbol_name, lttng_ht_seed); + hash ^= hash_key_u64(&symbol_location->offset, lttng_ht_seed); + + return hash; +} + +static +bool lttng_kernel_function_location_symbol_is_equal( + const struct lttng_kernel_function_location *_a, + const struct lttng_kernel_function_location *_b) +{ + bool is_equal = false; + struct lttng_kernel_function_location_symbol *a, *b; + + a = container_of(_a, struct lttng_kernel_function_location_symbol, + parent); + b = container_of(_b, struct lttng_kernel_function_location_symbol, + parent); + + assert(a->symbol_name); + assert(b->symbol_name); + if (strcmp(a->symbol_name, b->symbol_name)) { + goto end; + } + + if (a->offset != b->offset) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +LTTNG_HIDDEN +bool lttng_kernel_function_location_is_equal( + const struct lttng_kernel_function_location *a, + const struct lttng_kernel_function_location *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + if (a->type != b->type) { + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +static struct lttng_kernel_function_location * +lttng_kernel_function_location_symbol_copy( + const struct lttng_kernel_function_location *location) +{ + struct lttng_kernel_function_location *new_location = NULL; + struct lttng_kernel_function_location_symbol *symbol_location; + enum lttng_kernel_function_location_status status; + const char *symbol_name = NULL; + uint64_t offset; + + assert(location); + assert(location->type == LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET); + symbol_location = container_of( + location, typeof(*symbol_location), parent); + + /* Get function location offset */ + status = lttng_kernel_function_location_symbol_get_offset(location, &offset); + if (status != LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK) { + ERR("Get kernel function location offset failed."); + goto error; + } + + symbol_name = lttng_kernel_function_location_symbol_get_name(location); + if (!symbol_name) { + ERR("Kernel function symbol name is NULL."); + goto error; + } + + /* Create the function_location */ + new_location = lttng_kernel_function_location_symbol_create( + symbol_name, offset); + + goto end; + +error: + new_location = NULL; +end: + return new_location; +} +static struct lttng_kernel_function_location * +lttng_kernel_function_location_address_copy( + const struct lttng_kernel_function_location *location) +{ + struct lttng_kernel_function_location *new_location = NULL; + struct lttng_kernel_function_location_address *address_location; + enum lttng_kernel_function_location_status status; + uint64_t address; + + assert(location); + assert(location->type == LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS); + address_location = container_of( + location, typeof(*address_location), parent); + + + /* Get function location fields */ + status = lttng_kernel_function_location_address_get_address(location, &address); + if (status != LTTNG_KERNEL_FUNCTION_LOCATION_STATUS_OK) { + ERR("Get kernel function address failed."); + goto error; + } + + /* Create the function_location */ + new_location = lttng_kernel_function_location_address_create(address); + + goto end; + +error: + new_location = NULL; +end: + return new_location; +} + +LTTNG_HIDDEN +struct lttng_kernel_function_location *lttng_kernel_function_location_copy( + const struct lttng_kernel_function_location *location) +{ + struct lttng_kernel_function_location *new_location = NULL; + enum lttng_kernel_function_location_type type; + + if (!location) { + goto err; + } + + type = lttng_kernel_function_location_get_type(location); + switch (type) { + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_ADDRESS: + new_location = + lttng_kernel_function_location_address_copy(location); + if (!new_location) { + goto err; + } + break; + case LTTNG_KERNEL_FUNCTION_LOCATION_TYPE_SYMBOL_OFFSET: + new_location = + lttng_kernel_function_location_symbol_copy(location); + if (!new_location) { + goto err; + } + break; + default: + new_location = NULL; + goto err; + } +err: + return new_location; +} + +LTTNG_HIDDEN +unsigned long lttng_kernel_function_location_hash( + const struct lttng_kernel_function_location *location) +{ + return location->hash(location); +} diff --git a/src/common/log-level-rule.c b/src/common/log-level-rule.c new file mode 100644 index 000000000..b5d0e9dcb --- /dev/null +++ b/src/common/log-level-rule.c @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool is_log_level_rule_exactly_type(const struct lttng_log_level_rule *rule) +{ + enum lttng_log_level_rule_type type = + lttng_log_level_rule_get_type(rule); + + return type == LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY; +} + +static bool is_log_level_rule_at_least_as_severe_type(const struct lttng_log_level_rule *rule) +{ + + enum lttng_log_level_rule_type type = + lttng_log_level_rule_get_type(rule); + + return type == LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS; +} + +enum lttng_log_level_rule_type lttng_log_level_rule_get_type( + const struct lttng_log_level_rule *rule) +{ + return rule ? rule->type : LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN; +} + +struct lttng_log_level_rule *lttng_log_level_rule_exactly_create( + int level) +{ + struct lttng_log_level_rule *rule = NULL; + + rule = zmalloc(sizeof(struct lttng_log_level_rule)); + if (!rule) { + goto end; + } + + rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY; + rule->level = level; + +end: + return rule; +} + +enum lttng_log_level_rule_status lttng_log_level_rule_exactly_get_level( + const struct lttng_log_level_rule *rule, int *level) +{ + enum lttng_log_level_rule_status status = LTTNG_LOG_LEVEL_RULE_STATUS_OK; + if (!rule || !level || !is_log_level_rule_exactly_type(rule)) { + status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID; + goto end; + } + + *level = rule->level; +end: + return status; +} + +struct lttng_log_level_rule * +lttng_log_level_rule_at_least_as_severe_as_create(int level) +{ + struct lttng_log_level_rule *rule = NULL; + + rule = zmalloc(sizeof(struct lttng_log_level_rule)); + if (!rule) { + goto end; + } + + rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS; + rule->level = level; + +end: + return rule; +} + +enum lttng_log_level_rule_status +lttng_log_level_rule_at_least_as_severe_as_get_level( + const struct lttng_log_level_rule *rule, int *level) +{ + enum lttng_log_level_rule_status status = LTTNG_LOG_LEVEL_RULE_STATUS_OK; + if (!rule || !level || !is_log_level_rule_at_least_as_severe_type(rule)) { + status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID; + goto end; + } + + *level = rule->level; +end: + return status; +} + +void lttng_log_level_rule_destroy(struct lttng_log_level_rule *log_level_rule) +{ + free(log_level_rule); +} + +LTTNG_HIDDEN +ssize_t lttng_log_level_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_log_level_rule **_rule) +{ + ssize_t ret; + size_t offset = 0; + struct lttng_log_level_rule *rule = NULL; + const struct lttng_log_level_rule_comm *comm = + (const struct lttng_log_level_rule_comm *) + view->buffer.data; + + offset += sizeof(*comm); + + if (!_rule) { + ret = -1; + goto end; + } + + if (view->buffer.size < sizeof(*comm)) { + ret = -1; + goto end; + } + + switch (comm->type) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + rule = lttng_log_level_rule_exactly_create((int) comm->level); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + rule = lttng_log_level_rule_at_least_as_severe_as_create( + (int) comm->level); + break; + default: + abort(); + } + + if (!rule) { + ret = -1; + goto end; + } + + *_rule = rule; + ret = offset; + +end: + return ret; +} + +LTTNG_HIDDEN +int lttng_log_level_rule_serialize(const struct lttng_log_level_rule *rule, + struct lttng_payload *payload) +{ + int ret; + struct lttng_log_level_rule_comm comm; + + + if (!rule) { + ret = 0; + goto end; + } + + comm.type = (int8_t) rule->type; + comm.level = (int32_t) rule->level; + + DBG("Serializing log level rule of type %d", rule->type); + ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, + sizeof(comm)); + if (ret) { + goto end; + } + +end: + return ret; +} + +LTTNG_HIDDEN +bool lttng_log_level_rule_is_equal(const struct lttng_log_level_rule *a, + const struct lttng_log_level_rule *b) +{ + bool is_equal = false; + + if (a == NULL && b == NULL) { + /* Both are null. */ + is_equal = true; + goto end; + } + + if (a == NULL || b == NULL) { + /* One is NULL.*/ + goto end; + } + + if (a == b) { + /* Same object.*/ + is_equal = true; + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a->level != b->level) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +LTTNG_HIDDEN +struct lttng_log_level_rule * lttng_log_level_rule_copy(const struct lttng_log_level_rule *source) +{ + struct lttng_log_level_rule *copy = NULL; + + assert(source); + + copy = zmalloc(sizeof(struct lttng_log_level_rule)); + if (!copy) { + goto end; + } + + copy->type = source->type; + copy->level = source->level; +end: + return copy; +} + +LTTNG_HIDDEN +void lttng_log_level_rule_to_loglevel( + const struct lttng_log_level_rule *log_level_rule, + enum lttng_loglevel_type *loglevel_type, + int *loglevel_value) +{ + assert(log_level_rule); + + switch(log_level_rule->type) + { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + *loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + *loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; + break; + default: + abort(); + } + + *loglevel_value = log_level_rule->level; +} + +LTTNG_HIDDEN +unsigned long lttng_log_level_rule_hash( + const struct lttng_log_level_rule *log_level_rule) +{ + unsigned long hash; + enum lttng_log_level_rule_status llr_status; + int log_level_value; + enum lttng_log_level_rule_type type; + + assert(log_level_rule); + + type = lttng_log_level_rule_get_type(log_level_rule); + + switch (type) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &log_level_value); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &log_level_value); + break; + default: + abort(); + break; + } + + assert(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + + hash = hash_key_ulong((void *) (unsigned long) type, lttng_ht_seed); + + hash ^= hash_key_ulong((void *) (unsigned long) log_level_value, + lttng_ht_seed); + + return hash; +} diff --git a/src/common/lttng-kernel.h b/src/common/lttng-kernel.h index cd343bb76..343d68dab 100644 --- a/src/common/lttng-kernel.h +++ b/src/common/lttng-kernel.h @@ -178,18 +178,140 @@ struct lttng_kernel_event { } u; } LTTNG_PACKED; -#define LTTNG_KERNEL_EVENT_NOTIFIER_PADDING 40 +#define LTTNG_KERNEL_EVENT_NOTIFIER_PADDING 32 struct lttng_kernel_event_notifier { struct lttng_kernel_event event; + uint64_t error_counter_idx; + char padding[LTTNG_KERNEL_EVENT_NOTIFIER_PADDING]; } LTTNG_PACKED; -#define LTTNG_KERNEL_EVENT_NOTIFIER_NOTIFICATION_PADDING 34 +enum lttng_kernel_key_token_type { + LTTNG_KERNEL_KEY_TOKEN_STRING = 0, /* arg: strtab_offset. */ + LTTNG_KERNEL_KEY_TOKEN_EVENT_NAME = 1, /* no arg. */ +}; + +#define LTTNG_KERNEL_KEY_ARG_PADDING1 60 +#define LTTNG_KERNEL_KEY_TOKEN_STRING_LEN_MAX 256 +struct lttng_kernel_key_token { + uint32_t type; /* enum lttng_kernel_key_token_type */ + union { + uint64_t string_ptr; + char padding[LTTNG_KERNEL_KEY_ARG_PADDING1]; + } arg; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_NR_KEY_TOKEN 4 +struct lttng_kernel_counter_key_dimension { + uint32_t nr_key_tokens; + struct lttng_kernel_key_token key_tokens[LTTNG_KERNEL_NR_KEY_TOKEN]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_DIMENSION_MAX 4 +struct lttng_kernel_counter_key { + uint32_t nr_dimensions; + struct lttng_kernel_counter_key_dimension key_dimensions[LTTNG_KERNEL_COUNTER_DIMENSION_MAX]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_EVENT_PADDING1 16 +struct lttng_kernel_counter_event { + struct lttng_kernel_event event; + struct lttng_kernel_counter_key key; + char padding[LTTNG_KERNEL_COUNTER_EVENT_PADDING1]; +} LTTNG_PACKED; + +enum lttng_kernel_counter_arithmetic { + LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR = 0, +}; + +enum lttng_kernel_counter_bitness { + LTTNG_KERNEL_COUNTER_BITNESS_32 = 0, + LTTNG_KERNEL_COUNTER_BITNESS_64 = 1, +}; + +struct lttng_kernel_counter_dimension { + uint64_t size; + uint64_t underflow_index; + uint64_t overflow_index; + uint8_t has_underflow; + uint8_t has_overflow; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_CONF_PADDING1 67 +struct lttng_kernel_counter_conf { + uint32_t arithmetic; /* enum lttng_kernel_counter_arithmetic */ + uint32_t bitness; /* enum lttng_kernel_counter_bitness */ + uint32_t number_dimensions; + int64_t global_sum_step; + struct lttng_kernel_counter_dimension dimensions[LTTNG_KERNEL_COUNTER_DIMENSION_MAX]; + uint8_t coalesce_hits; + char padding[LTTNG_KERNEL_COUNTER_CONF_PADDING1]; +} LTTNG_PACKED; + +struct lttng_kernel_counter_index { + uint32_t number_dimensions; + uint64_t dimension_indexes[LTTNG_KERNEL_COUNTER_DIMENSION_MAX]; +} LTTNG_PACKED; + +struct lttng_kernel_counter_value { + int64_t value; + uint8_t underflow; + uint8_t overflow; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_READ_PADDING 32 +struct lttng_kernel_counter_read { + struct lttng_kernel_counter_index index; + int32_t cpu; /* -1 for global counter, >= 0 for specific cpu. */ + struct lttng_kernel_counter_value value; /* output */ + char padding[LTTNG_KERNEL_COUNTER_READ_PADDING]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_AGGREGATE_PADDING 32 +struct lttng_kernel_counter_aggregate { + struct lttng_kernel_counter_index index; + struct lttng_kernel_counter_value value; /* output */ + char padding[LTTNG_KERNEL_COUNTER_AGGREGATE_PADDING]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_CLEAR_PADDING 32 +struct lttng_kernel_counter_clear { + struct lttng_kernel_counter_index index; + char padding[LTTNG_KERNEL_COUNTER_CLEAR_PADDING]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_COUNTER_MAP_NR_DESCRIPTORS_PADDING 32 +struct lttng_kernel_counter_map_nr_descriptors { + uint64_t nr_descriptors; +}; + +#define LTTNG_KERNEL_COUNTER_KEY_LEN 256 +#define LTTNG_KERNEL_COUNTER_MAP_DESCRIPTOR_PADDING 32 +struct lttng_kernel_counter_map_descriptor { + uint64_t descriptor_index; /* input. [ 0 .. nr_descriptors - 1 ] */ + uint32_t dimension; /* outputs */ + uint64_t array_index; + uint64_t user_token; + char key[LTTNG_KERNEL_COUNTER_KEY_LEN]; + + char padding[LTTNG_KERNEL_COUNTER_MAP_DESCRIPTOR_PADDING]; +} LTTNG_PACKED; + +#define LTTNG_KERNEL_EVENT_NOTIFIER_NOTIFICATION_PADDING 32 struct lttng_kernel_event_notifier_notification { uint64_t token; + uint16_t capture_buf_size; char padding[LTTNG_KERNEL_EVENT_NOTIFIER_NOTIFICATION_PADDING]; } LTTNG_PACKED; +#define LTTNG_KERNEL_CAPTURE_BYTECODE_MAX_LEN 65536 +struct lttng_kernel_capture_bytecode { + uint32_t len; + uint32_t reloc_offset; + uint64_t seqnum; + char data[0]; +} LTTNG_PACKED; + struct lttng_kernel_tracer_version { uint32_t major; uint32_t minor; diff --git a/src/common/map-key/map-key.c b/src/common/map-key/map-key.c new file mode 100644 index 000000000..1cce2b121 --- /dev/null +++ b/src/common/map-key/map-key.c @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define TOKEN_VAR_EVENT_NAME "EVENT_NAME" +#define TOKEN_VAR_PROVIDER_NAME "PROVIDER_NAME" + +static +void destroy_map_key_token(void *ptr) +{ + struct lttng_map_key_token *token = (struct lttng_map_key_token *) ptr; + switch (token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + struct lttng_map_key_token_string *token_string = + (struct lttng_map_key_token_string *) token; + free(token_string->string); + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + break; + default: + abort(); + } + + free(token); +} + +struct lttng_map_key *lttng_map_key_create(void) +{ + struct lttng_map_key *key = NULL; + + key = zmalloc(sizeof(*key)); + if (!key) { + return NULL; + } + + urcu_ref_init(&key->ref); + lttng_dynamic_pointer_array_init(&key->tokens, + destroy_map_key_token); + + return key; +} + +ssize_t lttng_map_key_create_from_payload(struct lttng_payload_view *src_view, + struct lttng_map_key **out_key) +{ + ssize_t ret, consumed_len; + uint32_t i; + const struct lttng_map_key_comm *comm; + struct lttng_map_key *key; + + if (!src_view || !out_key) { + ret = -1; + goto end; + } + + key = lttng_map_key_create(); + if (!key) { + ret = -1; + goto end; + } + + comm = (typeof(comm)) src_view->buffer.data; + consumed_len = sizeof(*comm); + + assert(comm->token_count > 0); + + for (i = 0; i < comm->token_count; i++) { + enum lttng_map_key_status key_status; + const struct lttng_map_key_token_comm *token_comm; + struct lttng_payload_view child_view = + lttng_payload_view_from_view(src_view, consumed_len, + src_view->buffer.size - consumed_len); + if (!lttng_payload_view_is_valid(&child_view)) { + ret = -1; + goto end; + } + + token_comm = (const struct lttng_map_key_token_comm *) child_view.buffer.data; + + switch (token_comm->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + const char *str_val; + const struct lttng_map_key_token_string_comm *comm; + + comm = (typeof(comm)) token_comm; + str_val = (const char *) &comm->payload; + + if (!lttng_buffer_view_contains_string(&child_view.buffer, + str_val, comm->string_len)) { + ret = -1; + goto end; + } + + key_status = lttng_map_key_append_token_string(key, str_val); + if (key_status != LTTNG_MAP_KEY_STATUS_OK) { + ret = -1; + goto end; + } + + consumed_len += sizeof(*comm) + comm->string_len; + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + const struct lttng_map_key_token_variable_comm *comm; + + comm = (typeof(comm)) token_comm; + key_status = lttng_map_key_append_token_variable(key, + comm->var_type); + if (key_status != LTTNG_MAP_KEY_STATUS_OK) { + ret = -1; + goto end; + } + + consumed_len += sizeof(*comm); + break; + } + default: + abort(); + } + } + + *out_key = key; + ret = consumed_len; +end: + return ret; +} + +int lttng_map_key_serialize(const struct lttng_map_key *key, + struct lttng_payload *payload) +{ + int ret; + uint32_t i, nb_tokens; + enum lttng_map_key_status key_status; + struct lttng_map_key_comm comm = {0}; + + DBG("Serializing map key"); + + key_status = lttng_map_key_get_token_count(key, &nb_tokens); + if (key_status != LTTNG_MAP_KEY_STATUS_OK) { + ret = -1; + goto end; + } + + if (nb_tokens == 0) { + ERR("Map key token number is zero"); + ret = -1; + goto end; + } + + DBG("Serializing map key token count: %" PRIu32, nb_tokens); + comm.token_count = nb_tokens; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, + sizeof(comm)); + if (ret) { + goto end; + } + + for (i = 0; i < nb_tokens; i++) { + uint8_t token_type; + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + DBG("Serializing map key token's type: %d", token->type); + + token_type = (uint8_t) token->type; + + switch (token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + struct lttng_map_key_token_string *str_token = + (struct lttng_map_key_token_string *) token; + struct lttng_map_key_token_string_comm comm = {0}; + uint32_t len = strlen(str_token->string) + 1; + + DBG("Serializing a string type key token"); + comm.parent_type = token_type; + comm.string_len = len; + + /* Serialize the length, include the null character */ + DBG("Serializing map key token string length (include null character):" + "%" PRIu32, len); + ret = lttng_dynamic_buffer_append(&payload->buffer, + &comm, sizeof(comm)); + if (ret) { + goto end; + } + + /* Serialize the string */ + DBG("Serializing map key token string's value: \"%s\"", + str_token->string); + ret = lttng_dynamic_buffer_append(&payload->buffer, + str_token->string, len); + if (ret) { + goto end; + } + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + struct lttng_map_key_token_variable *var_token = + (struct lttng_map_key_token_variable *) token; + struct lttng_map_key_token_variable_comm comm = {0}; + + DBG("Serializing a variable type key token"); + + comm.parent_type = token_type; + comm.var_type = var_token->type; + + ret = lttng_dynamic_buffer_append(&payload->buffer, + &comm, sizeof(comm)); + if (ret) { + goto end; + } + + break; + } + default: + abort(); + } + } + +end: + return ret; +} + +static inline +bool token_variable_type_is_valid(enum lttng_map_key_token_variable_type var_type) +{ + switch (var_type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + return true; + default: + return false; + } +} + +static bool lttng_map_key_token_variable_is_equal( + const struct lttng_map_key_token *_a, + const struct lttng_map_key_token *_b) +{ + struct lttng_map_key_token_variable *a, *b; + assert(_a->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE); + assert(_b->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE); + + a = container_of(_a, struct lttng_map_key_token_variable, parent); + b = container_of(_b, struct lttng_map_key_token_variable, parent); + + return lttng_map_key_token_variable_get_type(a) == + lttng_map_key_token_variable_get_type(b); +} + +enum lttng_map_key_status lttng_map_key_append_token_variable( + struct lttng_map_key *key, + enum lttng_map_key_token_variable_type var_type) +{ + int ret; + enum lttng_map_key_status status; + struct lttng_map_key_token_variable *token = NULL; + + if (!token_variable_type_is_valid(var_type)) { + ERR("Invalid token variable type"); + status = LTTNG_MAP_KEY_STATUS_INVALID; + goto end; + } + + token = zmalloc(sizeof(*token)); + if (!token) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + token->parent.type = LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE; + token->parent.equal = lttng_map_key_token_variable_is_equal; + token->type = var_type; + + ret = lttng_dynamic_pointer_array_add_pointer( + &key->tokens, token); + if (ret) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_KEY_STATUS_OK; + +end: + return status; +} + +static bool lttng_map_key_token_string_is_equal( + const struct lttng_map_key_token *_a, + const struct lttng_map_key_token *_b) +{ + struct lttng_map_key_token_string *a, *b; + const char *a_string, *b_string; + + assert(_a->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING); + assert(_b->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING); + + a = container_of(_a, struct lttng_map_key_token_string, parent); + b = container_of(_b, struct lttng_map_key_token_string, parent); + + a_string = lttng_map_key_token_string_get_string(a); + b_string = lttng_map_key_token_string_get_string(b); + + return !strcmp(a_string, b_string); +} + +enum lttng_map_key_status lttng_map_key_append_token_string( + struct lttng_map_key *key, const char *string) +{ + int ret; + enum lttng_map_key_status status; + struct lttng_map_key_token_string *token = NULL; + + + token = zmalloc(sizeof(*token)); + if (!token) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + token->parent.type = LTTNG_MAP_KEY_TOKEN_TYPE_STRING; + token->parent.equal = lttng_map_key_token_string_is_equal; + token->string = strdup(string); + if (!token->string) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &key->tokens, token); + if (ret) { + status = LTTNG_MAP_KEY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_KEY_STATUS_OK; +end: + return status; +} + +enum lttng_map_key_status lttng_map_key_get_token_count( + const struct lttng_map_key *key, unsigned int *count) +{ + enum lttng_map_key_status status; + if (!key || !count) { + status = LTTNG_MAP_KEY_STATUS_INVALID; + goto end; + + } + + *count = lttng_dynamic_pointer_array_get_count( + &key->tokens); + + status = LTTNG_MAP_KEY_STATUS_OK; +end: + return status; +} + +const struct lttng_map_key_token * +lttng_map_key_get_token_at_index(const struct lttng_map_key *key, + unsigned int index) +{ + const struct lttng_map_key_token *token = NULL; + enum lttng_map_key_status status; + unsigned int count; + + if (!key) { + goto end; + } + + status = lttng_map_key_get_token_count(key, &count); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + goto end; + } + + if (index >= count) { + goto end; + } + + token = lttng_dynamic_pointer_array_get_pointer(&key->tokens, + index); + +end: + return token; +} + +enum lttng_map_key_token_variable_type lttng_map_key_token_variable_get_type( + const struct lttng_map_key_token_variable *token) +{ + assert(token->parent.type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE); + + return token->type; +} + +const char *lttng_map_key_token_string_get_string( + const struct lttng_map_key_token_string *token) +{ + assert(token->parent.type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING); + + return token->string; +} + +void lttng_map_key_destroy(struct lttng_map_key *key) +{ + lttng_map_key_put(key); +} + +LTTNG_HIDDEN +struct lttng_map_key *lttng_map_key_parse_from_string(const char *_key_str) +{ + struct lttng_map_key *key = NULL; + enum lttng_map_key_status status; + char *key_str = NULL, *curr_pos; + + key = lttng_map_key_create(); + if (!key) { + goto end; + } + + key_str = strdup(_key_str); + + curr_pos = key_str; + + curr_pos = strtok(curr_pos, "$}"); + while (curr_pos) { + if (curr_pos[0] == '{') { + if (strncmp(&curr_pos[1], TOKEN_VAR_EVENT_NAME, strlen(TOKEN_VAR_EVENT_NAME)) == 0) { + status = lttng_map_key_append_token_variable(key, + LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + goto error; + } + } else if (strncmp(&curr_pos[1], TOKEN_VAR_PROVIDER_NAME, strlen(TOKEN_VAR_PROVIDER_NAME)) == 0) { + status = lttng_map_key_append_token_variable(key, + LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + goto error; + } + } else { + goto error; + } + } else { + status = lttng_map_key_append_token_string(key, curr_pos); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + goto error; + } + } + curr_pos = strtok(NULL, "$}"); + } + goto end; + +error: + lttng_map_key_destroy(key); + key = NULL; +end: + free(key_str); + + return key; +} + +LTTNG_HIDDEN +bool lttng_map_key_is_equal( + const struct lttng_map_key *a, const struct lttng_map_key *b) +{ + bool is_equal = false; + enum lttng_map_key_status status; + unsigned int a_count, b_count, i; + + if (!!a != !!b) { + goto end; + } + + if (a == NULL && b == NULL) { + is_equal = true; + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + status = lttng_map_key_get_token_count(a, &a_count); + assert(status == LTTNG_MAP_KEY_STATUS_OK); + status = lttng_map_key_get_token_count(b, &b_count); + assert(status == LTTNG_MAP_KEY_STATUS_OK); + + if (a_count != b_count) { + goto end; + } + + for (i = 0; i < a_count; i++) { + const struct lttng_map_key_token *token_a = NULL, *token_b = NULL; + + token_a = lttng_map_key_get_token_at_index(a, i); + token_b = lttng_map_key_get_token_at_index(b, i); + + /* JORAJ TODO: is order important for the map key token? */ + if(token_a->type != token_b->type) { + goto end; + } + + if(!token_a->equal(token_a, token_b)) { + goto end; + } + } + + is_equal = true; + +end: + return is_equal; + + +} + +static void map_key_destroy_ref(struct urcu_ref *ref) +{ + struct lttng_map_key *key = container_of(ref, struct lttng_map_key, ref); + + lttng_dynamic_pointer_array_reset(&key->tokens); + free(key); +} + +LTTNG_HIDDEN +void lttng_map_key_get(struct lttng_map_key *key) +{ + urcu_ref_get(&key->ref); +} + +LTTNG_HIDDEN +void lttng_map_key_put(struct lttng_map_key *key) +{ + if (!key) { + return; + } + + urcu_ref_put(&key->ref, map_key_destroy_ref); +} diff --git a/src/common/map-query.c b/src/common/map-query.c new file mode 100644 index 000000000..d89d6f38b --- /dev/null +++ b/src/common/map-query.c @@ -0,0 +1,676 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +struct lttng_map_query *lttng_map_query_create( + enum lttng_map_query_config_cpu cpu, + enum lttng_map_query_config_buffer buffer, + enum lttng_map_query_config_app_bitness bitness) +{ + struct lttng_map_query *query = NULL; + + if ((buffer == LTTNG_MAP_QUERY_CONFIG_BUFFER_KERNEL_GLOBAL) ^ + (bitness == LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_KERNEL)) { + /* + * If any of the buffer or bitness config is set to kernel, + * they other has to as well. + */ + goto end; + } + + if (bitness != LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL && + bitness != LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_KERNEL) { + /* We currently don't support targetting a specific bitness. */ + goto end; + } + + query = zmalloc(sizeof(struct lttng_map_query)); + if (!query) { + goto end; + } + + query->config_cpu = cpu; + query->config_buffer = buffer; + query->config_bitness = bitness; + + query->sum_by_uid = false; + query->sum_by_pid = false; + query->sum_by_cpu = false; + // defaults to true for now. + query->sum_by_app_bitness = true; + + if (query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + lttng_dynamic_array_init(&query->cpu_array, sizeof(int), NULL); + } + + switch(query->config_buffer) { + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + lttng_dynamic_array_init(&query->uid_array, sizeof(uid_t), NULL); + break; + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + lttng_dynamic_array_init(&query->pid_array, sizeof(pid_t), NULL); + break; + default: + break; + } +end: + return query; +} + +enum lttng_map_query_status lttng_map_query_set_sum_by_cpu( + struct lttng_map_query *query, bool sum_by_cpu) +{ + query->sum_by_cpu = sum_by_cpu; + + return LTTNG_MAP_QUERY_STATUS_OK; +} + +enum lttng_map_query_status lttng_map_query_set_sum_by_app_bitness( + struct lttng_map_query *query, bool sum_by_app_bitness) +{ + enum lttng_map_query_status status; + + if (query->config_bitness != LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + query->sum_by_app_bitness = sum_by_app_bitness; + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_set_sum_by_pid( + struct lttng_map_query *query, bool sum_by_pid) +{ + enum lttng_map_query_status status; + + switch (query->config_buffer) { + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_ALL: + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + break; + default: + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + query->sum_by_pid = sum_by_pid; + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_set_sum_by_uid( + struct lttng_map_query *query, bool sum_by_uid) +{ + enum lttng_map_query_status status; + + switch (query->config_buffer) { + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL: + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + break; + default: + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + query->sum_by_uid = sum_by_uid; + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_add_cpu( + struct lttng_map_query *query, int cpu_id) +{ + enum lttng_map_query_status status; + unsigned int cpu_count; + int ret; + + if (query->config_cpu != LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + lttng_map_query_get_cpu_count(query, &cpu_count); + if (cpu_count > 0) { + ERR("Only one CPU can be targeted in a query at the moment"); + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + ret = lttng_dynamic_array_add_element(&query->cpu_array, &cpu_id); + if (ret) { + status = LTTNG_MAP_QUERY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_add_uid( + struct lttng_map_query *query, uid_t uid) +{ + int ret; + unsigned int uid_count; + enum lttng_map_query_status status; + + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + lttng_map_query_get_uid_count(query, &uid_count); + if (uid_count > 0) { + ERR("Only one UID can be targeted in a query at the moment"); + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + ret = lttng_dynamic_array_add_element(&query->uid_array, &uid); + if (ret) { + status = LTTNG_MAP_QUERY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_add_pid( + struct lttng_map_query *query, pid_t pid) +{ + int ret; + unsigned int pid_count; + enum lttng_map_query_status status; + + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + lttng_map_query_get_pid_count(query, &pid_count); + if (pid_count > 0) { + ERR("Only one PID can be targeted in a query at the moment"); + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + ret = lttng_dynamic_array_add_element(&query->pid_array, &pid); + if (ret) { + status = LTTNG_MAP_QUERY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_add_key_filter( + struct lttng_map_query *query, const char *key_filter) +{ + enum lttng_map_query_status status; + + query->key_filter = strdup(key_filter); + if (!query->key_filter) { + status = LTTNG_MAP_QUERY_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_config_cpu lttng_map_query_get_config_cpu( + const struct lttng_map_query *query) +{ + return query->config_cpu; +} + +enum lttng_map_query_config_buffer lttng_map_query_get_config_buffer( + const struct lttng_map_query *query) +{ + return query->config_buffer; +} + +enum lttng_map_query_config_app_bitness lttng_map_query_get_config_app_bitness( + const struct lttng_map_query *query) +{ + return query->config_bitness; +} + +bool lttng_map_query_get_config_sum_by_cpu( + const struct lttng_map_query *query) +{ + return query->sum_by_cpu; +} + +bool lttng_map_query_get_config_sum_by_pid( + const struct lttng_map_query *query) +{ + return query->sum_by_pid; +} + +bool lttng_map_query_get_config_sum_by_uid( + const struct lttng_map_query *query) +{ + return query->sum_by_uid; +} + +bool lttng_map_query_get_config_sum_by_app_bitness( + const struct lttng_map_query *query) +{ + return query->sum_by_app_bitness; +} + +enum lttng_map_query_status lttng_map_query_get_cpu_count( + const struct lttng_map_query *query, unsigned int *count) +{ + enum lttng_map_query_status status; + + assert(query); + if (query->config_cpu != LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + if (!count) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_array_get_count(&query->cpu_array); + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_cpu_at_index( + const struct lttng_map_query *query, unsigned int index, + int *cpu) +{ + enum lttng_map_query_status status; + + assert(query); + + if (query->config_cpu != LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + + *cpu = *(int *) lttng_dynamic_array_get_element(&query->cpu_array, index); + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_uid_count( + const struct lttng_map_query *query, unsigned int *count) +{ + enum lttng_map_query_status status; + + assert(query); + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + if (!count) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_array_get_count(&query->uid_array); + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_uid_at_index( + const struct lttng_map_query *query, unsigned int index, + uid_t *uid) +{ + enum lttng_map_query_status status; + + assert(query); + + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + + *uid = *(uid_t *) lttng_dynamic_array_get_element(&query->uid_array, index); + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_pid_count( + const struct lttng_map_query *query, unsigned int *count) +{ + enum lttng_map_query_status status; + + assert(query); + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + if (!count) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_array_get_count(&query->pid_array); + status = LTTNG_MAP_QUERY_STATUS_OK; + +end: + return status; +} + +enum lttng_map_query_status lttng_map_query_get_pid_at_index( + const struct lttng_map_query *query, unsigned int index, + pid_t *pid) +{ + enum lttng_map_query_status status; + + assert(query); + + if (query->config_buffer != LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET) { + status = LTTNG_MAP_QUERY_STATUS_INVALID; + goto end; + } + + + *pid = *(pid_t *) lttng_dynamic_array_get_element(&query->pid_array, index); + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + + +enum lttng_map_query_status lttng_map_query_get_key_filter( + const struct lttng_map_query *query, const char **key_filter) +{ + enum lttng_map_query_status status; + + if (query->key_filter == NULL) { + status = LTTNG_MAP_QUERY_STATUS_NONE; + goto end; + } + + *key_filter = query->key_filter; + status = LTTNG_MAP_QUERY_STATUS_OK; +end: + return status; +} + +LTTNG_HIDDEN +ssize_t lttng_map_query_create_from_payload(struct lttng_payload_view *src_view, + struct lttng_map_query **query) +{ + ssize_t ret, offset = 0; + struct lttng_map_query *local_query; + const struct lttng_map_query_comm *query_comm; + + if (!src_view || !query) { + ret = -1; + goto end; + } + + query_comm = (typeof(query_comm)) src_view->buffer.data; + offset += sizeof(*query_comm); + + local_query = lttng_map_query_create(query_comm->config_cpu, + query_comm->config_buffer, query_comm->config_app_bitness); + if (!local_query) { + ret = -1; + goto end; + } + + local_query->sum_by_cpu = query_comm->sum_by_cpu; + local_query->sum_by_pid = query_comm->sum_by_pid; + local_query->sum_by_uid = query_comm->sum_by_uid; + local_query->sum_by_app_bitness = query_comm->sum_by_app_bitness; + + if (query_comm->key_filter_length != 0) { + const char *key_filter; + struct lttng_payload_view key_filter_view = + lttng_payload_view_from_view( + src_view, offset, + query_comm->key_filter_length); + + key_filter = key_filter_view.buffer.data; + if (!lttng_buffer_view_contains_string(&key_filter_view.buffer, + key_filter, query_comm->key_filter_length)){ + ret = -1; + goto end; + } + + lttng_map_query_add_key_filter(local_query, key_filter); + + offset += query_comm->key_filter_length; + } + + if (local_query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int cpu_idx; + + assert(query_comm->cpu_count > 0); + + for (cpu_idx = 0; cpu_idx < query_comm->cpu_count; cpu_idx++) { + int cpu_id; + struct lttng_payload_view cpu_id_view = + lttng_payload_view_from_view( src_view, + offset, sizeof(cpu_id)); + cpu_id = *(int *) cpu_id_view.buffer.data; + lttng_map_query_add_cpu(local_query, cpu_id); + offset+=sizeof(cpu_id); + } + } + + switch (local_query->config_buffer){ + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + { + unsigned int pid_idx; + + assert(query_comm->pid_count > 0); + + for (pid_idx = 0; pid_idx < query_comm->pid_count; pid_idx++) { + pid_t pid; + struct lttng_payload_view pid_view = + lttng_payload_view_from_view( src_view, + offset, sizeof(pid)); + pid = *(pid_t *) pid_view.buffer.data; + lttng_map_query_add_pid(local_query, pid); + offset+=sizeof(pid); + } + break; + } + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + { + unsigned int uid_idx; + + assert(query_comm->uid_count > 0); + + for (uid_idx = 0; uid_idx < query_comm->uid_count; uid_idx++) { + uid_t uid; + struct lttng_payload_view uid_view = + lttng_payload_view_from_view( src_view, + offset, sizeof(uid)); + uid = *(uid_t *) uid_view.buffer.data; + lttng_map_query_add_uid(local_query, uid); + offset+=sizeof(uid); + } + } + default: + break; + } + + ret = offset; + *query = local_query; + local_query = NULL; +end: + return ret; +} + +LTTNG_HIDDEN +int lttng_map_query_serialize(const struct lttng_map_query *query, + struct lttng_payload *payload) +{ + int ret; + struct lttng_map_query_comm query_comm = {}; + enum lttng_map_query_status status; + + query_comm.config_cpu = (uint8_t) query->config_cpu; + query_comm.config_buffer = (uint8_t) query->config_buffer; + query_comm.config_app_bitness = (uint8_t) query->config_bitness; + + query_comm.sum_by_cpu = (uint8_t) query->sum_by_cpu; + query_comm.sum_by_uid = (uint8_t) query->sum_by_uid; + query_comm.sum_by_pid = (uint8_t) query->sum_by_pid; + query_comm.sum_by_app_bitness = (uint8_t) query->sum_by_app_bitness; + + if (query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int cpu_count; + status = lttng_map_query_get_cpu_count(query, &cpu_count); + if (status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + + query_comm.cpu_count = cpu_count; + } + + switch (query->config_buffer){ + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + { + unsigned int pid_count; + status = lttng_map_query_get_pid_count(query, &pid_count); + if (status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + query_comm.pid_count = pid_count; + break; + } + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + { + unsigned int uid_count; + status = lttng_map_query_get_uid_count(query, &uid_count); + if (status != LTTNG_MAP_QUERY_STATUS_OK) { + ret = -1; + goto end; + } + query_comm.uid_count = uid_count; + } + default: + break; + } + + if (query->key_filter) { + query_comm.key_filter_length = strlen(query->key_filter) + 1; + } else { + query_comm.key_filter_length = 0; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, &query_comm, + sizeof(query_comm)); + if (ret) { + goto end; + } + + /* key_filter */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, query->key_filter, + query_comm.key_filter_length); + if (ret) { + goto end; + } + + if (query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + ret = lttng_dynamic_buffer_append(&payload->buffer, + query->cpu_array.buffer.data, + query->cpu_array.buffer.size); + if (ret) { + goto end; + } + } + + switch (query->config_buffer){ + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + { + ret = lttng_dynamic_buffer_append(&payload->buffer, + query->pid_array.buffer.data, + query->pid_array.buffer.size); + if (ret) { + goto end; + } + } + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + { + ret = lttng_dynamic_buffer_append(&payload->buffer, + query->uid_array.buffer.data, + query->uid_array.buffer.size); + if (ret) { + goto end; + } + } + default: + break; + } + +end: + return ret; +} + +void lttng_map_query_destroy(struct lttng_map_query *query) +{ + if (!query) { + return; + } + + if (query->config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + lttng_dynamic_array_reset(&query->cpu_array); + } + + switch(query->config_buffer) { + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET: + lttng_dynamic_array_reset(&query->uid_array); + break; + case LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET: + lttng_dynamic_array_reset(&query->pid_array); + break; + default: + break; + } + free(query->key_filter); + free(query); +} diff --git a/src/common/map.c b/src/common/map.c new file mode 100644 index 000000000..82b7c2541 --- /dev/null +++ b/src/common/map.c @@ -0,0 +1,1235 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +enum lttng_map_status lttng_map_set_name(struct lttng_map *map, + const char *name) +{ + char *name_copy = NULL; + enum lttng_map_status status; + + if (!map || !name || strlen(name) == 0) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + name_copy = strdup(name); + if (!name_copy) { + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + + free(map->name); + + map->name = name_copy; + name_copy = NULL; + + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +enum lttng_map_status lttng_map_get_name(const struct lttng_map *map, + const char **name) +{ + enum lttng_map_status status; + + if (!map || !name) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + if (!map->name) { + status = LTTNG_MAP_STATUS_UNSET; + } + + *name = map->name; + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +enum lttng_map_status lttng_map_create(const char *name, + unsigned int dimension_count, + uint64_t *dimension_sizes, + enum lttng_domain_type domain, + enum lttng_buffer_type buffer_type, + enum lttng_map_bitness bitness, + enum lttng_map_boundary_policy boundary_policy, + bool coalesce_hits, + struct lttng_map **map_out) +{ + enum lttng_map_status status; + struct lttng_map *map; + + if (dimension_count != 1) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + map = zmalloc(sizeof(struct lttng_map)); + if (!map) { + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + + if (name) { + status = lttng_map_set_name(map, name); + if (status != LTTNG_MAP_STATUS_OK) { + goto free_map; + } + } else { + map->name = NULL; + } + + map->dimension_count = dimension_count; + map->dimension_sizes = zmalloc( + sizeof(*map->dimension_sizes) * dimension_count); + if (!map->dimension_sizes) { + status = LTTNG_MAP_STATUS_ERROR; + goto free_name; + } + + memcpy(map->dimension_sizes, dimension_sizes, + sizeof(*map->dimension_sizes) * dimension_count); + + map->domain = domain; + map->buffer_type = buffer_type; + map->bitness = bitness; + map->boundary_policy = boundary_policy; + map->coalesce_hits = coalesce_hits; + + lttng_map_set_is_enabled(map, true); + + urcu_ref_init(&map->ref); + + *map_out = map; + + status = LTTNG_MAP_STATUS_OK; + + goto end; +free_name: + free(map->name); +free_map: + free(map); +end: + return status; +} + +unsigned int lttng_map_get_dimension_count( + const struct lttng_map *map) +{ + assert(map); + + return map->dimension_count; +} + +enum lttng_map_status lttng_map_get_dimension_length( + const struct lttng_map *map, unsigned int dimension, + uint64_t *dimension_length) +{ + enum lttng_map_status status; + + assert(map); + + if (dimension >= map->dimension_count) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + *dimension_length = map->dimension_sizes[dimension]; + + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +enum lttng_map_bitness lttng_map_get_bitness( + const struct lttng_map *map) +{ + assert(map); + + return map->bitness; +} + +enum lttng_domain_type lttng_map_get_domain( + const struct lttng_map *map) +{ + assert(map); + + return map->domain; +} + +enum lttng_buffer_type lttng_map_get_buffer_type( + const struct lttng_map *map) +{ + assert(map); + + return map->buffer_type; +} + +enum lttng_map_boundary_policy lttng_map_get_boundary_policy( + const struct lttng_map *map) +{ + assert(map); + + return map->boundary_policy; +} + +bool lttng_map_get_coalesce_hits(const struct lttng_map *map) +{ + assert(map); + + return map->coalesce_hits; +} + +LTTNG_HIDDEN +int lttng_map_serialize(const struct lttng_map *map, + struct lttng_payload *payload) +{ + int ret; + size_t header_offset, size_before_payload, size_name; + struct lttng_map_comm map_comm = {}; + struct lttng_map_comm *header; + + if (map->name != NULL) { + size_name = strlen(map->name) + 1; + } else { + size_name = 0; + } + + map_comm.name_length = size_name; + map_comm.is_enabled = LTTNG_OPTIONAL_GET(map->is_enabled); + map_comm.bitness = map->bitness; + map_comm.domain = map->domain; + map_comm.buffer_type = map->buffer_type; + map_comm.boundary_policy = map->boundary_policy; + map_comm.dimension_count = map->dimension_count; + map_comm.coalesce_hits = map->coalesce_hits; + + header_offset = payload->buffer.size; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &map_comm, + sizeof(map_comm)); + if (ret) { + goto end; + } + + size_before_payload = payload->buffer.size; + + /* map name */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, map->name, size_name); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append( + &payload->buffer, map->dimension_sizes, + sizeof(*map->dimension_sizes) * map->dimension_count); + if (ret) { + goto end; + } + + /* Update payload size. */ + header = (typeof(header)) (payload->buffer.data + header_offset); + header->length = payload->buffer.size - size_before_payload; + +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_map_create_from_payload( + struct lttng_payload_view *src_view, + struct lttng_map **map) +{ + ssize_t ret, offset = 0; + const struct lttng_map_comm *map_comm; + enum lttng_map_status status; + unsigned int dimension_count; + uint64_t *dimension_sizes = NULL; + bool coalesce_hits; + const char *name = NULL; + enum lttng_domain_type domain; + enum lttng_buffer_type buffer_type; + enum lttng_map_bitness bitness; + enum lttng_map_boundary_policy boundary_policy; + + if (!src_view || !map) { + ret = -1; + goto end; + } + + map_comm = (typeof(map_comm)) src_view->buffer.data; + offset += sizeof(*map_comm); + + domain = map_comm->domain; + buffer_type = map_comm->buffer_type; + bitness = map_comm->bitness; + boundary_policy = map_comm->boundary_policy; + dimension_count = map_comm->dimension_count; + coalesce_hits = map_comm->coalesce_hits; + + if (map_comm->name_length != 0) { + struct lttng_payload_view name_view = + lttng_payload_view_from_view( + src_view, offset, + map_comm->name_length); + + name = name_view.buffer.data; + if (!lttng_buffer_view_contains_string(&name_view.buffer, + name, map_comm->name_length)){ + ret = -1; + goto end; + } + offset += map_comm->name_length; + } + + size_t map_dim_sizes_len = sizeof(*(*map)->dimension_sizes) * dimension_count; + + struct lttng_payload_view dimension_sizes_view = + lttng_payload_view_from_view(src_view, offset, + map_dim_sizes_len); + + dimension_sizes = zmalloc(map_dim_sizes_len); + if (!dimension_sizes) { + ret = -1; + goto end; + } + + memcpy(dimension_sizes, dimension_sizes_view.buffer.data, + map_dim_sizes_len); + + offset += map_dim_sizes_len; + + status = lttng_map_create(name, dimension_count, + dimension_sizes, domain, buffer_type, bitness, + boundary_policy, coalesce_hits, map); + if (status != LTTNG_MAP_STATUS_OK) { + ret = -1; + goto end; + } + + lttng_map_set_is_enabled(*map, map_comm->is_enabled); + + ret = offset; + +end: + free(dimension_sizes); + return ret; +} + +LTTNG_HIDDEN +void lttng_map_set_is_enabled(struct lttng_map *map, bool enabled) +{ + assert(map); + + LTTNG_OPTIONAL_SET(&map->is_enabled, enabled); +} + +int lttng_map_get_is_enabled(const struct lttng_map *map) +{ + assert(map); + return (int) LTTNG_OPTIONAL_GET(map->is_enabled); +} + +LTTNG_HIDDEN +void lttng_map_get(struct lttng_map *map) +{ + urcu_ref_get(&map->ref); +} + +static void map_destroy_ref(struct urcu_ref *ref) +{ + struct lttng_map *map = container_of(ref, struct lttng_map, ref); + + free(map->dimension_sizes); + free(map->name); + free(map); + +} + +LTTNG_HIDDEN +void lttng_map_put(struct lttng_map *map) +{ + if (!map) { + return; + } + + urcu_ref_put(&map->ref , map_destroy_ref); +} + + +void lttng_map_destroy(struct lttng_map *map) +{ + lttng_map_put(map); +} + +static void delete_map_array_element(void *ptr) +{ + struct lttng_map *map = ptr; + + lttng_map_put(map); +} + +LTTNG_HIDDEN +struct lttng_map_list *lttng_map_list_create(void) +{ + struct lttng_map_list *map_list = NULL; + + map_list = zmalloc(sizeof(*map_list)); + if (!map_list) { + goto end; + } + + lttng_dynamic_pointer_array_init(&map_list->array, + delete_map_array_element); + +end: + return map_list; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_list_add(struct lttng_map_list *map_list, + struct lttng_map *map) +{ + enum lttng_map_status status; + int ret; + + assert(map_list); + assert(map); + + lttng_map_get(map); + + ret = lttng_dynamic_pointer_array_add_pointer(&map_list->array, map); + if (ret) { + lttng_map_put(map); + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + status = LTTNG_MAP_STATUS_OK; +end: + return status; + +} + +LTTNG_HIDDEN +ssize_t lttng_map_list_create_from_payload(struct lttng_payload_view *src_view, + struct lttng_map_list **map_list) +{ + unsigned int i; + ssize_t ret, offset = 0; + const struct lttng_map_list_comm *map_list_comm; + struct lttng_map_list *local_map_list = NULL; + + map_list_comm = (typeof(map_list_comm)) src_view->buffer.data; + offset += sizeof(*map_list_comm); + + local_map_list = lttng_map_list_create(); + if (!local_map_list) { + ret = -1; + goto end; + } + + for (i = 0; i < map_list_comm->count; i++) { + struct lttng_map *map = NULL; + struct lttng_payload_view map_view = + lttng_payload_view_from_view(src_view, offset, -1); + ssize_t map_size; + + map_size = lttng_map_create_from_payload(&map_view, &map); + if (map_size < 0) { + ret = map_size; + goto end; + } + + /* Transfer ownership of the map to the collection. */ + ret = lttng_map_list_add(local_map_list, map); + lttng_map_put(map); + if (ret < 0) { + ret = -1; + goto end; + } + + offset += map_size; + } + + /* Pass ownership to caller. */ + *map_list = local_map_list; + local_map_list = NULL; + + ret = offset; +end: + lttng_map_list_destroy(local_map_list); + return ret; +} + +LTTNG_HIDDEN +int lttng_map_list_serialize(const struct lttng_map_list *map_list, + struct lttng_payload *payload) +{ + int ret; + unsigned int i, count; + enum lttng_map_status status; + struct lttng_map_list_comm map_list_comm = {}; + + status = lttng_map_list_get_count(map_list, &count); + if (status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_INVALID; + goto end; + } + + map_list_comm.count = count; + ret = lttng_dynamic_buffer_append(&payload->buffer, &map_list_comm, + sizeof(map_list_comm)); + if (ret) { + goto end; + } + for (i = 0; i < count; i++) { + const struct lttng_map *map = + lttng_map_list_get_at_index(map_list, i); + + assert(map); + + ret = lttng_map_serialize(map, payload); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +const struct lttng_map *lttng_map_list_get_at_index( + const struct lttng_map_list *map_list, unsigned int index) +{ + struct lttng_map *map = NULL; + + assert(map_list); + if (index >= lttng_dynamic_pointer_array_get_count(&map_list->array)) { + goto end; + } + + map = (struct lttng_map *) + lttng_dynamic_pointer_array_get_pointer( + &map_list->array, index); +end: + return map; +} + +enum lttng_map_status lttng_map_list_get_count( + const struct lttng_map_list *map_list, unsigned int *count) +{ + enum lttng_map_status status = LTTNG_MAP_STATUS_OK; + + if (!map_list || !count) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&map_list->array); + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +void lttng_map_list_destroy(struct lttng_map_list *map_list) +{ + if (!map_list) { + return; + } + + lttng_dynamic_pointer_array_reset(&map_list->array); + free(map_list); +} + +struct lttng_map_key_value_pair *lttng_map_key_value_pair_create(const char *key, + int64_t value) +{ + struct lttng_map_key_value_pair *key_value; + + key_value = zmalloc(sizeof(struct lttng_map_key_value_pair)); + if (!key_value) { + goto end; + } + + key_value->key = strdup(key); + if (!key_value->key) { + free(key_value); + key_value = NULL; + goto end; + } + key_value->value = value; + +end: + return key_value; +} + +enum lttng_map_status lttng_map_key_value_pair_get_key( + const struct lttng_map_key_value_pair *key_value, + const char **key) +{ + assert(key_value); + assert(key_value->key); + + *key = key_value->key; + return LTTNG_MAP_STATUS_OK; +} + +enum lttng_map_status lttng_map_key_value_pair_get_value( + const struct lttng_map_key_value_pair *key_value, + int64_t *value) +{ + assert(key_value); + *value = key_value->value; + return LTTNG_MAP_STATUS_OK; +} + +LTTNG_HIDDEN +void lttng_map_key_value_pair_set_has_overflowed( + struct lttng_map_key_value_pair *key_value) +{ + assert(key_value); + + key_value->has_overflowed = true; +} + +LTTNG_HIDDEN +void lttng_map_key_value_pair_set_has_underflowed( + struct lttng_map_key_value_pair *key_value) +{ + assert(key_value); + + key_value->has_underflowed = true; +} + +bool lttng_map_key_value_pair_get_has_overflowed( + const struct lttng_map_key_value_pair *key_value) +{ + assert(key_value); + + return key_value->has_overflowed; +} + +bool lttng_map_key_value_pair_get_has_underflowed( + const struct lttng_map_key_value_pair *key_value) +{ + assert(key_value); + + return key_value->has_underflowed; +} + +LTTNG_HIDDEN +ssize_t lttng_map_key_value_pair_create_from_payload( + struct lttng_payload_view *src_view, + struct lttng_map_key_value_pair **key_value) +{ + const struct lttng_map_key_value_pair_comm *kv_pair_comm; + struct lttng_map_key_value_pair *kv_pair; + ssize_t ret, offset = 0; + const char *key; + int64_t value; + + if (!src_view || !key_value) { + ret = -1; + goto end; + } + + kv_pair_comm = (typeof(kv_pair_comm)) src_view->buffer.data; + offset += sizeof(*kv_pair_comm); + + if (kv_pair_comm->key_length == 0) { + ret = -1; + goto end; + } + + value = kv_pair_comm->value; + + struct lttng_payload_view key_view = + lttng_payload_view_from_view(src_view, offset, + kv_pair_comm->key_length); + key = key_view.buffer.data; + if (!lttng_buffer_view_contains_string(&key_view.buffer, + key, kv_pair_comm->key_length)) { + ret = -1; + goto end; + } + + offset += kv_pair_comm->key_length; + + kv_pair = lttng_map_key_value_pair_create(key, value); + if (!kv_pair) { + ret = -1; + goto end; + } + + kv_pair->has_overflowed = kv_pair_comm->has_overflowed; + kv_pair->has_underflowed = kv_pair_comm->has_underflowed; + + *key_value = kv_pair; + + ret = offset; + +end: + return ret; +} + +LTTNG_HIDDEN +int lttng_map_key_value_pair_serialize( + const struct lttng_map_key_value_pair *key_value, + struct lttng_payload *payload) +{ + int ret; + size_t key_len; + struct lttng_map_key_value_pair_comm kv_pair_comm = {0}; + + assert(key_value); + assert(key_value->key); + + key_len = strlen(key_value->key) + 1; + + kv_pair_comm.key_length = key_len; + kv_pair_comm.value = key_value->value; + kv_pair_comm.has_overflowed = key_value->has_overflowed; + kv_pair_comm.has_underflowed = key_value->has_underflowed; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &kv_pair_comm, + sizeof(kv_pair_comm)); + if (ret) { + goto end; + } + + /* Append key.*/ + ret = lttng_dynamic_buffer_append( + &payload->buffer, key_value->key, key_len); + if (ret) { + goto end; + } + +end: + return ret; +} + +void lttng_map_key_value_pair_destroy(struct lttng_map_key_value_pair *key_value) +{ + if (!key_value) { + return; + } + + free(key_value->key); + free(key_value); +} + +static void delete_map_key_value_pair_array_element(void *ptr) +{ + struct lttng_map_key_value_pair *key_value = ptr; + lttng_map_key_value_pair_destroy(key_value); +} + +LTTNG_HIDDEN +struct lttng_map_key_value_pair_list *lttng_map_key_value_pair_list_create( + enum lttng_map_key_value_pair_list_type type, + bool summed_all_cpus) +{ + struct lttng_map_key_value_pair_list *map_key_values = NULL; + + map_key_values = zmalloc(sizeof(*map_key_values)); + if (!map_key_values) { + goto end; + } + + map_key_values->type = type; + map_key_values->summed_all_cpus = summed_all_cpus; + + lttng_dynamic_pointer_array_init(&map_key_values->array, + delete_map_key_value_pair_array_element); + +end: + return map_key_values; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_set_identifier( + struct lttng_map_key_value_pair_list *kv_pair_list, + uint64_t identifier) +{ + enum lttng_map_status status; + assert(kv_pair_list); + + switch (kv_pair_list->type) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + kv_pair_list->id = identifier; + status = LTTNG_MAP_STATUS_OK; + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED: + ERR("Cannot set an identifier for an UST per-pid aggregation key value pair list"); + status = LTTNG_MAP_STATUS_INVALID; + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL: + ERR("Cannot set an identifier for a kernel key value pair list"); + status = LTTNG_MAP_STATUS_INVALID; + break; + default: + ERR("Unknown key value par list type %d", kv_pair_list->type); + abort(); + } + + return status; +} + +bool lttng_map_key_value_pair_list_get_summed_all_cpu( + const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + assert(kv_pair_list); + + return kv_pair_list->summed_all_cpus; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_set_cpu( + struct lttng_map_key_value_pair_list *kv_pair_list, + uint64_t cpu) +{ + assert(kv_pair_list); + + kv_pair_list->cpu = cpu; + + return LTTNG_MAP_STATUS_OK; +} + +uint64_t lttng_map_key_value_pair_list_get_cpu( + const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + assert(kv_pair_list); + + return kv_pair_list->cpu; +} + +enum lttng_map_key_value_pair_list_type lttng_map_key_value_pair_list_get_type( + const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + assert(kv_pair_list); + + return kv_pair_list->type; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_key_value_pair_list_append_key_value( + struct lttng_map_key_value_pair_list *kv_pair_list, + struct lttng_map_key_value_pair *key_value) +{ + int ret; + enum lttng_map_status status; + + assert(kv_pair_list); + assert(key_value); + + ret = lttng_dynamic_pointer_array_add_pointer(&kv_pair_list->array, + key_value); + if (ret) { + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_STATUS_OK; + +end: + return status; +} + +uint64_t lttng_map_key_value_pair_list_get_identifer( + const struct lttng_map_key_value_pair_list *kv_pair_list) +{ + assert(kv_pair_list); + + switch (kv_pair_list->type) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED: + ERR("No identifier for UST per-pid aggregation key value pair lists"); + abort(); + break; + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL: + ERR("No identifier for kernel key value pair lists"); + abort(); + break; + default: + ERR("Unknown key value par list type %d", kv_pair_list->type); + abort(); + } + + return kv_pair_list->id; +} + +const struct lttng_map_key_value_pair *lttng_map_key_value_pair_list_get_at_index( + const struct lttng_map_key_value_pair_list *kv_pair_list, + unsigned int index) +{ + struct lttng_map_key_value_pair *key_value = NULL; + + assert(kv_pair_list); + if (index >= lttng_dynamic_pointer_array_get_count(&kv_pair_list->array)) { + goto end; + } + + key_value = (struct lttng_map_key_value_pair *) + lttng_dynamic_pointer_array_get_pointer( + &kv_pair_list->array, index); +end: + return key_value; +} + +enum lttng_map_status lttng_map_key_value_pair_list_get_count( + const struct lttng_map_key_value_pair_list *kv_pair_list, + unsigned int *count) +{ + enum lttng_map_status status; + + if (!kv_pair_list || !count) { + status = LTTNG_MAP_STATUS_INVALID;; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&kv_pair_list->array); + + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +void lttng_map_key_value_pair_list_destroy(struct lttng_map_key_value_pair_list *kv_pair_list) +{ + if (!kv_pair_list) { + return; + } + + lttng_dynamic_pointer_array_reset(&kv_pair_list->array); + free(kv_pair_list); +} + +int lttng_map_key_value_pair_list_serialize( + const struct lttng_map_key_value_pair_list *kv_pair_list, + struct lttng_payload *payload) +{ + int ret; + unsigned int i, count; + enum lttng_map_status status; + struct lttng_map_key_value_pair_list_comm kv_pair_list_comm = {}; + + kv_pair_list_comm.id = kv_pair_list->id; + kv_pair_list_comm.cpu = kv_pair_list->cpu; + kv_pair_list_comm.type = (uint8_t) kv_pair_list->type; + kv_pair_list_comm.summed_all_cpus = (uint8_t) kv_pair_list->summed_all_cpus; + + status = lttng_map_key_value_pair_list_get_count(kv_pair_list, &count); + if (status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_INVALID; + goto end; + } + + kv_pair_list_comm.count = count; + ret = lttng_dynamic_buffer_append(&payload->buffer, &kv_pair_list_comm, + sizeof(kv_pair_list_comm)); + if (ret) { + goto end; + } + for (i = 0; i < count; i++) { + const struct lttng_map_key_value_pair *kv_pair = + lttng_map_key_value_pair_list_get_at_index(kv_pair_list, i); + + assert(kv_pair); + + ret = lttng_map_key_value_pair_serialize(kv_pair, payload); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_map_key_value_pair_list_create_from_payload( + struct lttng_payload_view *src_view, + struct lttng_map_key_value_pair_list **kv_pair_list) +{ + ssize_t ret, offset = 0; + unsigned int i; + enum lttng_map_status status; + const struct lttng_map_key_value_pair_list_comm *kv_pair_list_comm; + struct lttng_map_key_value_pair_list *local_key_values = NULL; + + kv_pair_list_comm = (typeof(kv_pair_list_comm)) src_view->buffer.data; + offset += sizeof(*kv_pair_list_comm); + + local_key_values = lttng_map_key_value_pair_list_create( + kv_pair_list_comm->type, kv_pair_list_comm->summed_all_cpus); + if (!local_key_values) { + ret = -1; + goto end; + } + + local_key_values->cpu = kv_pair_list_comm->cpu; + + switch (lttng_map_key_value_pair_list_get_type(local_key_values)) { + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: + case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: + status = lttng_map_key_value_pair_list_set_identifier(local_key_values, + kv_pair_list_comm->id); + if (status != LTTNG_MAP_STATUS_OK) { + ret = -1; + goto end; + } + break; + default: + break; + } + + for (i = 0; i < kv_pair_list_comm->count; i++) { + struct lttng_map_key_value_pair *kv_pair = NULL; + struct lttng_payload_view kv_view = + lttng_payload_view_from_view(src_view, offset, -1); + ssize_t kv_size; + + kv_size = lttng_map_key_value_pair_create_from_payload( + &kv_view, &kv_pair); + if (kv_size < 0) { + ret = kv_size; + goto end; + } + + /* Transfer ownership of the key-value to the collection. */ + status = lttng_map_key_value_pair_list_append_key_value(local_key_values, + kv_pair); + if (status != LTTNG_MAP_STATUS_OK) { + ret = -1; + goto end; + } + + offset += kv_size; + } + + /* Pass ownership to caller. */ + *kv_pair_list = local_key_values; + local_key_values = NULL; + + ret = offset; +end: + lttng_map_key_value_pair_list_destroy(local_key_values); + return ret; +} + +static void delete_map_key_value_pair_list_array_element(void *ptr) +{ + struct lttng_map_key_value_pair_list *kv_list = ptr; + lttng_map_key_value_pair_list_destroy(kv_list); +} + +LTTNG_HIDDEN +struct lttng_map_content *lttng_map_content_create( + enum lttng_buffer_type type) +{ + struct lttng_map_content *map_content = NULL; + + map_content = zmalloc(sizeof(*map_content)); + if (!map_content) { + goto end; + } + + map_content->type = type; + + lttng_dynamic_pointer_array_init(&map_content->array, + delete_map_key_value_pair_list_array_element); + +end: + return map_content; +} + +enum lttng_map_status lttng_map_content_get_count( + const struct lttng_map_content *map_content, + unsigned int *count) +{ + enum lttng_map_status status = LTTNG_MAP_STATUS_OK; + + if (!map_content || !count) { + status = LTTNG_MAP_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&map_content->array); + status = LTTNG_MAP_STATUS_OK; +end: + return status; +} + +enum lttng_buffer_type lttng_map_content_get_buffer_type( + const struct lttng_map_content *map_content) +{ + assert(map_content); + + return map_content->type; +} + +LTTNG_HIDDEN +enum lttng_map_status lttng_map_content_append_key_value_list( + struct lttng_map_content *map_content, + struct lttng_map_key_value_pair_list *kv_list) +{ + int ret; + enum lttng_map_status status; + + assert(map_content); + assert(kv_list); + + ret = lttng_dynamic_pointer_array_add_pointer(&map_content->array, + kv_list); + if (ret) { + status = LTTNG_MAP_STATUS_ERROR; + goto end; + } + + status = LTTNG_MAP_STATUS_OK; + +end: + return status; +} + +const struct lttng_map_key_value_pair_list *lttng_map_content_get_at_index( + const struct lttng_map_content *map_content, + unsigned int index) +{ + struct lttng_map_key_value_pair_list *kv_pair_list = NULL; + + assert(map_content); + if (index >= lttng_dynamic_pointer_array_get_count(&map_content->array)) { + goto end; + } + + kv_pair_list = (struct lttng_map_key_value_pair_list *) + lttng_dynamic_pointer_array_get_pointer( + &map_content->array, index); +end: + return kv_pair_list; +} + +LTTNG_HIDDEN +ssize_t lttng_map_content_create_from_payload( + struct lttng_payload_view *src_view, + struct lttng_map_content **map_content) +{ + ssize_t ret, offset = 0; + unsigned int i; + struct lttng_map_content_comm *map_content_comm; + struct lttng_map_content *local_map_content; + + map_content_comm = (typeof(map_content_comm)) src_view->buffer.data; + offset += sizeof(*map_content_comm); + + local_map_content = lttng_map_content_create(map_content_comm->type); + if (!local_map_content) { + ret = -1; + goto end; + } + + for (i = 0; i < map_content_comm->count; i++) { + struct lttng_map_key_value_pair_list *kv_pair_list = NULL; + struct lttng_payload_view kv_list_view = + lttng_payload_view_from_view(src_view, offset, -1); + ssize_t kv_list_size; + + kv_list_size = lttng_map_key_value_pair_list_create_from_payload( + &kv_list_view, &kv_pair_list); + if (kv_list_size < 0) { + ret = kv_list_size; + goto end; + } + + /* Transfer ownership of the key-value to the collection. */ + ret = lttng_map_content_append_key_value_list(local_map_content, + kv_pair_list); + if (ret < 0) { + ret = -1; + goto end; + } + + offset += kv_list_size; + } + + /* Pass ownership to caller. */ + *map_content = local_map_content; + local_map_content = NULL; + + ret = offset; +end: + lttng_map_content_destroy(local_map_content); + return ret; +} + +LTTNG_HIDDEN +int lttng_map_content_serialize( + const struct lttng_map_content *map_content, + struct lttng_payload *payload) +{ + int ret; + unsigned int i, count; + enum lttng_map_status status; + struct lttng_map_content_comm map_content_comm = {}; + + status = lttng_map_content_get_count(map_content, &count); + if (status != LTTNG_MAP_STATUS_OK) { + ret = LTTNG_ERR_INVALID; + goto end; + } + + map_content_comm.count = count; + map_content_comm.type = lttng_map_content_get_buffer_type(map_content); + + ret = lttng_dynamic_buffer_append(&payload->buffer, &map_content_comm, + sizeof(map_content_comm)); + if (ret) { + goto end; + } + for (i = 0; i < count; i++) { + const struct lttng_map_key_value_pair_list *kv_pair_list = + lttng_map_content_get_at_index(map_content, i); + + assert(kv_pair_list); + + ret = lttng_map_key_value_pair_list_serialize(kv_pair_list, payload); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +void lttng_map_content_destroy(struct lttng_map_content *map_content) +{ + if (!map_content) { + return; + } + + lttng_dynamic_pointer_array_reset(&map_content->array); + free(map_content); +} diff --git a/src/common/notification.c b/src/common/notification.c index c347b3cea..a4e971bd5 100644 --- a/src/common/notification.c +++ b/src/common/notification.c @@ -122,7 +122,7 @@ ssize_t lttng_notification_create_from_payload( notification_size, -1); evaluation_size = lttng_evaluation_create_from_payload( - &evaluation_view, &evaluation); + condition, &evaluation_view, &evaluation); } if (evaluation_size < 0) { diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index 4c1ba5cce..e65d358ad 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -107,6 +107,11 @@ enum lttcomm_sessiond_command { LTTNG_CREATE_SESSION_EXT = 49, LTTNG_CLEAR_SESSION = 50, LTTNG_LIST_TRIGGERS = 51, + LTTNG_ADD_MAP = 52, + LTTNG_ENABLE_MAP = 53, + LTTNG_DISABLE_MAP = 54, + LTTNG_LIST_MAPS = 55, + LTTNG_LIST_MAP_VALUES = 56, }; static inline @@ -197,6 +202,16 @@ const char *lttcomm_sessiond_command_str(enum lttcomm_sessiond_command cmd) return "LTTNG_CLEAR_SESSION"; case LTTNG_LIST_TRIGGERS: return "LTTNG_LIST_TRIGGERS"; + case LTTNG_ADD_MAP: + return "LTTNG_ADD_MAP"; + case LTTNG_ENABLE_MAP: + return "LTTNG_ENABLE_MAP"; + case LTTNG_DISABLE_MAP: + return "LTTNG_DISABLE_MAP"; + case LTTNG_LIST_MAPS: + return "LTTNG_LIST_MAPS"; + case LTTNG_LIST_MAP_VALUES: + return "LTTNG_LIST_MAP_VALUES"; default: abort(); } @@ -413,6 +428,18 @@ struct lttcomm_session_msg { struct lttng_channel chan; struct lttng_channel_extended extended; } LTTNG_PACKED channel; + /* Add map */ + struct { + uint32_t length; + } LTTNG_PACKED add_map; + /* Enable map */ + struct { + char map_name[LTTNG_SYMBOL_NAME_LEN]; + } LTTNG_PACKED enable_map; + /* Disable map */ + struct { + char map_name[LTTNG_SYMBOL_NAME_LEN]; + } LTTNG_PACKED disable_map; /* Context */ struct { char channel_name[LTTNG_SYMBOL_NAME_LEN]; @@ -508,6 +535,10 @@ struct lttcomm_session_msg { uint64_t session_descriptor_size; /* An lttng_session_descriptor follows. */ } LTTNG_PACKED create_session; + struct { + uint32_t map_length; + uint32_t query_length; + } LTTNG_PACKED list_map_values; } u; /* Count of fds sent. */ uint32_t fd_count; diff --git a/src/bin/lttng-sessiond/shm.c b/src/common/shm.c similarity index 86% rename from src/bin/lttng-sessiond/shm.c rename to src/common/shm.c index 8b7744221..737f73e08 100644 --- a/src/bin/lttng-sessiond/shm.c +++ b/src/common/shm.c @@ -189,3 +189,41 @@ char *shm_ust_get_mmap(char *shm_path, int global) error: return NULL; } + +/* + * shm_create_anonymous is never called concurrently within a process. + */ +int shm_create_anonymous(const char *owner_name) +{ + char tmp_name[NAME_MAX]; + int shmfd, ret; + + ret = snprintf(tmp_name, NAME_MAX, "/shm-%s-%d", owner_name, getpid()); + if (ret < 0) { + PERROR("snprintf"); + return -1; + } + /* + * Allocate shm, and immediately unlink its shm oject, keeping only the + * file descriptor as a reference to the object. + */ + shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700); + if (shmfd < 0) { + PERROR("shm_open"); + goto error_shm_open; + } + ret = shm_unlink(tmp_name); + if (ret < 0 && errno != ENOENT) { + PERROR("shm_unlink"); + goto error_shm_release; + } + return shmfd; + +error_shm_release: + ret = close(shmfd); + if (ret) { + PERROR("close"); + } +error_shm_open: + return -1; +} diff --git a/src/bin/lttng-sessiond/shm.h b/src/common/shm.h similarity index 85% rename from src/bin/lttng-sessiond/shm.h rename to src/common/shm.h index 94d2b72a1..d714506b8 100644 --- a/src/bin/lttng-sessiond/shm.h +++ b/src/common/shm.h @@ -11,4 +11,6 @@ char *shm_ust_get_mmap(char *shm_path, int global); +int shm_create_anonymous(const char *owner_name); + #endif /* _LTT_SHM_H */ diff --git a/src/common/trigger.c b/src/common/trigger.c index 71162e79b..9e0333be9 100644 --- a/src/common/trigger.c +++ b/src/common/trigger.c @@ -7,13 +7,14 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -92,7 +93,6 @@ const struct lttng_condition *lttng_trigger_get_const_condition( return trigger ? trigger->condition : NULL; } - /* * Note: the lack of reference counting 'get' on the action object is normal. * This API was exposed as such in 2.11. The client is not expected to call @@ -395,8 +395,13 @@ bool lttng_trigger_is_equal( } /* - * Name is not taken into account since it is cosmetic only. + * FIXME: frdeso: this is a change of behavior. + * See internal tracker issue 1028. */ + if (strcmp(a->name, b->name) != 0) { + return false; + } + if (!lttng_condition_is_equal(a->condition, b->condition)) { return false; } @@ -909,9 +914,9 @@ enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction( /* Apply to any domain. */ type = LTTNG_DOMAIN_NONE; break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: /* Return the domain of the event rule. */ - c_status = lttng_condition_event_rule_get_rule( + c_status = lttng_condition_on_event_get_rule( trigger->condition, &event_rule); assert(c_status == LTTNG_CONDITION_STATUS_OK); type = lttng_event_rule_get_domain_type(event_rule); @@ -949,11 +954,11 @@ enum lttng_error_code lttng_trigger_generate_bytecode( } switch (lttng_condition_get_type(condition)) { - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: { struct lttng_event_rule *event_rule; const enum lttng_condition_status condition_status = - lttng_condition_event_rule_borrow_rule_mutable( + lttng_condition_on_event_borrow_rule_mutable( condition, &event_rule); assert(condition_status == LTTNG_CONDITION_STATUS_OK); @@ -966,7 +971,7 @@ enum lttng_error_code lttng_trigger_generate_bytecode( } /* Generate the capture bytecode. */ - ret = lttng_condition_event_rule_generate_capture_descriptor_bytecode( + ret = lttng_condition_on_event_generate_capture_descriptor_bytecode( condition); if (ret != LTTNG_OK) { goto end; @@ -1013,3 +1018,88 @@ end: lttng_payload_reset(©_buffer); return copy; } + + +static +bool action_type_needs_tracer_notifier(enum lttng_action_type action_type) +{ + switch (action_type) { + case LTTNG_ACTION_TYPE_NOTIFY: + case LTTNG_ACTION_TYPE_START_SESSION: + case LTTNG_ACTION_TYPE_STOP_SESSION: + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + return true; + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + return false; + case LTTNG_ACTION_TYPE_GROUP: + case LTTNG_ACTION_TYPE_UNKNOWN: + default: + abort(); + } +} + +static +bool action_needs_tracer_notifier(const struct lttng_action *action) +{ + bool needs_tracer_notifier = false; + unsigned int i, count; + enum lttng_action_status action_status; + enum lttng_action_type action_type; + + assert(action); + /* If there is only one action. Check if it needs a tracer notifier. */ + action_type = lttng_action_get_type(action); + if (action_type != LTTNG_ACTION_TYPE_GROUP) { + needs_tracer_notifier = action_type_needs_tracer_notifier( + action_type); + goto end; + } + + /* + * Iterate over all the actions of the action group and check if any of + * them needs a tracer notifier. + */ + 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 *inner_action = + lttng_action_group_get_at_index(action, i); + + action_type = lttng_action_get_type(inner_action); + if (action_type_needs_tracer_notifier(action_type)) { + needs_tracer_notifier = true; + goto end; + } + } + +end: + return needs_tracer_notifier; +} + +LTTNG_HIDDEN +bool lttng_trigger_needs_tracer_notifier(const struct lttng_trigger *trigger) +{ + bool needs_tracer_notifier = false; + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const struct lttng_action *action = + lttng_trigger_get_const_action(trigger); + + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_ON_EVENT: + needs_tracer_notifier = action_needs_tracer_notifier(action); + goto end; + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + goto end; + case LTTNG_CONDITION_TYPE_UNKNOWN: + default: + abort(); + } +end: + return needs_tracer_notifier; +} diff --git a/src/common/ust-consumer/ust-consumer.c b/src/common/ust-consumer/ust-consumer.c index 6b195c77d..017ea5fb8 100644 --- a/src/common/ust-consumer/ust-consumer.c +++ b/src/common/ust-consumer/ust-consumer.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include "ust-consumer.h" @@ -352,48 +353,6 @@ error_alloc: return ret; } -/* - * create_posix_shm is never called concurrently within a process. - */ -static -int create_posix_shm(void) -{ - char tmp_name[NAME_MAX]; - int shmfd, ret; - - ret = snprintf(tmp_name, NAME_MAX, "/ust-shm-consumer-%d", getpid()); - if (ret < 0) { - PERROR("snprintf"); - return -1; - } - /* - * Allocate shm, and immediately unlink its shm oject, keeping - * only the file descriptor as a reference to the object. - * We specifically do _not_ use the / at the beginning of the - * pathname so that some OS implementations can keep it local to - * the process (POSIX leaves this implementation-defined). - */ - shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700); - if (shmfd < 0) { - PERROR("shm_open"); - goto error_shm_open; - } - ret = shm_unlink(tmp_name); - if (ret < 0 && errno != ENOENT) { - PERROR("shm_unlink"); - goto error_shm_release; - } - return shmfd; - -error_shm_release: - ret = close(shmfd); - if (ret) { - PERROR("close"); - } -error_shm_open: - return -1; -} - static int open_ust_stream_fd(struct lttng_consumer_channel *channel, int cpu, const struct lttng_credentials *session_credentials) { @@ -401,7 +360,7 @@ static int open_ust_stream_fd(struct lttng_consumer_channel *channel, int cpu, int ret; if (!channel->shm_path[0]) { - return create_posix_shm(); + return shm_create_anonymous("ust-consumer"); } ret = get_stream_shm_path(shm_path, channel->shm_path, cpu); if (ret) { diff --git a/src/common/utils.c b/src/common/utils.c index 134d2054f..df409aa4c 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -1486,6 +1486,16 @@ fopen_error: return ret; } +LTTNG_HIDDEN +int utils_get_number_of_possible_cpus(void) +{ + /* + * Return the number of configured cpus as opposed to number of online + * cpus. + */ + return sysconf(_SC_NPROCESSORS_CONF); +} + /* * Returns an estimate of the number of bytes of memory available based on the * the information in `/proc/meminfo`. The number returned by this function is diff --git a/src/common/utils.h b/src/common/utils.h index 570216d53..6fb5b2dfd 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -53,6 +53,7 @@ int utils_truncate_stream_file(int fd, off_t length); int utils_show_help(int section, const char *page_name, const char *help_msg); int utils_get_memory_available(size_t *value); int utils_get_memory_total(size_t *value); +int utils_get_number_of_possible_cpus(void); int utils_change_working_directory(const char *path); enum lttng_error_code utils_user_id_from_name( const char *user_name, uid_t *user_id); diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 6deab7848..8cb1e9b4e 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -10,6 +10,7 @@ * */ +#include "lttng/domain.h" #define _LGPL_SOURCE #include #include @@ -38,6 +39,8 @@ #include #include #include +#include +#include #include #include #include @@ -1212,10 +1215,16 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, } ret = lttng_dynamic_buffer_append(&payload.buffer, - *(exclusion_list + i), LTTNG_SYMBOL_NAME_LEN); + exclusion_list[i], exclusion_len); if (ret) { goto mem_error; } + + for (int i = 0; i < (LTTNG_SYMBOL_NAME_LEN - exclusion_len); i++) { + char c = 0; + + lttng_dynamic_buffer_append(&payload.buffer, &c, 1); + } } /* Add filter expression next. */ @@ -1636,6 +1645,178 @@ end: return ret; } +enum lttng_error_code lttng_add_map(struct lttng_handle *handle, + struct lttng_map *map) +{ + + int ret; + enum lttng_error_code ret_code; + struct lttcomm_session_msg lsm = { + .cmd_type = LTTNG_ADD_MAP, + }; + struct lttcomm_session_msg *message_lsm; + struct lttng_payload message; + struct lttng_payload reply; + + lttng_payload_init(&message); + lttng_payload_init(&reply); + + if (!map) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + lsm.domain.type = lttng_map_get_domain(map); + + lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + message_lsm = (struct lttcomm_session_msg *) message.buffer.data; + + ret = lttng_map_serialize(map, &message); + if (ret < 0) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + + message_lsm->u.add_map.length = (uint32_t) message.buffer.size - sizeof(lsm); + + { + struct lttng_payload_view message_view = + lttng_payload_view_from_payload(&message, 0, -1); + + message_lsm->fd_count = lttng_payload_view_get_fd_handle_count( + &message_view); + + ret = lttng_ctl_ask_sessiond_payload(&message_view, &reply); + if (ret < 0) { + ret_code = ret; + goto end; + } + } + + ret_code = LTTNG_OK; + +end: + lttng_payload_reset(&message); + lttng_payload_reset(&reply); + return ret_code; +} + +enum lttng_error_code lttng_enable_map(struct lttng_handle *handle, + const char *map_name) +{ + int ret; + enum lttng_error_code ret_code; + struct lttcomm_session_msg lsm; + struct lttng_payload message; + struct lttng_payload_view lsm_view = + lttng_payload_view_init_from_buffer( + (const char *) &lsm, 0, sizeof(lsm)); + struct lttng_payload reply; + + lttng_payload_init(&message); + lttng_payload_init(&reply); + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_ENABLE_MAP; + + COPY_DOMAIN_PACKED(lsm.domain, handle->domain); + + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(lsm.u.enable_map.map_name, map_name, + sizeof(lsm.u.enable_map.map_name)); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply); + if (ret < 0) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + + ret_code = LTTNG_OK; + +end: + lttng_payload_reset(&message); + lttng_payload_reset(&reply); + return ret_code; +} + +enum lttng_error_code lttng_disable_map(struct lttng_handle *handle, + const char *map_name) +{ + int ret; + enum lttng_error_code ret_code; + struct lttcomm_session_msg lsm; + struct lttng_payload message; + struct lttng_payload_view lsm_view = + lttng_payload_view_init_from_buffer( + (const char *) &lsm, 0, sizeof(lsm)); + struct lttng_payload reply; + + lttng_payload_init(&message); + lttng_payload_init(&reply); + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_DISABLE_MAP; + + COPY_DOMAIN_PACKED(lsm.domain, handle->domain); + + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(lsm.u.disable_map.map_name, map_name, + sizeof(lsm.u.disable_map.map_name)); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply); + if (ret < 0) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + + ret_code = LTTNG_OK; + +end: + lttng_payload_reset(&message); + lttng_payload_reset(&reply); + return ret_code; +} + /* * All tracing will be stopped for registered events of the channel. * Returns size of returned session payload data or a negative error code. @@ -2276,6 +2457,61 @@ end: return ret; } +enum lttng_error_code lttng_list_maps(struct lttng_handle *handle, + struct lttng_map_list **map_list) +{ + int ret; + enum lttng_error_code ret_code = LTTNG_OK; + struct lttcomm_session_msg lsm = { .cmd_type = LTTNG_LIST_MAPS }; + struct lttng_map_list *local_map_list = NULL; + struct lttng_payload reply; + struct lttng_payload_view lsm_view = + lttng_payload_view_init_from_buffer( + (const char *) &lsm, 0, sizeof(lsm)); + + lttng_payload_init(&reply); + + if (handle == NULL) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + COPY_DOMAIN_PACKED(lsm.domain, handle->domain); + + ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply); + if (ret < 0) { + ret_code = (enum lttng_error_code) -ret; + goto end; + } + + { + struct lttng_payload_view reply_view = + lttng_payload_view_from_payload( + &reply, 0, reply.buffer.size); + + ret = lttng_map_list_create_from_payload( + &reply_view, &local_map_list); + if (ret < 0) { + ret_code = LTTNG_ERR_FATAL; + goto end; + } + } + + *map_list = local_map_list; + local_map_list = NULL; +end: + lttng_payload_reset(&reply); + lttng_map_list_destroy(local_map_list); + return ret_code; +} + /* * Ask the session daemon for all available events of a session channel. * Sets the contents of the events array. @@ -3352,6 +3588,101 @@ end: return ret_code; } +/* + * Ask the session daemon for all values for a given map. + * On error, returns a negative value. + */ +enum lttng_error_code lttng_list_map_content( + struct lttng_handle *handle, const struct lttng_map *map, + const struct lttng_map_query *map_query, + struct lttng_map_content **map_content) +{ + struct lttcomm_session_msg lsm = { + .cmd_type = LTTNG_LIST_MAP_VALUES, + }; + struct lttcomm_session_msg *message_lsm; + struct lttng_payload message; + struct lttng_payload reply; + enum lttng_error_code ret; + struct lttng_map_content *local_map_content = NULL; + uint32_t map_length, map_query_length; + + lttng_payload_init(&message); + lttng_payload_init(&reply); + + if (!map || !map_query) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + lsm.domain.type = handle->domain.type; + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_dynamic_buffer_append(&message.buffer, &lsm, sizeof(lsm)); + if (ret) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_map_serialize(map, &message); + if (ret < 0) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + map_length = (uint32_t) message.buffer.size - sizeof(lsm); + + ret = lttng_map_query_serialize(map_query, &message); + if (ret < 0) { + ret = -LTTNG_ERR_UNK; + goto end; + } + map_query_length = (uint32_t) message.buffer.size - map_length - sizeof(lsm); + + message_lsm = (struct lttcomm_session_msg *) message.buffer.data; + message_lsm->u.list_map_values.map_length = map_length; + message_lsm->u.list_map_values.query_length = map_query_length; + { + struct lttng_payload_view message_view = + lttng_payload_view_from_payload( + &message, 0, -1); + + ret = lttng_ctl_ask_sessiond_payload(&message_view, &reply); + if (ret < 0) { + goto end; + } + } + + + { + struct lttng_payload_view reply_view = + lttng_payload_view_from_payload( + &reply, 0, reply.buffer.size); + ret = lttng_map_content_create_from_payload( + &reply_view, &local_map_content); + if (ret < 0) { + ret = LTTNG_ERR_FATAL; + goto end; + } + } + + *map_content = local_map_content; + local_map_content = NULL; + + ret = LTTNG_OK; + goto end; +end: + lttng_payload_reset(&reply); + lttng_payload_reset(&message); + lttng_map_content_destroy(local_map_content); + return ret; +} + /* * lib constructor. */ diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am index 817918f68..ec91e3b56 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -27,13 +27,17 @@ TESTS = tools/filtering/test_invalid_filter \ tools/crash/test_crash \ tools/regen-metadata/test_ust \ tools/regen-statedump/test_ust \ + tools/map/test_map_kernel \ tools/notification/test_notification_ust_error \ tools/notification/test_notification_ust_buffer_usage \ + tools/notification/test_notification_ust_capture \ tools/notification/test_notification_ust_event_rule_condition_exclusion \ tools/notification/test_notification_kernel_error \ tools/notification/test_notification_kernel_buffer_usage \ + tools/notification/test_notification_kernel_capture \ tools/notification/test_notification_kernel_instrumentation \ tools/notification/test_notification_kernel_syscall \ + tools/notification/test_notification_notifier_discarded_count \ tools/notification/test_notification_kernel_userspace_probe \ tools/notification/test_notification_multi_app \ tools/rotation/test_ust \ @@ -68,6 +72,7 @@ TESTS += ust/before-after/test_before_after \ ust/blocking/test_blocking \ ust/multi-lib/test_multi_lib \ ust/rotation-destroy-flush/test_rotation_destroy_flush \ + tools/map/test_map_ust \ tools/metadata/test_ust \ tools/relayd-grouping/test_ust diff --git a/tests/regression/kernel/Makefile.am b/tests/regression/kernel/Makefile.am index 8547efd67..f06f51528 100644 --- a/tests/regression/kernel/Makefile.am +++ b/tests/regression/kernel/Makefile.am @@ -1,11 +1,18 @@ # SPDX-License-Identifier: GPL-2.0-only -EXTRA_DIST = test_event_basic test_all_events test_syscall \ - test_clock_override test_rotation_destroy_flush \ - test_select_poll_epoll test_lttng_logger \ - test_userspace_probe test_callstack \ - test_syscall validate_select_poll_epoll.py \ - test_ns_contexts test_ns_contexts_change +EXTRA_DIST = test_all_events test_syscall \ + test_callstack \ + test_clock_override \ + test_event_basic \ + test_kernel_function \ + test_lttng_logger \ + test_ns_contexts \ + test_ns_contexts_change + test_rotation_destroy_flush \ + test_select_poll_epoll \ + test_syscall \ + test_userspace_probe \ + validate_select_poll_epoll.py noinst_PROGRAMS = select_poll_epoll select_poll_epoll_SOURCES = select_poll_epoll.c diff --git a/tests/regression/kernel/test_kernel_function b/tests/regression/kernel/test_kernel_function new file mode 100755 index 000000000..47deb9222 --- /dev/null +++ b/tests/regression/kernel/test_kernel_function @@ -0,0 +1,62 @@ +#!/bin/bash +# +# Copyright (C) 2013 Christian Babeux +# +# SPDX-License-Identifier: GPL-2.0-only +# + +TEST_DESC="Kernel tracer - function event" + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../.. +NUM_TESTS=6 + +source $TESTDIR/utils/utils.sh + +function test_kernel_function_basic() +{ + local TRACE_PATH=$(mktemp -d) + local SESSION_NAME="kernel_function_basic" + local EVENT_NAME="my_event_name" + local TARGET_SYMBOL="lttng_test_filter_event_write" + + create_lttng_session_ok $SESSION_NAME $TRACE_PATH + + lttng_enable_kernel_function_event_ok $SESSION_NAME "$TARGET_SYMBOL" "$EVENT_NAME" + + start_lttng_tracing_ok + + echo 1 > /proc/lttng-test-filter-event + + stop_lttng_tracing_ok + + validate_trace "${EVENT_NAME}_entry" $TRACE_PATH + validate_trace "${EVENT_NAME}_return" $TRACE_PATH + + destroy_lttng_session_ok $SESSION_NAME + + rm -rf $TRACE_PATH +} + +# MUST set TESTDIR before calling those functions +plan_tests $NUM_TESTS + +print_test_banner "$TEST_DESC" + +if [ "$(id -u)" == "0" ]; then + isroot=1 +else + isroot=0 +fi + +skip $isroot "Root access is needed. Skipping all tests." $NUM_TESTS || +{ + start_lttng_sessiond_notap + validate_lttng_modules_present + modprobe lttng-test + + test_kernel_function_basic + + modprobe --remove lttng-test + stop_lttng_sessiond_notap +} diff --git a/tests/regression/tools/Makefile.am b/tests/regression/tools/Makefile.am index d561a6479..1aa0b6c6a 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 trigger + base-path map metadata working-directory relayd-grouping clear tracker trigger diff --git a/tests/regression/tools/map/Makefile.am b/tests/regression/tools/map/Makefile.am new file mode 100644 index 000000000..8fb83c33f --- /dev/null +++ b/tests/regression/tools/map/Makefile.am @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-only + +AM_CPPFLAGS += -I$(top_srcdir)/tests -I$(srcdir) + +noinst_SCRIPTS = \ + map_base_test.sh \ + test_map_kernel + +EXTRA_DIST = \ + map_base_test.sh \ + test_map_kernel + +if HAVE_LIBLTTNG_UST_CTL +EXTRA_DIST += test_map_ust +noinst_SCRIPTS += test_map_ust +endif + +all-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + cp -f $(srcdir)/$$script $(builddir); \ + done; \ + fi + +clean-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + rm -f $(builddir)/$$script; \ + done; \ + fi diff --git a/tests/regression/tools/map/map_base_test.sh b/tests/regression/tools/map/map_base_test.sh new file mode 100644 index 000000000..ae7584936 --- /dev/null +++ b/tests/regression/tools/map/map_base_test.sh @@ -0,0 +1,578 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../.. + +TMPDIR=$(mktemp -d) + +SH_TAP=1 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +function view_map_ok() { + local map_name="$1" + local key="$2" + local expected_value="$3" + local extracted_value + local temp_view_output + + temp_view_output=$(mktemp -t map_view_output.XXXXXX) + + "$FULL_LTTNG_BIN" view-map "$map_name" --key="$key" > "$temp_view_output" + ok $? "Map '$map_name' viewed succesfully" + + grep -q " $key " "$temp_view_output" + ok $? "Key '$key' found in view-map output" + + # Get value + # TODO: this is based on the text output, ideally when mi is availabe we + # who should use it to parse the value! + # Sample output + # | key | 5| + # Keep white space surrounding the key so to avoid grepping a substring + # in a larger key. + extracted_value=$(grep " $key " "$temp_view_output" | tr -d " " | cut -d "|" -f3) + # Necessary since the returned value can be non existent + extracted_value=${extracted_value:-"-1"} + + is "$extracted_value" "$expected_value" "Key value is $expected_value as expected" + + rm -f "$temp_view_output" +} + +function test_map_view_empty() +{ + local domain="$1" + local bitness="$2" + local buf_option="$3" + + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + + diag "Map view empty: $domain bitness $bitness $buf_option" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + "$FULL_LTTNG_BIN" view-map "$MAP_NAME" > /dev/null + ok $? "Map enabled viewed succesfully" + + "$FULL_LTTNG_BIN" disable-map "$domain" "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" view-map "$MAP_NAME" > /dev/null + ok $? "Map disabled viewed succesfully" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_formated_keys() +{ + local domain="$1" + local event_name="$2" + local key_format="$3" + local expected_key="$4" + local test_app="$5" + + local bitness="32" + # buf option left empty for use with both UST and kernel domain. + local buf_option="" + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + + diag "Map with $domain formated key. event-name: \"$event_name\", key format: \"$key_format\", expecting: \"$expected_key\"" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$key_format" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$expected_key" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_n_triggers_n_keys() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + local number_of_trigger=5 + + diag "Map $domain with $number_of_trigger triggers with all different keys" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + for i in $(seq 1 $number_of_trigger); do + cur_trigger_name="${TRIGGER_NAME}${i}" + lttng_add_trigger_ok "$cur_trigger_name" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" \ + --map "$MAP_NAME" \ + --key "${KEY}${i}" + done + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + for i in $(seq 1 $number_of_trigger); do + view_map_ok "$MAP_NAME" "$KEY${i}" "$NR_ITER" + done + + for i in $(seq 1 $number_of_trigger); do + lttng_remove_trigger_ok "$TRIGGER_NAME${i}" + done + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_n_triggers_1_key() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + local number_of_trigger=5 + + diag "Map $domain with $number_of_trigger triggers all with the same key" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + for i in $(seq 1 $number_of_trigger); do + cur_trigger_name="${TRIGGER_NAME}${i}" + lttng_add_trigger_ok "$cur_trigger_name" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" \ + --map "$MAP_NAME" \ + --key "${KEY}" + done + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER * number_of_trigger))" + + for i in $(seq 1 $number_of_trigger); do + lttng_remove_trigger_ok "$TRIGGER_NAME${i}" + done + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_n_triggers_1_key_coalesced() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + local number_of_trigger=5 + + diag "Map $domain with $number_of_trigger triggers all with the same key" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" "--coalesce-hits" + + for i in $(seq 1 $number_of_trigger); do + cur_trigger_name="${TRIGGER_NAME}${i}" + lttng_add_trigger_ok "$cur_trigger_name" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" \ + --map "$MAP_NAME" \ + --key "${KEY}" + done + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + # With the `coalesce-hits` map option two enablers on the same event + # with the same key will only increment the counter once. + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER))" + + for i in $(seq 1 $number_of_trigger); do + lttng_remove_trigger_ok "$TRIGGER_NAME${i}" + done + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_disable_enable() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map $domain disable-enable --bitness $bitness" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + "$FULL_LTTNG_BIN" disable-map "$domain" -s "$SESSION_NAME" "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + # The values in the map should not have changed since the map is + # disabled. + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + "$FULL_LTTNG_BIN" enable-map "$domain" -s "$SESSION_NAME" "$MAP_NAME" > /dev/null + ok $? "Map enabled succesfully" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER * 2))" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_add_remove_add_trigger() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map $domain add-remove-add the same trigger" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER * 2))" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_creation_after_trigger() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map $domain creation after trigger creation" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_remove_trigger_before_stop() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map remove trigger before stop" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + stop_lttng_tracing_ok $SESSION_NAME + + # Confirm that the map content is unchanged after a stop. + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_two_incr_value_two_keys() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY1="romados" + local KEY2="pitarifique" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + + diag "Map remove trigger before stop" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY1" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY2" + + start_lttng_tracing_ok $SESSION_NAME + + "$test_app" + + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY1" "$NR_ITER" + view_map_ok "$MAP_NAME" "$KEY2" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_filter() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local event_name="$2" + local filter_field="$3" + local test_app="$4" + local buf_option="" + local bitness="32" + + diag "Map $domain filtering $event_name filter: \"$filter_field == 0\"" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" --filter "$filter_field==0"\ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "1" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_clear() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="$1" + local bitness="$2" + local event_name="$3" + local test_app="$4" + local buf_option="" + local bitness="32" + + diag "Map $domain clear" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + $test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_clear_session_ok "$SESSION_NAME" + + view_map_ok "$MAP_NAME" "$KEY" "0" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} diff --git a/tests/regression/tools/map/test_map_kernel b/tests/regression/tools/map/test_map_kernel new file mode 100755 index 000000000..3317e25e8 --- /dev/null +++ b/tests/regression/tools/map/test_map_kernel @@ -0,0 +1,322 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../.. +NR_ITER=5 +KERNEL_EVENT_NAME="lttng_test_filter_event" +KERNEL_TEST_FILE_NAME="/proc/lttng-test-filter-event" +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_SYSCALL_BIN="$TESTAPP_PATH/gen-syscall-events/gen-syscall-events" +TESTAPP_USERSPACE_BINARY="$TESTAPP_PATH/userspace-probe-elf-binary/.libs/userspace-probe-elf-binary" + +KERNEL_NUM_TESTS=421 +NUM_TESTS=$(($KERNEL_NUM_TESTS)) + +TMPDIR=$(mktemp -d) + +SH_TAP=1 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +source "$CURDIR/map_base_test.sh" + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +plan_tests $NUM_TESTS + +function kernel_test_app() +{ + /bin/echo -n "$NR_ITER" > "$KERNEL_TEST_FILE_NAME" +} + +function kernel_syscall_test_app() +{ + for i in $(seq 1 $NR_ITER) + do + "$TESTAPP_SYSCALL_BIN" /dev/null /proc/cpuinfo /proc/cmdline + done +} + +function kernel_userspace_test_app() +{ + for i in $(seq 1 $NR_ITER) + do + "$TESTAPP_USERSPACE_BINARY" + done +} + +function test_map_kernel_create() +{ + local MAP_NAME="my_map_name" + local MAP_NAME_2="my_map_name2" + local MAP_NAME_3="my_map_name3" + local MAP_NAME_4="my_map_name4" + local SESSION_NAME="my_session_name" + + create_lttng_session_ok "$SESSION_NAME" + + "$FULL_LTTNG_BIN" add-map --kernel --bitness 32 --session "wrong_session_name" "$MAP_NAME" > /dev/null + isnt $? 0 "Map creation failed on wrong session name" + + "$FULL_LTTNG_BIN" add-map --kernel --bitness 42 --session "$SESSION_NAME" "$MAP_NAME" > /dev/null + isnt $? 0 "Map creation failed \"--bitness\" wrong value as expected" + + "$FULL_LTTNG_BIN" add-map --kernel --session "SESS_DOESNT_EXIST" "$MAP_NAME" > /dev/null + isnt $? 0 "Failed to add map to session that doesn't exist" + + "$FULL_LTTNG_BIN" disable-map --kernel "MAP_DOESNT_EXIST" > /dev/null + isnt $? 0 "Failed to disable map that doesn't exist" + + "$FULL_LTTNG_BIN" add-map --kernel --bitness 64 --session "$SESSION_NAME" "$MAP_NAME" > /dev/null + ok $? "Map with 64bit bitness created succesfully" + + "$FULL_LTTNG_BIN" disable-map --kernel "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" add-map --kernel --bitness 32 --session "$SESSION_NAME" "$MAP_NAME_2" > /dev/null + ok $? "Map with 32bit bitness created succesfully" + + "$FULL_LTTNG_BIN" add-map --kernel --session "$SESSION_NAME" "$MAP_NAME_2" > /dev/null + isnt $? 0 "Duplicated map fails to create as expected" + + "$FULL_LTTNG_BIN" disable-map --kernel "$MAP_NAME_2" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" add-map --kernel --session "$SESSION_NAME" "$MAP_NAME_3" > /dev/null + ok $? "Map with default bitness created succesfully" + + "$FULL_LTTNG_BIN" disable-map --kernel "$MAP_NAME_3" > /dev/null + ok $? "Map removed succesfully" + + "$FULL_LTTNG_BIN" add-map --kernel --session "$SESSION_NAME" --max-key-count 212 "$MAP_NAME_4" > /dev/null + ok $? "Map with max key count created succesfully" + + "$FULL_LTTNG_BIN" disable-map --kernel "$MAP_NAME_4" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" enable-map --kernel "$MAP_NAME_4" > /dev/null + ok $? "Map enabled succesfully" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_kernel_probe() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME_1="my_trigger_name_1" + local TRIGGER_NAME_2="my_trigger_name_2" + local TARGET_SYMBOL="lttng_test_filter_event_write" + local event_name_1="my_probe1" + local event_name_2="my_probe2" + local KEY="foo" + + diag "Test map kernel probe" + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "--kernel" "32" "" + + lttng_add_trigger_ok "$TRIGGER_NAME_1" \ + --condition \ + on-event --kernel --probe="$TARGET_SYMBOL" "$event_name_1" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + lttng_add_trigger_ok "$TRIGGER_NAME_2" \ + --condition \ + on-event --kernel --probe="$TARGET_SYMBOL" "$event_name_2" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "\${EVENT_NAME}" + + start_lttng_tracing_ok $SESSION_NAME + + kernel_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "1" + view_map_ok "$MAP_NAME" "$event_name_2" "1" + + lttng_remove_trigger_ok "$TRIGGER_NAME_1" + lttng_remove_trigger_ok "$TRIGGER_NAME_2" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_kernel_function() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name_1" + local TARGET_SYMBOL="lttng_test_filter_event_write" + local event_name="my_probe1" + local KEY="foo" + + diag "Test map kernel function" + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "--kernel" "32" "" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --kernel --function="$TARGET_SYMBOL" "$event_name" \ + --action incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" \ + --action incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "\${EVENT_NAME}" + + start_lttng_tracing_ok $SESSION_NAME + + kernel_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "${KEY}" "2" + view_map_ok "$MAP_NAME" "${event_name}_entry" "1" + view_map_ok "$MAP_NAME" "${event_name}_return" "1" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_kernel_syscall() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME_1="my_trigger_name_1" + local TRIGGER_NAME_2="my_trigger_name_2" + local FILE_1="/proc/cmdline" + local FILE_2="/proc/cpuinfo" + local TARGET_SYSCALL=openat + local KEY="foo" + + diag "Test map kernel syscall" + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "--kernel" "32" "" + + lttng_add_trigger_ok "$TRIGGER_NAME_1" \ + --condition \ + on-event --kernel --syscall "$TARGET_SYSCALL" --filter "filename == \"$FILE_1\"" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + lttng_add_trigger_ok "$TRIGGER_NAME_2" \ + --condition \ + on-event --kernel --syscall "$TARGET_SYSCALL" --filter "filename == \"$FILE_2\"" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "\${EVENT_NAME}" + + start_lttng_tracing_ok $SESSION_NAME + + kernel_syscall_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + # When using filtering on filename we only get hits on the entry event + # as it's the one having the filename argument. + view_map_ok "$MAP_NAME" "${KEY}" "$NR_ITER" + view_map_ok "$MAP_NAME" "syscall_entry_$TARGET_SYSCALL" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME_1" + lttng_remove_trigger_ok "$TRIGGER_NAME_2" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_kernel_userspace_probe() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME_1="my_trigger_name_1" + local TEST_FUNCTION="test_function" + local KEY="foo" + + diag "Test map kernel userspace probe" + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "--kernel" "32" "" + + lttng_add_trigger_ok "$TRIGGER_NAME_1" \ + --condition \ + on-event --kernel --userspace-probe="elf:$TESTAPP_USERSPACE_BINARY:$TEST_FUNCTION" event_name \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + kernel_userspace_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME_1" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +if [ "$(id -u)" == "0" ]; then + + start_lttng_sessiond_notap + + validate_lttng_modules_present + + modprobe lttng-test + + test_map_kernel_create + + test_map_formated_keys "--kernel" "$KERNEL_EVENT_NAME" "\${EVENT_NAME}" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_formated_keys "--kernel" "$KERNEL_EVENT_NAME" "pitarifique-\${EVENT_NAME}" "pitarifique-$KERNEL_EVENT_NAME" kernel_test_app + + test_map_view_empty "--kernel" "64" "" + test_map_view_empty "--kernel" "32" "" + + test_map_n_triggers_n_keys "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_n_triggers_n_keys "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_n_triggers_1_key "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_n_triggers_1_key "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_n_triggers_1_key_coalesced "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_n_triggers_1_key_coalesced "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_disable_enable "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_disable_enable "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_add_remove_add_trigger "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_add_remove_add_trigger "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_creation_after_trigger "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_creation_after_trigger "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_remove_trigger_before_stop "--kernel" "32" "$KERNEL_EVENT_NAME" kernel_test_app + test_map_remove_trigger_before_stop "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_two_incr_value_two_keys "--kernel" "64" "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_clear "--kernel" 32 "$KERNEL_EVENT_NAME" kernel_test_app + test_map_clear "--kernel" 64 "$KERNEL_EVENT_NAME" kernel_test_app + + test_map_kernel_probe + test_map_kernel_function + + test_map_kernel_syscall + + test_map_kernel_userspace_probe + + test_map_filter "--kernel" "$KERNEL_EVENT_NAME" "intfield" kernel_test_app + + modprobe --remove lttng-test + + stop_lttng_sessiond_notap +else + # Kernel tests are skipped. + skip 0 "Root access is needed. Skipping all kernel notification tests." $KERNEL_NUM_TESTS +fi + + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/map/test_map_query.c b/tests/regression/tools/map/test_map_query.c new file mode 100644 index 000000000..04e868961 --- /dev/null +++ b/tests/regression/tools/map/test_map_query.c @@ -0,0 +1,125 @@ +#include +#include + +#include +#include +#include +#include + +int main() { + int ret; + enum lttng_map_query_status query_status; + enum lttng_error_code ret_code; + unsigned int map_count, list_count; + enum lttng_map_status map_status; + struct lttng_map_content *map_content = NULL; + struct lttng_map_list *map_list = NULL; + const struct lttng_map *map = NULL; + struct lttng_domain *domains = NULL; + const struct lttng_map_key_value_pair_list *kv_list; + const struct lttng_map_key_value_pair *kv_pair; + const char *key; + int64_t value; + int nb_domains; + + + nb_domains = lttng_list_domains("mysession", &domains); + + struct lttng_handle *handle = lttng_create_handle("mysession", &domains[0]); + + struct lttng_map_query *map_query = lttng_map_query_create( + LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET, + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL, + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL); + + if (!map_query) { + printf("Error creating the map query\n"); + ret = -1; + goto end; + } + + query_status = lttng_map_query_add_cpu(map_query, 0); + if (query_status != LTTNG_MAP_QUERY_STATUS_OK) { + printf("Error setting the targeted cpu\n"); + ret = -1; + goto end; + } + + query_status = lttng_map_query_add_key_filter(map_query, + "total number of hits"); + if (query_status != LTTNG_MAP_QUERY_STATUS_OK) { + printf("Error setting the targeted key\n"); + ret = -1; + goto end; + } + + ret_code = lttng_list_maps(handle, &map_list); + if (ret_code != LTTNG_OK) { + printf("Error getting list of all maps\n"); + ret = -1; + goto end; + } + + map_status = lttng_map_list_get_count(map_list, &map_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + printf("Error getting the number of maps\n"); + ret = -1; + goto end; + } + + if (map_count < 1) { + printf("Error: expecting at least 1 map.\n"); + ret = -1; + goto end; + } + + map = lttng_map_list_get_at_index(map_list, 0); + if (!map) { + printf("Error getting map at index 0\n"); + ret = -1; + goto end; + } + + ret_code = lttng_list_map_content(handle, map, map_query, &map_content); + if (ret_code != LTTNG_OK) { + printf("Error executing the query on map\n"); + ret = -1; + goto end; + } + + map_status = lttng_map_content_get_count(map_content, &list_count); + if (map_status != LTTNG_MAP_STATUS_OK) { + printf("Error getting the number of key value pair list\n"); + ret = -1; + goto end; + } + + if (list_count < 1) { + printf("Error: expecting at least 1 list.\n"); + ret = -1; + goto end; + } + + kv_list = lttng_map_content_get_at_index(map_content, 0); + if (!kv_list) { + printf("Error getting key value pair list at index 0\n"); + ret = -1; + goto end; + } + + kv_pair = lttng_map_key_value_pair_list_get_at_index(kv_list, 0); + if (!kv_pair) { + printf("Error getting key value pair at index 0\n"); + ret = -1; + goto end; + } + + lttng_map_key_value_pair_get_key(kv_pair, &key); + lttng_map_key_value_pair_get_value(kv_pair, &value); + + printf("Key: \"%s\", value: %"PRId64"\n", key, value); + + ret = 0; +end: + return ret; +} diff --git a/tests/regression/tools/map/test_map_ust b/tests/regression/tools/map/test_map_ust new file mode 100755 index 000000000..b6c21c240 --- /dev/null +++ b/tests/regression/tools/map/test_map_ust @@ -0,0 +1,416 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../.. +NR_ITER=5 +NR_USEC_WAIT=1 +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" + +GEN_UST_NEVENTS_BIN="$TESTAPP_PATH/gen-ust-nevents/gen-ust-nevents" + +UST_EVENT_NAME="tp:tptest" + +UST_NUM_TESTS=474 +NUM_TESTS=$(($UST_NUM_TESTS)) + +TMPDIR=$(mktemp -d) + +SH_TAP=1 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +source "$CURDIR/map_base_test.sh" + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + +if [ ! -x "$TESTAPP_BIN" ]; then + BAIL_OUT "No UST events binary detected." +fi + +plan_tests $NUM_TESTS + + +function ust_test_app() +{ + $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT +} + +function test_map_ust_per_uid_create() +{ + local MAP_NAME="my_map_name" + local MAP_NAME_2="my_map_name2" + local MAP_NAME_3="my_map_name3" + local SESSION_NAME="my_session_name" + + diag "Map creation" + + create_lttng_session_ok "$SESSION_NAME" + + "$FULL_LTTNG_BIN" add-map --userspace --bitness 32 --session "wrong_session_name" "$MAP_NAME" > /dev/null + isnt $? 0 "Map creation failed on wrong session name" + + "$FULL_LTTNG_BIN" add-map --userspace --bitness 42 --session "$SESSION_NAME" "$MAP_NAME" > /dev/null + isnt $? 0 "Map creation failed \"--bitness\" wrong value as expected" + + "$FULL_LTTNG_BIN" add-map --userspace --session "SESS_DOESNT_EXIST" "$MAP_NAME" > /dev/null + isnt $? 0 "Failed to add map to session that doesn't exist" + + "$FULL_LTTNG_BIN" disable-map --userspace --session "$SESSION_NAME" "MAP_DOESNT_EXIST" > /dev/null + isnt $? 0 "Failed to disable map that doesn't exist" + + "$FULL_LTTNG_BIN" add-map --userspace --bitness 64 --session "$SESSION_NAME" "$MAP_NAME" > /dev/null + ok $? "Map with 64bit bitness created succesfully" + + "$FULL_LTTNG_BIN" disable-map --userspace "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_ust_per_pid_create() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + + diag "Map per-pid creation" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" --userspace 64 --per-pid + + "$FULL_LTTNG_BIN" disable-map --userspace "$MAP_NAME" > /dev/null + ok $? "Map disabled succesfully" + + "$FULL_LTTNG_BIN" enable-map --userspace "$MAP_NAME" > /dev/null + ok $? "Map enabled succesfully" + + destroy_lttng_session_ok "$SESSION_NAME" +} + +function test_map_base_scenario() +{ + local domain="$1" + local bitness="$2" + local buf_option="$3" + local event_name="$4" + + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + + diag "Map base tracing scenario: $domain bitness $bitness $buf_option" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event "$domain" "$event_name" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + ust_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_ust_two_apps() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="--userspace" + local bitness="64" + local buf_option="--per-uid" + + diag "Map with two apps" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --userspace "$UST_EVENT_NAME" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + ust_test_app + ust_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$((NR_ITER * 2))" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_ust_with_events() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="--userspace" + local bitness="64" + local buf_option="--per-uid" + + diag "Map with regular events" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + enable_ust_lttng_event_ok "$SESSION_NAME" "*" + + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --userspace "$UST_EVENT_NAME" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + ust_test_app + + stop_lttng_tracing_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +function test_map_ust_per_pid_dead_app_aggregation() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME1="my_trigger_name1" + local TRIGGER_NAME2="my_trigger_name2" + local KEY1="foo" + local KEY2="ashton" + local domain="--userspace" + local bitness="64" + local buf_option="--per-pid" + + local file_sync_before_exit=$(mktemp -u) + local file_sync_before_exit_touch=$(mktemp -u) + + # In per-pid, test that running apps and dead apps aggrgated values are + # listed in their own map. + + diag "Map per-pid user with dead app aggregation" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + + lttng_add_trigger_ok "$TRIGGER_NAME1" \ + --condition \ + on-event --userspace "tp:tptest1" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY1" + + lttng_add_trigger_ok "$TRIGGER_NAME2" \ + --condition \ + on-event --userspace "$UST_EVENT_NAME" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY2" + + start_lttng_tracing_ok $SESSION_NAME + + # Two apps will have run completely when we call view-map. + $GEN_UST_NEVENTS_BIN -i $NR_ITER -w $NR_USEC_WAIT + $GEN_UST_NEVENTS_BIN -i $NR_ITER -w $NR_USEC_WAIT + + # One app will be done generating events but is still running when we + # call view-map. + + $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT \ + --sync-before-exit-touch $file_sync_before_exit_touch \ + --sync-before-exit $file_sync_before_exit & + + # Wait for the before exit sync point. This ensure that we went over the + # last tracepoint. + while [ ! -f "${file_sync_before_exit_touch}" ]; do + sleep 0.1 + done + + stop_lttng_tracing_ok $SESSION_NAME + + # After the apps are dead, we should see map key value pairs in the + # dead map aggregation listing. Two apps ran and exited, so we should + # have NR_ITER * 2 hits. + view_map_ok "$MAP_NAME" "$KEY1" "$(( $NR_ITER * 2 ))" + + # One app is still running and is done generating events, we should see + # NR_ITER hits. + view_map_ok "$MAP_NAME" "$KEY2" "$NR_ITER" + + lttng_remove_trigger_ok "$TRIGGER_NAME1" + lttng_remove_trigger_ok "$TRIGGER_NAME2" + + touch "$file_sync_before_exit" + wait + + destroy_lttng_session_ok $SESSION_NAME + + rm -f ${file_sync_before_exit} + rm -f ${file_sync_before_exit_touch} +} + +function test_map_ust_exclusion() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="--userspace" + local bitness="64" + local buf_option="--per-uid" + local exclusion="tp:tptest1,tp:tptest2,tp:tptest3,tp:tptest4" + + diag "Map per-pid user with dead app aggregation" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --userspace "tp:tptest*" --exclude="$exclusion" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$GEN_UST_NEVENTS_BIN" -i "$NR_ITER" -w 1 + + stop_lttng_tracing_ok $SESSION_NAME + + # After the map is dead, we should still see map key value pairs in the + # dead map aggregation listing. + view_map_ok "$MAP_NAME" "$KEY" "5" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + + +function test_map_ust_clear_per_pid() +{ + local MAP_NAME="my_map_name" + local SESSION_NAME="my_session_name" + local TRIGGER_NAME="my_trigger_name" + local KEY="foo" + local domain="--userspace" + local bitness="64" + local buf_option="--per-pid" + + diag "Map UST per-pid clear" + + create_lttng_session_ok "$SESSION_NAME" + + lttng_add_map_ok "$MAP_NAME" "$SESSION_NAME" "$domain" "$bitness" "$buf_option" + lttng_add_trigger_ok "$TRIGGER_NAME" \ + --condition \ + on-event --userspace "tp:tptest1" \ + --action \ + incr-value --session "$SESSION_NAME" --map "$MAP_NAME" --key "$KEY" + + start_lttng_tracing_ok $SESSION_NAME + + "$GEN_UST_NEVENTS_BIN" -i "$NR_ITER" -w 1 + + stop_lttng_tracing_ok $SESSION_NAME + + # After the map is dead, we should still see map key value pairs in the + # dead map aggregation listing. + view_map_ok "$MAP_NAME" "$KEY" "5" + + lttng_clear_session_ok $SESSION_NAME + + view_map_ok "$MAP_NAME" "$KEY" "0" + + lttng_remove_trigger_ok "$TRIGGER_NAME" + + destroy_lttng_session_ok $SESSION_NAME +} + +start_lttng_sessiond_notap + +test_map_ust_per_pid_dead_app_aggregation +test_map_n_triggers_n_keys "--userspace" "64" "$UST_EVENT_NAME" ust_test_app +test_map_n_triggers_n_keys "--userspace" "32" "$UST_EVENT_NAME" ust_test_app + +test_map_n_triggers_1_key "--userspace" "64" "$UST_EVENT_NAME" ust_test_app +test_map_n_triggers_1_key "--userspace" "32" "$UST_EVENT_NAME" ust_test_app + +test_map_n_triggers_1_key_coalesced "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_n_triggers_1_key_coalesced "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_ust_per_uid_create +test_map_ust_per_pid_create + +test_map_view_empty "--userspace" "64" "--per-uid" +test_map_view_empty "--userspace" "64" "--per-pid" +test_map_view_empty "--userspace" "32" "--per-uid" +test_map_view_empty "--userspace" "32" "--per-pid" + +test_map_base_scenario "--userspace" "64" "--per-uid" "$UST_EVENT_NAME" +test_map_base_scenario "--userspace" "32" "--per-uid" "$UST_EVENT_NAME" +test_map_base_scenario "--userspace" "64" "--per-pid" "$UST_EVENT_NAME" +test_map_base_scenario "--userspace" "32" "--per-pid" "$UST_EVENT_NAME" + +test_map_formated_keys "--userspace" "$UST_EVENT_NAME" "\${PROVIDER_NAME}:\${EVENT_NAME}" "$UST_EVENT_NAME" ust_test_app +test_map_formated_keys "--userspace" "$UST_EVENT_NAME" "pitarifique-\${EVENT_NAME}" "pitarifique-tptest" ust_test_app + +test_map_disable_enable "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_disable_enable "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_add_remove_add_trigger "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_add_remove_add_trigger "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_creation_after_trigger "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_creation_after_trigger "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_remove_trigger_before_stop "--userspace" "32" "$UST_EVENT_NAME" ust_test_app +test_map_remove_trigger_before_stop "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_ust_two_apps +test_map_ust_with_events +test_map_two_incr_value_two_keys "--userspace" "64" "$UST_EVENT_NAME" ust_test_app + +test_map_clear "--userspace" 32 "$UST_EVENT_NAME" ust_test_app +test_map_clear "--userspace" 64 "$UST_EVENT_NAME" ust_test_app +test_map_ust_clear_per_pid + +test_map_ust_exclusion + +test_map_filter "--userspace" "$UST_EVENT_NAME" "intfield" ust_test_app + +stop_lttng_sessiond_notap + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/Makefile.am b/tests/regression/tools/notification/Makefile.am index 3aca442f2..0dceea572 100644 --- a/tests/regression/tools/notification/Makefile.am +++ b/tests/regression/tools/notification/Makefile.am @@ -9,18 +9,22 @@ noinst_PROGRAMS = base_client notification rotation if NO_SHARED -CLEANFILES = libpause_consumer.so libpause_consumer.so.debug +CLEANFILES = libpause_consumer.so libpause_consumer.so.debug libpause_sessiond.so libpause_sessiond.so.debug EXTRA_DIST = \ base_client.c \ consumer_testpoints.c \ + sessiond_testpoints.c \ notification.c \ test_notification_kernel_buffer_usage \ + test_notification_kernel_capture \ test_notification_kernel_error \ test_notification_kernel_instrumentation \ test_notification_kernel_syscall \ test_notification_kernel_userspace_probe \ test_notification_multi_app \ + test_notification_notifier_discarded_count \ test_notification_ust_buffer_usage \ + test_notification_ust_capture \ test_notification_ust_error \ test_notification_ust_event_rule_condition_exclusion \ util_event_generator.sh @@ -38,7 +42,14 @@ libpause_consumer_la_LIBADD = \ $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ $(DL_LIBS) libpause_consumer_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS) -noinst_LTLIBRARIES = libpause_consumer.la + +libpause_sessiond_la_SOURCES = sessiond_testpoints.c +libpause_sessiond_la_LIBADD = \ + $(top_builddir)/src/common/libcommon.la \ + $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ + $(DL_LIBS) +libpause_sessiond_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS) +noinst_LTLIBRARIES = libpause_sessiond.la libpause_consumer.la base_client_SOURCES = base_client.c base_client_LDADD = $(LIB_LTTNG_CTL) @@ -56,6 +67,7 @@ noinst_SCRIPTS = \ test_notification_kernel_syscall \ test_notification_kernel_userspace_probe \ test_notification_multi_app \ + test_notification_notifier_discarded_count \ test_notification_ust_buffer_usage \ test_notification_ust_error \ test_notification_ust_event_rule_condition_exclusion \ @@ -64,12 +76,15 @@ noinst_SCRIPTS = \ EXTRA_DIST = \ test_notification_kernel_buffer_usage \ + test_notification_kernel_capture \ test_notification_kernel_error \ test_notification_kernel_instrumentation \ test_notification_kernel_syscall \ test_notification_kernel_userspace_probe \ test_notification_multi_app \ + test_notification_notifier_discarded_count \ test_notification_ust_buffer_usage \ + test_notification_ust_capture \ test_notification_ust_error \ test_notification_ust_event_rule_condition_exclusion \ test_rotation \ diff --git a/tests/regression/tools/notification/base_client.c b/tests/regression/tools/notification/base_client.c index 70ad763ab..c215b5cf4 100644 --- a/tests/regression/tools/notification/base_client.c +++ b/tests/regression/tools/notification/base_client.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,7 @@ static const char *channel_name = NULL; static double threshold_ratio = 0.0; static uint64_t threshold_bytes = 0; static bool is_threshold_ratio = false; +static bool use_action_group = false; static enum lttng_condition_type buffer_usage_type = LTTNG_CONDITION_TYPE_UNKNOWN; static enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; @@ -50,6 +52,7 @@ int parse_arguments(char **argv) const char *buffer_usage_threshold_type = NULL; const char *buffer_usage_threshold_value = NULL; const char *nr_expected_notifications_string = NULL; + const char *use_action_group_value = NULL; session_name = argv[1]; channel_name = argv[2]; @@ -58,6 +61,7 @@ int parse_arguments(char **argv) buffer_usage_threshold_type = argv[5]; buffer_usage_threshold_value = argv[6]; nr_expected_notifications_string = argv[7]; + use_action_group_value = argv[8]; /* Parse arguments */ /* Domain type */ @@ -98,6 +102,11 @@ int parse_arguments(char **argv) /* Number of notification to expect */ sscanf(nr_expected_notifications_string, "%d", &nr_expected_notifications); + /* Put notify action in a group. */ + if (!strcasecmp("1", use_action_group_value)) { + use_action_group = true; + } + return 0; error: return 1; @@ -107,6 +116,7 @@ int main(int argc, char **argv) { int ret = 0; enum lttng_condition_status condition_status; + enum lttng_action_status action_status; enum lttng_notification_channel_status nc_status; struct lttng_notification_channel *notification_channel = NULL; struct lttng_condition *condition = NULL; @@ -120,7 +130,7 @@ int main(int argc, char **argv) */ setbuf(stdout, NULL); - if (argc < 8) { + if (argc < 9) { printf("error: Missing arguments for tests\n"); ret = 1; goto end; @@ -196,11 +206,39 @@ int main(int argc, char **argv) goto end; } - action = lttng_action_notify_create(); - if (!action) { - printf("error: Could not create action notify\n"); - ret = 1; - goto end; + if (use_action_group) { + struct lttng_action *notify, *group; + + group = lttng_action_group_create(); + if (!group) { + printf("error: Could not create action group\n"); + ret = 1; + goto end; + } + notify = lttng_action_notify_create(); + if (!notify) { + lttng_action_destroy(group); + printf("error: Could not create action notify\n"); + ret = 1; + goto end; + } + action_status = lttng_action_group_add_action(group, notify); + if (action_status != LTTNG_ACTION_STATUS_OK) { + printf("error: Could not add action notify to action group\n"); + lttng_action_destroy(group); + lttng_action_destroy(notify); + ret = 1; + goto end; + } + + action = group; + } else { + 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); @@ -265,7 +303,7 @@ int main(int argc, char **argv) goto end; default: /* Unhandled conditions / errors. */ - printf("error: Unknown notification channel status\n"); + printf("error: Unknown notification channel status (%d) \n", status); ret = 1; goto end; } diff --git a/tests/regression/tools/notification/notification.c b/tests/regression/tools/notification/notification.c index 73415c51e..28c8e58ce 100644 --- a/tests/regression/tools/notification/notification.c +++ b/tests/regression/tools/notification/notification.c @@ -28,11 +28,553 @@ #include +/* A callback to populate the condition capture descriptor */ +typedef int (*condition_capture_desc_cb)(struct lttng_condition *condition); + +/* A callback for captured field validation */ +typedef int (*validate_cb)(const struct lttng_event_field_value *event_field, unsigned iteration); + int nb_args = 0; int named_pipe_args_start = 0; pid_t app_pid = 0; const char *app_state_file = NULL; +enum field_type { + FIELD_TYPE_PAYLOAD, + FIELD_TYPE_CONTEXT, + FIELD_TYPE_APP_CONTEXT, + FIELD_TYPE_ARRAY_FIELD, +}; + +struct capture_base_field_tuple { + char* field_name; + enum field_type field_type; + bool expected_ust; // Do we expect a capture? + bool expected_kernel; // Do we expect a capture? + validate_cb validate_ust; + validate_cb validate_kernel; +}; + +static +const char *field_value_type_to_str(enum lttng_event_field_value_type type) +{ + switch (type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN: + return "UNKNOWN"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID: + return "INVALID"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: + return "UNSIGNED INT"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: + return "SIGNED INT"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + return "UNSIGNED ENUM"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + return "SIGNED ENUM"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL: + return "REAL"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: + return "STRING"; + case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: + return "ARRAY"; + default: + abort(); + } +} + +static int validate_type( + const struct lttng_event_field_value *event_field, + enum lttng_event_field_value_type expect) +{ + int ret; + enum lttng_event_field_value_type value; + + value = lttng_event_field_value_get_type(event_field); + if (value == LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID) { + ret = 1; + goto end; + } + + ret = (expect == value); + ok(ret, "Expected field type: %s got %s", + field_value_type_to_str(expect), + field_value_type_to_str(value)); + + ret = !ret; + +end: + return ret; +} + +/* + * Validate unsigned captured field against the iteration number. + * The iteration number is always unsigned and will always be compared to value + * under MAX_UINT. + */ +static int validate_unsigned_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + uint64_t value; + enum lttng_event_field_value_status status; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field , &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ret = (value == (uint64_t) iteration); + ok (ret, "Expected unsigned int of value: %u got %" PRIu64, iteration, value); + + ret = !ret; + +end: + + return ret; +} + +/* + * Validate signed captured field. + * Value should be -1. + */ +static int validate_signed_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + int64_t expected = -1; + int64_t value; + enum lttng_event_field_value_status status; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_signed_int_get_value( + event_field , &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_signed_int_get_value"); + ret = 1; + goto end; + } + + ret = (value == expected); + ok(ret, "Expected signed int of value: %" PRId64 " got %" PRId64, expected, value); + + ret = !ret; + +end: + + return ret; +} + +/* + * Validate array of unsigned int. + */ +static int validate_array_unsigned_int_field( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + unsigned int expected = 3; + unsigned int count; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); + if (ret) { + goto end; + } + + status = lttng_event_field_value_array_get_length(event_field, &count); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; + } + + ret = (count == expected); + ok(ret, "Expected %d subelements got %d", expected, count); + if (!ret) { + ret = 1; + goto end; + } + + for (unsigned int i = 1; i < count + 1; i++) { + const struct lttng_event_field_value *value; + status = lttng_event_field_value_array_get_element_at_index( + event_field, i - 1, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_element_at_index"); + ret = 1; + goto end; + } + ret = validate_unsigned_int_field(value, i); + if (ret) { + goto end; + } + } + + ret = 0; +end: + + return ret; +} +static int validate_array_unsigned_int_field_at_index( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + uint64_t expected_value = 2; + enum lttng_event_field_value_status status; + uint64_t value; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field , &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ret = (value == expected_value); + ok (ret, "Expected unsigned int of value: %u got %" PRIu64, + expected_value, value); + + ret = 0; +end: + return ret; +} + +/* + * Validate sequence for a string (seqfield1): + * + * Value: "test" in utf8 [116, 101, 115, 116] + */ +static int validate_seqfield1( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + unsigned int count; + unsigned int expect[4] = {116, 101, 115, 116}; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY); + if (ret) { + goto end; + } + + status = lttng_event_field_value_array_get_length(event_field, &count); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; + } + + ret = (count == 4); + ok(ret, "Expected 4 subelement got %d", count); + if (!ret) { + ret = 1; + goto end; + } + + for (unsigned int i = 0; i < count ; i++) { + const struct lttng_event_field_value *value; + status = lttng_event_field_value_array_get_element_at_index( + event_field, i, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_array_get_element_at_index"); + ret = 1; + goto end; + } + ret = validate_unsigned_int_field(value, expect[i]); + if (ret) { + goto end; + } + } + + ret = 0; +end: + + return ret; +} + +static int validate_string( + const struct lttng_event_field_value *event_field, + const char *expect) +{ + int ret; + const char *value = NULL; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_STRING); + if (ret) { + goto end; + } + + value = lttng_event_field_value_string_get_value(event_field); + if (!value) { + fail("lttng_event_field_value_array_get_length"); + ret = 1; + goto end; + } + + ok(!strcmp(value, expect), "Expected string: \"%s\" got \"%s\"", expect, value); + + ret = 0; +end: + + return ret; +} + +/* + * Validate string. Expected value is "test". + */ +static int validate_string_test( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + const char *expect = "test"; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, expect); + return ret; +} + +/* + * Validate escaped string. Expected value is "\*". + */ +static int validate_string_escaped( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + const char *expect = "\\*"; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, expect); + return ret; +} + +/* + * Validate real field. + */ +static int validate_real( + const struct lttng_event_field_value *event_field, + double expect) +{ + int ret; + double value; + enum lttng_event_field_value_status status; + + ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_REAL); + if (ret) { + goto end; + } + + status = lttng_event_field_value_real_get_value(event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_real_get_value"); + ret = 1; + goto end; + } + + ret = (value == expect); + ok(ret, "Real expected: %f got: %f", expect, value); + + ret = !ret; +end: + return ret; +} + +/* + * Validate floatfield. + */ +static int validate_floatfield( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + double expect = 2222.0; + + /* Unused */ + (void) iteration; + + ret = validate_real(event_field, expect); + return ret; +} + +/* + * Validate doublefield. + */ +static int validate_doublefield( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + double expect = 2.0; + + /* Unused */ + (void) iteration; + + ret = validate_real(event_field, expect); + return ret; +} + +/* + * Validate enum0: enum0 = ( "AUTO: EXPECT 0" : container = 0 ) + */ +static int validate_enum0(const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + uint64_t value; + uint64_t expected_value = 0; + + /* Unused */ + (void) iteration; + + ret = validate_type(event_field, + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM); + if (ret) { + goto end; + } + + status = lttng_event_field_value_unsigned_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ok(value == expected_value, + "Enum value expected: %" PRIu64 " got %" PRIu64, + expected_value, value); + +end: + return ret; +} + +/* + * Validate enumnegative: enumnegative = ( "AUTO: EXPECT 0" : container = 0 ) + * + * We expect 2 labels here. + */ +static int validate_enumnegative( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + enum lttng_event_field_value_status status; + int64_t value; + int64_t expected_value = -1; + + /* Unused */ + (void) iteration; + + ret = validate_type( + event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM); + if (ret) { + goto end; + } + + status = lttng_event_field_value_signed_int_get_value( + event_field, &value); + if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + fail("lttng_event_field_value_unsigned_int_get_value"); + ret = 1; + goto end; + } + + ok(value == expected_value, + "Enum value expected: %" PRId64 " got %" PRId64, + expected_value, value); + +end: + return ret; +} + +static int validate_context_procname_ust( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, "gen-ust-events"); + return ret; +} + +static int validate_context_procname_kernel( + const struct lttng_event_field_value *event_field, + unsigned int iteration) +{ + int ret; + + /* Unused */ + (void) iteration; + + ret = validate_string(event_field, "echo"); + return ret; +} + +struct capture_base_field_tuple test_capture_base_fields[] = { + {"DOESNOTEXIST", FIELD_TYPE_PAYLOAD, false, false, NULL, NULL}, + {"intfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field}, + {"longfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field}, + {"signedfield", FIELD_TYPE_PAYLOAD, true, true, validate_signed_int_field, validate_signed_int_field}, + {"arrfield1", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"arrfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test}, + {"arrfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"seqfield1", FIELD_TYPE_PAYLOAD, true, true, validate_seqfield1, validate_seqfield1}, + {"seqfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test}, + {"seqfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"seqfield4", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field}, + {"arrfield1[1]", FIELD_TYPE_ARRAY_FIELD, true, true, validate_array_unsigned_int_field_at_index, validate_array_unsigned_int_field_at_index}, + {"stringfield", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test}, + {"stringfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_escaped, validate_string_escaped}, + {"floatfield", FIELD_TYPE_PAYLOAD, true, false, validate_floatfield, validate_floatfield}, + {"doublefield", FIELD_TYPE_PAYLOAD, true, false, validate_doublefield, validate_doublefield}, + {"enum0", FIELD_TYPE_PAYLOAD, true, true, validate_enum0, validate_enum0}, + {"enumnegative", FIELD_TYPE_PAYLOAD, true, true, validate_enumnegative, validate_enumnegative}, + {"$ctx.procname", FIELD_TYPE_CONTEXT, true, true, validate_context_procname_ust, validate_context_procname_kernel}, +}; + static const char *get_notification_trigger_name( struct lttng_notification *notification) { @@ -46,7 +588,7 @@ static const char *get_notification_trigger_name( } switch (lttng_evaluation_get_type(evaluation)) { - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: { status = lttng_evaluation_event_rule_get_trigger_name( evaluation, &name); @@ -904,6 +1446,7 @@ static void create_tracepoint_event_rule_trigger(const char *event_pattern, unsigned int exclusion_count, const char * const *exclusions, enum lttng_domain_type domain_type, + condition_capture_desc_cb capture_desc_cb, struct lttng_condition **condition, struct lttng_trigger **trigger) { @@ -960,9 +1503,16 @@ static void create_tracepoint_event_rule_trigger(const char *event_pattern, ok(success, "Setting tracepoint event rule exclusions"); } - tmp_condition = lttng_condition_event_rule_create(event_rule); + tmp_condition = lttng_condition_on_event_create(event_rule); ok(tmp_condition, "Condition event rule object creation"); + if (capture_desc_cb) { + ret = capture_desc_cb(tmp_condition); + if (ret) { + assert("Generating the condition capture descriptor"); + } + } + tmp_action = lttng_action_notify_create(); ok(tmp_action, "Action event rule object creation"); @@ -1033,7 +1583,7 @@ static void test_tracepoint_event_rule_notification( } create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0, - NULL, domain_type, &condition, &trigger); + NULL, domain_type, NULL, &condition, &trigger); notification_channel = lttng_notification_channel_create( lttng_session_daemon_notification_endpoint); @@ -1101,7 +1651,7 @@ static void test_tracepoint_event_rule_notification_filter( 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); + 0, NULL, domain_type, NULL, &ctrl_condition, &ctrl_trigger); nc_status = lttng_notification_channel_subscribe( notification_channel, ctrl_condition); @@ -1113,7 +1663,7 @@ static void test_tracepoint_event_rule_notification_filter( * `intfield` is even. */ create_tracepoint_event_rule_trigger(pattern, trigger_name, - "(intfield & 1) == 0", 0, NULL, domain_type, &condition, + "(intfield & 1) == 0", 0, NULL, domain_type, NULL, &condition, &trigger); nc_status = lttng_notification_channel_subscribe( @@ -1201,7 +1751,8 @@ static void test_tracepoint_event_rule_notification_exclusion( 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); + 0, NULL, domain_type, NULL, &ctrl_condition, + &ctrl_trigger); nc_status = lttng_notification_channel_subscribe( notification_channel, ctrl_condition); @@ -1209,7 +1760,8 @@ static void test_tracepoint_event_rule_notification_exclusion( "Subscribe to tracepoint event rule condition"); create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 4, - exclusions, domain_type, &condition, &trigger); + exclusions, domain_type, NULL, &condition, + &trigger); nc_status = lttng_notification_channel_subscribe( notification_channel, condition); @@ -1310,20 +1862,15 @@ static void test_kprobe_event_rule_notification( lttng_session_daemon_notification_endpoint); ok(notification_channel, "Notification channel object creation"); - event_rule = lttng_event_rule_kprobe_create(); + event_rule = lttng_event_rule_kernel_probe_create(location); ok(event_rule, "kprobe event rule object creation"); - event_rule_status = lttng_event_rule_kprobe_set_location( - event_rule, location); - ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting kprobe event rule location: '%s'", symbol_name); - - event_rule_status = lttng_event_rule_kprobe_set_name( + event_rule_status = lttng_event_rule_kernel_probe_set_event_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); + condition = lttng_condition_on_event_create(event_rule); ok(condition, "Condition event rule object creation"); /* Register the trigger for condition. */ @@ -1424,20 +1971,15 @@ static void test_uprobe_event_rule_notification( lttng_session_daemon_notification_endpoint); ok(notification_channel, "Notification channel object creation"); - event_rule = lttng_event_rule_uprobe_create(); + event_rule = lttng_event_rule_userspace_probe_create(probe_location); 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_status = lttng_event_rule_userspace_probe_set_event_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); + condition = lttng_condition_on_event_create(event_rule); ok(condition, "Condition event rule object creation"); /* Register the trigger for condition. */ @@ -1529,7 +2071,7 @@ static void test_syscall_event_rule_notification( 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); + condition = lttng_condition_on_event_create(event_rule); ok(condition, "Condition syscall event rule object creation"); /* Register the trigger for condition. */ @@ -1624,7 +2166,7 @@ static void test_syscall_event_rule_notification_filter( ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK, "Setting filter: '%s'", filter_pattern); - condition = lttng_condition_event_rule_create(event_rule); + condition = lttng_condition_on_event_create(event_rule); ok(condition, "Condition event rule object creation"); /* Register the triggers for condition */ @@ -1681,6 +2223,241 @@ end: return; } +static int generate_capture_descr(struct lttng_condition *condition) +{ + int ret; + struct lttng_event_expr *expr = NULL; + unsigned int basic_field_size; + enum lttng_condition_status cond_status; + + basic_field_size = sizeof(test_capture_base_fields) / sizeof(*test_capture_base_fields); + for (int i = 0; i < basic_field_size; i++) { + + diag("Adding capture descriptor \"%s\"", test_capture_base_fields[i].field_name); + + switch (test_capture_base_fields[i].field_type) { + case FIELD_TYPE_PAYLOAD: + expr = lttng_event_expr_event_payload_field_create( + test_capture_base_fields[i].field_name); + break; + case FIELD_TYPE_CONTEXT: + expr = lttng_event_expr_channel_context_field_create( + test_capture_base_fields[i].field_name); + break; + case FIELD_TYPE_ARRAY_FIELD: + { + int nb_matches; + unsigned int index; + char field_name[256]; + struct lttng_event_expr *array_expr = NULL; + nb_matches = sscanf(test_capture_base_fields[i].field_name, + "%[^[][%u]", field_name, &index); + if (nb_matches != 2) { + ret = 1; + goto end; + } + array_expr = lttng_event_expr_event_payload_field_create( + field_name); + + expr = lttng_event_expr_array_field_element_create( + array_expr, index); + break; + } + case FIELD_TYPE_APP_CONTEXT: + fail("Application context not tested yet."); + default: + ret = 1; + goto end; + } + if (expr == NULL) { + fail("Creating capture expression"); + ret = -1; + goto end; + } + cond_status = lttng_condition_on_event_append_capture_descriptor( + condition, expr); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + fail("Appending capture_descriptor"); + ret = -1; + lttng_event_expr_destroy(expr); + goto end; + } + } + + ret = 0; + +end: + return ret; +} + +static int validator_notification_trigger_capture( + enum lttng_domain_type domain, + struct lttng_notification *notification, + const int iteration) +{ + int ret; + unsigned int capture_count, i; + enum lttng_evaluation_status evaluation_status; + enum lttng_event_field_value_status event_field_value_status; + const struct lttng_evaluation *evaluation; + const struct lttng_event_field_value *captured_fields; + bool at_least_one_error = false; + + evaluation = lttng_notification_get_evaluation(notification); + if (evaluation == NULL) { + fail("lttng_notification_get_evaluation"); + ret = 1; + goto end; + } + + /* TODO: it seems weird that lttng_evaluation_get_captured_values return + * INVALID if no capture were present. might be better to return + * something with more meaning. Another question is how we link the + * notion of capture and the descriptor from the perspective of a + * client. Is it really invalid to ask for captured value when there might + * not be any? + */ + evaluation_status = lttng_evaluation_get_captured_values(evaluation, &captured_fields); + if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) { + diag("lttng_evaluation_get_captured_values"); + ret = 1; + goto end; + } + + event_field_value_status = + lttng_event_field_value_array_get_length(captured_fields, + &capture_count); + if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + assert(0 && "get count of captured field"); + } + + for (i = 0; i < capture_count; i++) { + const struct lttng_event_field_value *captured_field = NULL; + validate_cb validate; + bool expected; + + diag("Validating capture \"%s\"", test_capture_base_fields[i].field_name); + event_field_value_status = lttng_event_field_value_array_get_element_at_index(captured_fields, i, &captured_field); + + switch(domain) { + case LTTNG_DOMAIN_UST: + expected = test_capture_base_fields[i].expected_ust; + break; + case LTTNG_DOMAIN_KERNEL: + expected = test_capture_base_fields[i].expected_kernel; + break; + default: + assert(0 && "Domain invalid for this test"); + } + + if (!expected) { + ok(event_field_value_status == LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE, "No payload captured"); + continue; + } + + if (domain == LTTNG_DOMAIN_UST) { + validate = test_capture_base_fields[i].validate_ust; + } else { + validate = test_capture_base_fields[i].validate_kernel; + } + + if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) { + const char *reason; + if (event_field_value_status == + LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) { + reason = "Expected a capture but it is unavailable"; + } else { + reason = "lttng_event_field_value_array_get_element_at_index"; + } + fail(reason); + ret = 1; + goto end; + } + diag("Captured field of type %s", + field_value_type_to_str( + lttng_event_field_value_get_type(captured_field))); + + assert(validate); + ret = validate(captured_field, iteration); + if (ret) { + at_least_one_error = true; + } + } + + ret = at_least_one_error; + +end: + return ret; +} + +static void test_tracepoint_event_rule_notification_capture( + enum lttng_domain_type domain_type) +{ + enum lttng_notification_channel_status nc_status; + + int i, ret; + 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, generate_capture_descr, &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++) { + struct lttng_notification *notification = get_next_notification( + notification_channel); + ok(notification, "Received notification"); + + /* Error */ + if (notification == NULL) { + goto end; + } + + ret = validator_notification_trigger_name(notification, trigger_name); + if (ret) { + lttng_notification_destroy(notification); + goto end; + } + + ret = validator_notification_trigger_capture(domain_type, notification, i); + if (ret) { + lttng_notification_destroy(notification); + goto end; + } + + lttng_notification_destroy(notification); + } + +end: + suspend_application(); + lttng_notification_channel_destroy(notification_channel); + lttng_unregister_trigger(trigger); + lttng_trigger_destroy(trigger); + lttng_condition_destroy(condition); + return; +} + int main(int argc, const char *argv[]) { int test_scenario; @@ -1798,7 +2575,7 @@ int main(int argc, const char *argv[]) } case 4: { - plan_tests(13); + plan_tests(12); /* Test cases that need the kernel tracer. */ assert(domain_type == LTTNG_DOMAIN_KERNEL); @@ -1831,7 +2608,7 @@ int main(int argc, const char *argv[]) { const char *testapp_path, *test_symbol_name; - plan_tests(13); + plan_tests(12); if (argc < 7) { fail("Missing parameter for tests to run %d", argc); @@ -1851,6 +2628,26 @@ int main(int argc, const char *argv[]) break; } + case 7: + { + switch(domain_type) { + case LTTNG_DOMAIN_UST: + plan_tests(222); + break; + case LTTNG_DOMAIN_KERNEL: + plan_tests(216); + break; + default: + assert(0); + } + + diag("Test tracepoint event rule notification captures for domain %s", + domain_type_string); + test_tracepoint_event_rule_notification_capture(domain_type); + + break; + } + default: abort(); } diff --git a/tests/regression/tools/notification/sessiond_testpoints.c b/tests/regression/tools/notification/sessiond_testpoints.c new file mode 100644 index 000000000..0f6e2083f --- /dev/null +++ b/tests/regression/tools/notification/sessiond_testpoints.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *pause_pipe_path; +static struct lttng_pipe *pause_pipe; +static int *notifier_notif_consumption_state;; + +int lttng_opt_verbose; +int lttng_opt_mi; +int lttng_opt_quiet; + +static +void __attribute__((destructor)) pause_pipe_fini(void) +{ + int ret; + + if (pause_pipe_path) { + ret = unlink(pause_pipe_path); + if (ret) { + PERROR("unlink pause pipe"); + } + } + + free(pause_pipe_path); + lttng_pipe_destroy(pause_pipe); +} + +/* + */ +int __testpoint_sessiond_thread_notification(void); +int __testpoint_sessiond_thread_notification(void) +{ + int ret = 0; + const char *pause_pipe_path_prefix; + + pause_pipe_path_prefix = lttng_secure_getenv( + "NOTIFIER_PAUSE_PIPE_PATH"); + if (!pause_pipe_path_prefix) { + ret = -1; + goto end; + } + + notifier_notif_consumption_state = dlsym(NULL, "notifier_consumption_paused"); + assert(notifier_notif_consumption_state); + + ret = asprintf(&pause_pipe_path, "%s", pause_pipe_path_prefix); + if (ret < 1) { + ERR("Failed to allocate pause pipe path"); + goto end; + } + + DBG("Creating pause pipe at %s", pause_pipe_path); + pause_pipe = lttng_pipe_named_open(pause_pipe_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, O_NONBLOCK); + if (!pause_pipe) { + ERR("Failed to create pause pipe at %s", pause_pipe_path); + ret = -1; + goto end; + } + + /* Only the read end of the pipe is useful to us. */ + ret = lttng_pipe_write_close(pause_pipe); +end: + return ret; +} + +int __testpoint_sessiond_handle_notifier_event_pipe(void); +int __testpoint_sessiond_handle_notifier_event_pipe(void) +{ + int ret = 0; + uint8_t value; + bool value_read = false; + + if (!pause_pipe) { + ret = -1; + goto end; + } + + /* Purge pipe and only consider the freshest value. */ + do { + ret = lttng_pipe_read(pause_pipe, &value, sizeof(value)); + if (ret == sizeof(value)) { + value_read = true; + } + } while (ret == sizeof(value)); + + ret = (errno == EAGAIN) ? 0 : -errno; + + if (value_read) { + *notifier_notif_consumption_state = !!value; + DBG("Message received on pause pipe: %s data consumption", + *notifier_notif_consumption_state ? "paused" : "resumed"); + } +end: + return ret; +} diff --git a/tests/regression/tools/notification/test_notification_kernel_capture b/tests/regression/tools/notification/test_notification_kernel_capture new file mode 100755 index 000000000..9b6e7ac57 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_kernel_capture @@ -0,0 +1,55 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +NUM_TESTS=104 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_basic_error_path +{ + kernel_event_generator_run_once_per_transition generate_filter_events \ + "$TESTAPP_STATE_PATH" 10 & + APP_PID=$! + + "$CURDIR/notification" 7 LTTNG_DOMAIN_KERNEL $APP_PID \ + "$TESTAPP_STATE_PATH" + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + + +if [ "$(id -u)" == "0" ]; then + validate_lttng_modules_present + + modprobe lttng-test + + start_lttng_sessiond_notap + + test_basic_error_path + + stop_lttng_sessiond_notap + rmmod lttng-test + +else + # Kernel tests are skipped. + plan_tests $NUM_TESTS + skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS +fi + +# Just in case cleanup +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_multi_app b/tests/regression/tools/notification/test_notification_multi_app index afac94d10..5d5427c9d 100755 --- a/tests/regression/tools/notification/test_notification_multi_app +++ b/tests/regression/tools/notification/test_notification_multi_app @@ -54,8 +54,9 @@ function start_client { local buffer_usage_threshold_type=$6 local buffer_usage_threshold_value=$7 local nr_expected_notification=$8 + local use_action_group=$9 - ${CURDIR}/base_client ${session_name} ${channel_name} ${domain_type} ${buffer_usage_type} ${buffer_usage_threshold_type} ${buffer_usage_threshold_value} ${nr_expected_notification} > ${output_file} & + ${CURDIR}/base_client ${session_name} ${channel_name} ${domain_type} ${buffer_usage_type} ${buffer_usage_threshold_type} ${buffer_usage_threshold_value} ${nr_expected_notification} ${use_action_group} > ${output_file} & pid=$! app_pids+=("$pid") @@ -177,8 +178,8 @@ function test_multi_app () for (( i = 0; i < $nr_client_app; i++ )); do low_app_output_file=$output_dir/${low_output_file_pattern}${i} high_app_output_file=$output_dir/${high_output_file_pattern}${i} - start_client $low_app_output_file $SESSION_NAME $CHANNEL_NAME $domain_string LOW RATIO 0.0 $nr_notification_expected - start_client $high_app_output_file $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 $nr_notification_expected + start_client $low_app_output_file $SESSION_NAME $CHANNEL_NAME $domain_string LOW RATIO 0.0 $nr_notification_expected $(( $i % 2)) + start_client $high_app_output_file $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 $nr_notification_expected $(( $i % 2)) done wait_for_message $output_dir "${low_output_file_pattern}" "sync: ready" @@ -362,7 +363,7 @@ function test_on_register_evaluation () high_app_output_file=${high_output_file_pattern}.first_receiver high_app_output_path=$output_dir/${high_app_output_file} - start_client $high_app_output_path $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 1 + start_client $high_app_output_path $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 1 0 wait_for_message $output_dir "${high_app_output_file}" "sync: ready" @@ -379,7 +380,7 @@ function test_on_register_evaluation () # notification on subscription high_app_output_file=${high_output_file_pattern}.second_receiver high_app_output_path=$output_dir/${high_app_output_file} - start_client $high_app_output_path $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 1 + start_client $high_app_output_path $SESSION_NAME $CHANNEL_NAME $domain_string HIGH RATIO 0.420 1 0 wait_for_message $output_dir "${high_app_output_file}" "sync: ready" wait_for_message $output_dir "${high_app_output_file}" "notification: high 0" diff --git a/tests/regression/tools/notification/test_notification_notifier_discarded_count b/tests/regression/tools/notification/test_notification_notifier_discarded_count new file mode 100755 index 000000000..620a8a861 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_notifier_discarded_count @@ -0,0 +1,254 @@ +#!/bin/bash +# +# Copyright (C) 2020 Francis Deslauriers +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +TESTAPP_PATH="$TESTDIR/utils/testapp" +TESTAPP_NAME="gen-ust-events" +TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME" + +TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n") +TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX") +TESTPOINT=$(readlink -f "${CURDIR}/.libs/libpause_sessiond.so") + +SH_TAP=1 + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" +FULL_LTTNG_SESSIOND_BIN="${TESTDIR}/../src/bin/lttng-sessiond/lttng-sessiond" + +UST_NUM_TESTS=16 +KERNEL_NUM_TESTS=15 +NUM_TESTS=$(($UST_NUM_TESTS + $KERNEL_NUM_TESTS)) + +plan_tests $NUM_TESTS + +function test_kernel_notifier_discarded_count +{ + local sessiond_pipe=() + local trigger_name="my_trigger" + local list_triggers_stdout=$(mktemp -t list_triggers_stdout.XXXXXX) + + # Used on sessiond launch. + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + NOTIFIER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + diag "Kernel event notifer error counter" + + start_lttng_sessiond_notap + + # This is needed since the testpoint create a pipe with the sessiond + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + sessiond_pipe+=("$f") + done + + lttng_add_trigger_ok "$trigger_name" \ + --condition on-event --kernel lttng_test_filter_event \ + --action notify + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + # Stop consumption of notifier tracer notifications. + echo -n 1 > $sessiond_pipe + + # The notifier ring buffer configuration is currently made of 16 4096 + # bytes subbuffers. Each kernel notification is at least 42 bytes long. + # To fill it, we need to generate (16 * 4096)/42 = 1561 notifications. + # That number is a bit larger than what we need since some of the space + # is lost in subbuffer boundaries. + echo -n "200000" > /proc/lttng-test-filter-event + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. To avoid + # false positive. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded" + ok $? "Tracer notification discarded line printed" + + # Confirm that the number of tracer notifications discarded is not zero. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + isnt $? 0 "Discarded tracer notification number non-zero as expected" + + lttng_remove_trigger_ok "$trigger_name" + + # Confirm that no notifier is enabled. + list_triggers_line_count=$("$FULL_LTTNG_BIN" list-triggers | wc -l) + is "$list_triggers_line_count" "0" "No \`on-event\` kernel notifier enabled as expected" + + # Enable another notifier and list it to confirm the counter was cleared. + lttng_add_trigger_ok "$trigger_name" \ + --condition on-event --kernel lttng_test_filter_event \ + --action notify + + # Confirm that the discarded notification line is present. + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + lttng_remove_trigger_ok "$trigger_name" + + stop_lttng_sessiond_notap + + unset LTTNG_SESSIOND_ENV_VARS + + rm -f "$list_triggers_stdout" +} + +function test_kernel_notifier_discarded_count_max_bucket +{ + start_lttng_sessiond "" "--event-notifier-error-number-of-bucket=3" + + diag "Kernel event notifer error counter bucket limit" + for i in $(seq 3); do + lttng_add_trigger_ok "$i" \ + --condition on-event --kernel my_event_that_doesnt_need_to_really_exist_$i \ + --action notify + done + + for i in $(seq 4 5); do + lttng_add_trigger_fail "$i" \ + --condition on-event --kernel my_event_that_doesnt_need_to_really_exist_$i \ + --action notify + done + + stop_lttng_sessiond_notap +} + +function test_ust_notifier_discarded_count +{ + local sessiond_pipe=() + local trigger_name="my_trigger" + local list_triggers_stdout=$(mktemp -t list_triggers_stdout.XXXXXX) + local NR_ITER=2000 + local NR_USEC_WAIT=0 + + + diag "UST event notifer error counter" + # Used on sessiond launch. + LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \ + NOTIFIER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \ + LD_PRELOAD=${TESTPOINT}" + + start_lttng_sessiond_notap + + # This is needed since the testpoint create a pipe with the sessiond + # type suffixed. + for f in "$TESTPOINT_BASE_PATH"*; do + sessiond_pipe+=("$f") + done + + lttng_add_trigger_ok "$trigger_name" \ + --condition on-event --userspace tp:tptest \ + --action notify + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + # Stop consumption of notifier tracer notifications. + echo -n 1 > $sessiond_pipe + + # The notifier ring buffer configuration is currently made of 16 4096 + # bytes subbuffers. Each userspace notification is at least 42 bytes long. + # To fill it, we need to generate (16 * 4096)/42 = 1561 notifications. + # That number is a bit larger than what we need since some of the space + # is lost in subbuffer boundaries. + $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT + ok $? "Generating $NR_ITER tracer notifications" + + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + + # Confirm that the discarded notification line is present. To avoid + # false positive. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded" + ok $? "Tracer notification discarded line printed" + + # Confirm that the number of tracer notifications discarded is not zero. + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + isnt $? 0 "Discarded tracer notification number non-zero as expected" + + # Remove the notifier. + lttng_remove_trigger_ok "$trigger_name" + + # Confirm that no notifier is enabled. + list_triggers_line_count=$("$FULL_LTTNG_BIN" list-triggers | wc -l) + is "$list_triggers_line_count" "0" "No \`on-event\` userspace notifier enabled as expected" + + # Enable another notifier and list it to confirm the counter was cleared. + lttng_add_trigger_ok "$trigger_name" \ + --condition on-event --userspace tp:tptest \ + --action notify + + # Confirm that the discarded notification line is present. + "$FULL_LTTNG_BIN" list-triggers > "$list_triggers_stdout" + cat "$list_triggers_stdout" | grep --quiet "tracer notifications discarded: 0" + ok $? "No discarded tracer notification" + + lttng_remove_trigger_ok "$trigger_name" + + stop_lttng_sessiond_notap + + unset LTTNG_SESSIOND_ENV_VARS + + rm -f "$list_triggers_stdout" +} +function test_ust_notifier_discarded_count_max_bucket +{ + start_lttng_sessiond "" "--event-notifier-error-number-of-bucket=3" + + diag "UST event notifer error counter bucket limit" + for i in $(seq 3); do + lttng_add_trigger_ok "$i" \ + --condition on-event --userspace my_event_that_doesnt_need_to_really_exist_$i \ + --action notify + done + + for i in $(seq 4 5); do + lttng_add_trigger_fail "$i" \ + --condition on-event --userspace my_event_that_doesnt_need_to_really_exist_$i \ + --action notify + done + + stop_lttng_sessiond_notap +} + +test_ust_notifier_discarded_count +test_ust_notifier_discarded_count_max_bucket + +if [ "$(id -u)" == "0" ]; then + + validate_lttng_modules_present + + modprobe lttng-test + + test_kernel_notifier_discarded_count + + test_kernel_notifier_discarded_count_max_bucket + + modprobe --remove lttng-test + + rm -rf "${sessiond_pipe[@]}" 2> /dev/null +else + # Kernel tests are skipped. + skip 0 "Root access is needed. Skipping all kernel notification tests." $KERNEL_NUM_TESTS +fi + +rm -rf "$TMPDIR" diff --git a/tests/regression/tools/notification/test_notification_ust_capture b/tests/regression/tools/notification/test_notification_ust_capture new file mode 100755 index 000000000..5003c2894 --- /dev/null +++ b/tests/regression/tools/notification/test_notification_ust_capture @@ -0,0 +1,40 @@ +#!/bin/bash +# +# Copyright (C) 2017 Jonathan Rajotte-Julien +# +# SPDX-License-Identifier: LGPL-2.1-only + +CURDIR=$(dirname "$0")/ +TESTDIR=$CURDIR/../../../ + +TMPDIR=$(mktemp -d) + +TESTAPP_PATH="$TESTDIR/utils/testapp" + +GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events" +GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME" + +TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX") + +# shellcheck source=../../../utils/utils.sh +source "$TESTDIR/utils/utils.sh" +# shellcheck source=./util_event_generator.sh +source "$CURDIR/util_event_generator.sh" + +function test_basic_error_path +{ + ust_event_generator_run_once_per_transition \ + "$GEN_UST_EVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" 3 10 & + APP_PID=$! + + "$CURDIR/notification" 7 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH" + + kill -SIGUSR2 $APP_PID + wait $APP_PID 2> /dev/null +} + +start_lttng_sessiond_notap + +test_basic_error_path + +stop_lttng_sessiond_notap diff --git a/tests/regression/tools/save-load/Makefile.am b/tests/regression/tools/save-load/Makefile.am index 3361744e9..469102f8e 100644 --- a/tests/regression/tools/save-load/Makefile.am +++ b/tests/regression/tools/save-load/Makefile.am @@ -3,7 +3,8 @@ noinst_SCRIPTS = test_save test_load test_autoload EXTRA_DIST = $(noinst_SCRIPTS) load-42.lttng load-42-complex.lttng \ load-42-trackers.lttng tracker_legacy_none.lttng \ - tracker_legacy_all.lttng tracker_legacy_selective.lttng + tracker_legacy_all.lttng tracker_legacy_selective.lttng \ + load-42-maps.lttng SUBDIRS = configuration diff --git a/tests/regression/tools/save-load/load-42-maps.lttng b/tests/regression/tools/save-load/load-42-maps.lttng new file mode 100644 index 000000000..82aaecd50 --- /dev/null +++ b/tests/regression/tools/save-load/load-42-maps.lttng @@ -0,0 +1,67 @@ + + + + load-42-maps + + + UST + PER_UID + + + + ze-map-32 + true + 32 + OVERFLOW + false + + + 4096 + + + + + ze-map-64 + true + 64 + OVERFLOW + false + + + 4096 + + + + + + + + JUL + PER_UID + + + + + LOG4J + PER_UID + + + + + PYTHON + PER_UID + + + + + false + + + true + + /tmp/lttng/load-42-maps + + + + + diff --git a/tests/regression/tools/save-load/test_load b/tests/regression/tools/save-load/test_load index 4f18dec2d..1870bf0b4 100755 --- a/tests/regression/tools/save-load/test_load +++ b/tests/regression/tools/save-load/test_load @@ -16,7 +16,7 @@ EVENT_NAME="tp:tptest" DIR=$(readlink -f $TESTDIR) -NUM_TESTS=75 +NUM_TESTS=78 source $TESTDIR/utils/utils.sh @@ -101,6 +101,7 @@ function test_all_load() destroy_lttng_session_ok $SESSION_NAME destroy_lttng_session_ok "$SESSION_NAME-complex" destroy_lttng_session_ok "$SESSION_NAME-trackers" + destroy_lttng_session_ok "$SESSION_NAME-maps" destroy_lttng_session_ok "tracker_legacy_all" destroy_lttng_session_ok "tracker_legacy_none" destroy_lttng_session_ok "tracker_legacy_selective" @@ -164,6 +165,25 @@ function test_trackers() rm -f ${mi_output_file} } +function test_maps() +{ + diag "Test maps loading" + + lttng_load_ok "-i $CURDIR/$SESSION_NAME-maps.lttng" + + local mi_output_file=$(mktemp) + if [ $? -ne 0 ]; then + break; + fi + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN --mi XML list "$SESSION_NAME-maps" > $mi_output_file + + # TODO: once map information is in the MI, do some mad testing with sick xpath expressions. + + destroy_lttng_session_ok "$SESSION_NAME-maps" + rm -f ${mi_output_file} +} + function test_override_url_normal() { local local_url_override="file:///tmp/override/to/here" @@ -354,6 +374,7 @@ TESTS=( test_all_load test_overwrite test_trackers + test_maps test_override_session_name test_override_url_normal test_override_url_snapshot diff --git a/tests/regression/tools/save-load/test_save b/tests/regression/tools/save-load/test_save index 0f3e596a0..c4bc515e3 100755 --- a/tests/regression/tools/save-load/test_save +++ b/tests/regression/tools/save-load/test_save @@ -12,13 +12,17 @@ TESTDIR=$CURDIR/../../../ SESSION_NAME="save-42" CHANNEL_NAME="chan-save" EVENT_NAME="tp:tptest" +MAP_32_NAME="ze-map-32" +MAP_64_NAME="ze-map-64" DIR=$(readlink -f $TESTDIR) -NUM_TESTS=41 +NUM_TESTS=46 source $TESTDIR/utils/utils.sh +FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" + # MUST set TESTDIR before calling those functions plan_tests $NUM_TESTS @@ -54,12 +58,25 @@ function test_basic_save() create_lttng_session_ok $SESSION_NAME $TRACE_PATH enable_ust_lttng_channel_ok $SESSION_NAME $CHANNEL_NAME enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME $CHANNEL_NAME + lttng_add_map_ok $MAP_32_NAME $SESSION_NAME -u 32 "" "" + lttng_add_map_ok $MAP_64_NAME $SESSION_NAME -u 64 "" "" lttng_track_ok "-p 666 -u -s $SESSION_NAME" lttng_save $SESSION_NAME "-o $TRACE_PATH" is_session_saved $TRACE_PATH $SESSION_NAME + local session_file="$TRACE_PATH/$SESSION_NAME.lttng" + + local map_count=$(xmllint --xpath 'count(/sessions/session[1]/domains/domain[./type = "UST"]/maps/map)' "$session_file") + is "$map_count" 2 + + local bitness_32=$(xmllint --xpath '/sessions/session[1]/domains/domain[./type = "UST"]/maps/map[./name = "ze-map-32"]/bitness/text()' "$session_file") + is "$bitness_32" 32 + + local bitness_64=$(xmllint --xpath '/sessions/session[1]/domains/domain[./type = "UST"]/maps/map[./name = "ze-map-64"]/bitness/text()' "$session_file") + is "$bitness_64" 64 + destroy_lttng_session_ok $SESSION_NAME } diff --git a/tests/regression/tools/trigger/start-stop/test_start_stop b/tests/regression/tools/trigger/start-stop/test_start_stop index cecea3b19..a6fc7bd02 100755 --- a/tests/regression/tools/trigger/start-stop/test_start_stop +++ b/tests/regression/tools/trigger/start-stop/test_start_stop @@ -22,47 +22,6 @@ NUM_TESTS=18 NR_ITER=5 NR_USEC_WAIT=5 -function lttng_add_trigger_ust() -{ - local expected_to_fail="$1" - local trigger_name="$2" - shift 2 - - "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" "$@" 1> /dev/null 2> /dev/null - ret=$? - if [[ $expected_to_fail -eq "1" ]]; then - test "$ret" -ne "0" - ok $? "Add trigger $trigger_name failed as expected" - else - ok $ret "Add trigger $trigger_name" - fi -} - -function lttng_remove_trigger_ust() -{ - local expected_to_fail="$1" - local trigger_name="$2" - - "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" 1> /dev/null 2> /dev/null - ret=$? - if [[ $expected_to_fail -eq "1" ]]; then - test "$ret" -ne "0" - ok $? "Remove trigger $trigger_name failed as expected" - else - ok $ret "Remove trigger $trigger_name" - fi -} - -function lttng_add_trigger_ust_ok() -{ - lttng_add_trigger_ust 0 "$@" -} - -function lttng_remove_trigger_ust_ok() -{ - lttng_remove_trigger_ust 0 "$@" -} - function lttng_session_is_active() { local SESSION_NAME="$1" @@ -96,7 +55,7 @@ function test_start_session_action() # Add `start-session` action to an event-rule condition _followed_ by # a `notify` action. - lttng_add_trigger_ust_ok \ + lttng_add_trigger_ok \ $TRIGGER_NAME \ --condition on-event -u "tp:tptest" \ --action start-session $SESSION_NAME \ @@ -124,7 +83,7 @@ function test_start_session_action() lttng_session_is_active $SESSION_NAME # Tearing down. - lttng_remove_trigger_ust_ok $TRIGGER_NAME + lttng_remove_trigger_ok $TRIGGER_NAME stop_lttng_tracing_ok $SESSION_NAME destroy_lttng_session_ok $SESSION_NAME @@ -150,7 +109,7 @@ function test_stop_session_action() # Add `stop-session` action to an event-rule condition _followed_ by # a `notify` action. - lttng_add_trigger_ust_ok \ + lttng_add_trigger_ok \ $TRIGGER_NAME \ --condition on-event -u "tp:tptest" \ --action stop-session $SESSION_NAME \ @@ -178,7 +137,7 @@ function test_stop_session_action() lttng_session_is_inactive $SESSION_NAME # Tearing down. - lttng_remove_trigger_ust_ok $TRIGGER_NAME + lttng_remove_trigger_ok $TRIGGER_NAME destroy_lttng_session_ok $SESSION_NAME rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH" diff --git a/tests/regression/tools/trigger/test_add_trigger_cli b/tests/regression/tools/trigger/test_add_trigger_cli index db325a90d..b3a51dd47 100755 --- a/tests/regression/tools/trigger/test_add_trigger_cli +++ b/tests/regression/tools/trigger/test_add_trigger_cli @@ -101,20 +101,20 @@ test_success "--fire-every" \ skip $ist_root "non-root user: skipping kprobe tests" 9 || { test_success "--condition on-event probe by symbol" \ - --condition on-event -k --probe=lttng_channel_enable my_channel_enable \ + --condition on-event -k --probe=lttng_event_container_enable my_channel_enable \ --action notify - channel_enable_addr=$(grep ' t lttng_channel_enable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') - channel_disable_addr=$(grep ' t lttng_channel_disable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') + channel_enable_addr=$(grep 'lttng_event_container_enable' /proc/kallsyms | cut -f 1 -d ' ') + channel_disable_addr=$(grep 'lttng_event_container_disable' /proc/kallsyms | cut -f 1 -d ' ') # We need to find a valid offset. base_symbol="" offset=0 if [[ 0x$channel_enable_addr -lt 0x$channel_disable_addr ]]; then - base_symbol="lttng_channel_enable" + base_symbol="lttng_event_container_enable" offset=$(( 0x$channel_disable_addr - 0x$channel_enable_addr )) else - base_symbol="lttng_channel_disable" + base_symbol="lttng_event_container_disable" offset=$(( 0x$channel_enable_addr - 0x$channel_disable_addr )) fi diff --git a/tests/regression/tools/trigger/test_list_triggers_cli b/tests/regression/tools/trigger/test_list_triggers_cli index e09d7cdc6..88b2e87f3 100755 --- a/tests/regression/tools/trigger/test_list_triggers_cli +++ b/tests/regression/tools/trigger/test_list_triggers_cli @@ -23,7 +23,7 @@ TESTDIR="$CURDIR/../../.." # shellcheck source=../../../utils/utils.sh source "$TESTDIR/utils/utils.sh" -plan_tests 40 +plan_tests 54 FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}" @@ -41,12 +41,6 @@ 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" @@ -68,9 +62,9 @@ test_top_level_options () 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 + lttng_add_trigger_ok "hello" --condition on-event -u test-id --action notify + lttng_add_trigger_ok "T0" --fire-once-after 123 --condition on-event -u test-fire-once-after --action notify + lttng_add_trigger_ok "T1" --fire-every 124 --condition on-event -u test-fire-every --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: T0 @@ -78,6 +72,7 @@ test_top_level_options () firing policy: once after 123 occurences condition: event rule hit rule: test-fire-once-after (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify - id: T1 @@ -85,12 +80,14 @@ test_top_level_options () firing policy: after every 124 occurences condition: event rule hit rule: test-fire-every (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify - id: hello user id: ${uid} condition: event rule hit rule: test-id (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify EOF @@ -105,41 +102,88 @@ 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 + lttng_add_trigger_ok "C" --condition on-event -u -a --action notify + lttng_add_trigger_ok "A" --condition on-event aaa -u --filter 'p == 2' --action notify + lttng_add_trigger_ok "D" --condition on-event 'hello*' -u -x 'hello2,hello3,hello4' --action notify + lttng_add_trigger_ok "B" --condition on-event -u gerboise --loglevel INFO --action notify + lttng_add_trigger_ok "E" --condition on-event -u lemming --loglevel-only WARNING --action notify + lttng_add_trigger_ok "F" --condition on-event -u capture-payload-field --capture a --action notify + lttng_add_trigger_ok "G" --condition on-event -u capture-array --capture 'a[2]' --capture '$ctx.tourlou[18]' --action notify + lttng_add_trigger_ok "H" --condition on-event -u capture-chan-ctx --capture '$ctx.vpid' --action notify + lttng_add_trigger_ok "I" --condition on-event -u capture-app-ctx --capture '$app.iga:active_clients' --action notify + cat > "${tmp_expected_stdout}" <<- EOF - - id: ABC + - id: A user id: ${uid} condition: event rule hit rule: aaa (type: tracepoint, domain: ust, filter: p == 2) + tracer notifications discarded: 0 actions: notify - - id: BCD + - id: B user id: ${uid} condition: event rule hit rule: gerboise (type: tracepoint, domain: ust, log level <= TRACE_INFO) + tracer notifications discarded: 0 actions: notify - - id: T0 + - id: C user id: ${uid} condition: event rule hit rule: * (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify - - id: T1 + - id: D user id: ${uid} condition: event rule hit rule: hello* (type: tracepoint, domain: ust, exclusions: hello2,hello3,hello4) + tracer notifications discarded: 0 actions: notify - - id: T2 + - id: E user id: ${uid} condition: event rule hit rule: lemming (type: tracepoint, domain: ust, log level == TRACE_WARNING) + tracer notifications discarded: 0 + actions: + notify + - id: F + user id: ${uid} + condition: event rule hit + rule: capture-payload-field (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + captures: + - a + actions: + notify + - id: G + user id: ${uid} + condition: event rule hit + rule: capture-array (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + captures: + - a[2] + - \$ctx.tourlou[18] + actions: + notify + - id: H + user id: ${uid} + condition: event rule hit + rule: capture-chan-ctx (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + captures: + - \$ctx.vpid + actions: + notify + - id: I + user id: ${uid} + condition: event rule hit + rule: capture-app-ctx (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + captures: + - \$app.iga:active_clients actions: notify EOF @@ -157,43 +201,46 @@ test_on_event_probe () # shellcheck disable=SC2119 start_lttng_sessiond_notap - channel_enable_addr=$(grep ' t lttng_channel_enable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') - channel_disable_addr=$(grep ' t lttng_channel_disable\s\[lttng_tracer\]$' /proc/kallsyms | cut -f 1 -d ' ') + channel_enable_addr=$(grep 'lttng_event_container_enable' /proc/kallsyms | cut -f 1 -d ' ') + channel_disable_addr=$(grep 'lttng_event_container_disable' /proc/kallsyms | cut -f 1 -d ' ') # We need to find a valid offset. base_symbol="" offset=0 if [[ 0x$channel_enable_addr -lt 0x$channel_disable_addr ]]; then - base_symbol="lttng_channel_enable" + base_symbol="lttng_event_container_enable" offset=$(( 0x$channel_disable_addr - 0x$channel_enable_addr )) else - base_symbol="lttng_channel_disable" + base_symbol="lttng_event_container_disable" offset=$(( 0x$channel_enable_addr - 0x$channel_disable_addr )) fi offset_hex="0x$(printf '%x' $offset)" - add_trigger --condition on-event -k --probe=lttng_channel_enable my_channel_enable --action notify - add_trigger --condition on-event -k --probe="${base_symbol}+${offset_hex}" my_channel_enable --action notify - add_trigger --condition on-event -k --probe="0x${channel_enable_addr}" my_channel_enable --action notify + lttng_add_trigger_ok "T0" --condition on-event -k --probe=lttng_event_container_enable my_channel_enable --action notify + lttng_add_trigger_ok "T1" --condition on-event -k --probe="${base_symbol}+${offset_hex}" my_channel_enable --action notify + lttng_add_trigger_ok "T2" --condition on-event -k --probe="0x${channel_enable_addr}" my_channel_enable --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: T0 user id: ${uid} condition: event rule hit - rule: my_channel_enable (type: probe, location: lttng_channel_enable) + rule: my_channel_enable (type: probe, location: lttng_event_container_enable) + tracer notifications discarded: 0 actions: notify - id: T1 user id: ${uid} condition: event rule hit rule: my_channel_enable (type: probe, location: ${base_symbol}+${offset_hex}) + tracer notifications discarded: 0 actions: notify - id: T2 user id: ${uid} condition: event rule hit rule: my_channel_enable (type: probe, location: 0x${channel_enable_addr}) + tracer notifications discarded: 0 actions: notify EOF @@ -208,13 +255,14 @@ 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 + lttng_add_trigger_ok "T0" --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: T0 user id: ${uid} condition: event rule hit rule: ma-probe (type: userspace probe, location: ${uprobe_elf_binary}:test_function) + tracer notifications discarded: 0 actions: notify EOF @@ -229,20 +277,22 @@ 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 + lttng_add_trigger_ok "T0" --condition on-event -k --syscall open --action notify + lttng_add_trigger_ok "T1" --condition on-event -k --syscall ptrace --filter 'a > 2' --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: T0 user id: ${uid} condition: event rule hit rule: open (type: syscall) + tracer notifications discarded: 0 actions: notify - id: T1 user id: ${uid} condition: event rule hit rule: ptrace (type: syscall, filter: a > 2) + tracer notifications discarded: 0 actions: notify EOF @@ -256,14 +306,14 @@ 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 --path /some/path - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --url file:///some/other/path - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --url net://1.2.3.4 - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --url 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 --path /some/path --max-size=1234 - add_trigger --condition on-event -u some-event --action snapshot-session ze-session --path /some/path --name=meh + lttng_add_trigger_ok "T0" --condition on-event -u some-event --action snapshot-session ze-session + lttng_add_trigger_ok "T1" --condition on-event -u some-event --action snapshot-session ze-session --path /some/path + lttng_add_trigger_ok "T2" --condition on-event -u some-event --action snapshot-session ze-session --url file:///some/other/path + lttng_add_trigger_ok "T3" --condition on-event -u some-event --action snapshot-session ze-session --url net://1.2.3.4 + lttng_add_trigger_ok "T4" --condition on-event -u some-event --action snapshot-session ze-session --url net://1.2.3.4:1234:1235 + lttng_add_trigger_ok "T5" --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 + lttng_add_trigger_ok "T6" --condition on-event -u some-event --action snapshot-session ze-session --path /some/path --max-size=1234 + lttng_add_trigger_ok "T7" --condition on-event -u some-event --action snapshot-session ze-session --path /some/path --name=meh cat > "${tmp_expected_stdout}" <<- EOF @@ -271,48 +321,56 @@ test_snapshot_action () user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\` - id: T1 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, path: /some/path - id: T2 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, path: /some/other/path - id: T3 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, url: net://1.2.3.4 - id: T4 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, url: net://1.2.3.4:1234:1235 - id: T5 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, control url: tcp://1.2.3.4:1111, data url: tcp://1.2.3.4:1112 - id: T6 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, path: /some/path, max size: 1234 - id: T7 user id: ${uid} condition: event rule hit rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: snapshot session \`ze-session\`, path: /some/path, name: meh EOF @@ -322,12 +380,90 @@ test_snapshot_action () stop_lttng_sessiond_notap } +test_on_event_kernel_incr_value () +{ + local session="session_doesnt_need_to_exist" + local map="map_doesnt_need_to_exist" + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + lttng_add_trigger_ok "T0" --condition on-event -k some-event --action incr-value -s $session -m $map --key string + lttng_add_trigger_ok "T1" --condition on-event -k some-event2 --action incr-value -s $session -m $map --key prefix_$\{EVENT_NAME\} + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T0 + user id: ${uid} + condition: event rule hit + rule: some-event (type: tracepoint, domain: kernel) + tracer notifications discarded: 0 + actions: + increment value: + session: \`${session}\` + map: \`${map}\` + key: \`string\` + - id: T1 + user id: ${uid} + condition: event rule hit + rule: some-event2 (type: tracepoint, domain: kernel) + tracer notifications discarded: 0 + actions: + increment value: + session: \`${session}\` + map: \`${map}\` + key: \`prefix_\${EVENT_NAME}\` + EOF + + list_triggers "on-event kernel incr-value" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + +test_on_event_ust_incr_value () +{ + local session="session_doesnt_need_to_exist" + local map="map_doesnt_need_to_exist" + # shellcheck disable=SC2119 + start_lttng_sessiond_notap + + lttng_add_trigger_ok "T0" --condition on-event -u some-event --action incr-value -s $session -m $map --key string + lttng_add_trigger_ok "T1" --condition on-event -u some-event2 --action incr-value -s $session -m $map --key prefix_$\{EVENT_NAME\} + + cat > "${tmp_expected_stdout}" <<- EOF + - id: T0 + user id: ${uid} + condition: event rule hit + rule: some-event (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + actions: + increment value: + session: \`${session}\` + map: \`${map}\` + key: \`string\` + - id: T1 + user id: ${uid} + condition: event rule hit + rule: some-event2 (type: tracepoint, domain: ust) + tracer notifications discarded: 0 + actions: + increment value: + session: \`${session}\` + map: \`${map}\` + key: \`prefix_\${EVENT_NAME}\` + EOF + + list_triggers "on-event UST incr-value" "${tmp_expected_stdout}" + + stop_lttng_sessiond_notap +} + test_top_level_options test_on_event_tracepoint skip $ist_root "non-root user: skipping kprobe tests" 6 || test_on_event_probe skip $ist_root "non-root user: skipping uprobe tests" 4 || test_on_event_userspace_probe skip $ist_root "non-root user: skipping syscall tests" 5 || test_on_event_syscall test_snapshot_action +skip $ist_root "non-root user: skipping incr-value tests" 5 || test_on_event_kernel_incr_value +test_on_event_ust_incr_value # Cleanup rm -f "${tmp_stdout}" diff --git a/tests/regression/tools/trigger/test_remove_trigger_cli b/tests/regression/tools/trigger/test_remove_trigger_cli index 168227a4a..582cce7ef 100755 --- a/tests/regression/tools/trigger/test_remove_trigger_cli +++ b/tests/regression/tools/trigger/test_remove_trigger_cli @@ -34,12 +34,6 @@ tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX) uid=$(id --user) gid=$(id --group) -function add_trigger () -{ - "${FULL_LTTNG_BIN}" add-trigger "$@" - ok $? "add trigger \`$*\`: exit code is 0" -} - function list_triggers () { local test_name="$1" @@ -74,20 +68,22 @@ function remove_trigger () 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 +lttng_add_trigger_ok "ABC" --condition on-event aaa -u --filter 'p == 2' --action notify +lttng_add_trigger_ok "DEF" --condition on-event -u -a --action notify cat > "${tmp_expected_stdout}" <<- EOF - id: ABC user id: ${uid} condition: event rule hit rule: aaa (type: tracepoint, domain: ust, filter: p == 2) + tracer notifications discarded: 0 actions: notify -- id: T0 +- id: DEF user id: ${uid} condition: event rule hit rule: * (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify EOF @@ -96,16 +92,17 @@ list_triggers "two triggers left" "${tmp_expected_stdout}" remove_trigger "ABC" cat > "${tmp_expected_stdout}" <<- EOF -- id: T0 +- id: DEF user id: ${uid} condition: event rule hit rule: * (type: tracepoint, domain: ust) + tracer notifications discarded: 0 actions: notify EOF list_triggers "one trigger left" "${tmp_expected_stdout}" -remove_trigger "T0" +remove_trigger "DEF" list_triggers "no triggers left" "/dev/null" diff --git a/tests/regression/tools/trigger/utils/notification-client.c b/tests/regression/tools/trigger/utils/notification-client.c index aecc478bd..47f1d67aa 100644 --- a/tests/regression/tools/trigger/utils/notification-client.c +++ b/tests/regression/tools/trigger/utils/notification-client.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include "utils.h" @@ -69,7 +69,7 @@ static bool is_expected_trigger_name(const char *expected_trigger_name, case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + case LTTNG_CONDITION_TYPE_ON_EVENT: { const char *trigger_name; enum lttng_evaluation_status evaluation_status; diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 01b298152..a88fe0b6c 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -8,27 +8,33 @@ LOG_DRIVER_FLAGS='--merge' LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ $(top_srcdir)/config/tap-driver.sh -TESTS = test_kernel_data \ - test_session \ - test_uri \ - test_utils_parse_size_suffix \ - test_utils_parse_time_suffix \ - test_utils_expand_path \ - test_utils_compat_poll \ - test_utils_compat_pthread \ - test_string_utils \ - test_notification \ - test_event_rule \ - test_directory_handle \ - test_relayd_backward_compat_group_by_session \ +TESTS = \ ini_config/test_ini_config \ - test_fd_tracker \ - test_uuid \ + test_action \ test_buffer_view \ + test_directory_handle \ test_event_expr_to_bytecode \ + test_event_rule \ + test_fd_tracker \ + test_kernel_data \ + test_kernel_probe \ + test_log_level_rule \ + test_map \ + test_map_key \ + test_map_query \ + test_notification \ test_payload \ + test_relayd_backward_compat_group_by_session \ + test_session \ + test_string_utils \ test_unix_socket \ - test_kernel_probe + test_uri \ + test_utils_compat_poll \ + test_utils_compat_pthread \ + test_utils_expand_path \ + test_utils_parse_size_suffix \ + test_utils_parse_time_suffix \ + test_uuid LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la @@ -41,19 +47,33 @@ LIBRELAYD=$(top_builddir)/src/common/relayd/librelayd.la LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la # Define test programs -noinst_PROGRAMS = test_uri test_session test_kernel_data \ - test_utils_parse_size_suffix test_utils_parse_time_suffix \ - test_utils_expand_path test_utils_compat_poll test_utils_compat_pthread \ - test_string_utils test_notification test_directory_handle \ - test_relayd_backward_compat_group_by_session \ - test_fd_tracker test_uuid \ - test_buffer_view \ - test_payload \ - test_unix_socket \ - test_kernel_probe \ - test_condition \ - test_event_expr_to_bytecode \ - test_event_rule +noinst_PROGRAMS = \ + test_action \ + test_buffer_view \ + test_condition \ + test_directory_handle \ + test_event_expr_to_bytecode \ + test_event_rule \ + test_fd_tracker \ + test_kernel_data \ + test_kernel_probe \ + test_log_level_rule \ + test_map \ + test_map_key \ + test_map_query \ + test_notification \ + test_payload \ + test_relayd_backward_compat_group_by_session \ + test_session \ + test_string_utils \ + test_unix_socket \ + test_uri \ + test_utils_compat_poll \ + test_utils_compat_pthread \ + test_utils_expand_path \ + test_utils_parse_size_suffix \ + test_utils_parse_time_suffix \ + test_uuid if HAVE_LIBLTTNG_UST_CTL noinst_PROGRAMS += test_ust_data @@ -70,7 +90,6 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) $(top_builddir)/src/bin/lttng-sessiond/condition-internal.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/save.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/notification-thread-commands.$(OBJEXT) \ - $(top_builddir)/src/bin/lttng-sessiond/shm.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/kernel.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/ht-cleanup.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/notification-thread.$(OBJEXT) \ @@ -79,6 +98,7 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) $(top_builddir)/src/bin/lttng-sessiond/channel.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/agent.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/kernel-consumer.$(OBJEXT) \ + $(top_builddir)/src/bin/lttng-sessiond/map.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/trace-kernel.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/rotation-thread.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/context.$(OBJEXT) \ @@ -87,6 +107,7 @@ SESSIOND_OBJS = $(top_builddir)/src/bin/lttng-sessiond/buffer-registry.$(OBJEXT) $(top_builddir)/src/bin/lttng-sessiond/fd-limit.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/notification-thread-events.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/event.$(OBJEXT) \ + $(top_builddir)/src/bin/lttng-sessiond/event-notifier-error-accounting.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/timer.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/snapshot.$(OBJEXT) \ $(top_builddir)/src/bin/lttng-sessiond/sessiond-config.$(OBJEXT) \ @@ -200,6 +221,18 @@ test_directory_handle_LDADD = $(LIBTAP) $(LIBHASHTABLE) $(LIBCOMMON) $(DL_LIBS) test_string_utils_SOURCES = test_string_utils.c test_string_utils_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBSTRINGUTILS) $(DL_LIBS) +# Map api +test_map_SOURCES = test_map.c +test_map_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) + +# Map key api +test_map_key_SOURCES = test_map_key.c +test_map_key_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) + +# Map query api +test_map_query_SOURCES = test_map_query.c +test_map_query_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) + # Notification api test_notification_SOURCES = test_notification.c test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS) @@ -208,6 +241,10 @@ test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS) test_event_rule_SOURCES = test_event_rule.c test_event_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) +# Action api +test_action_SOURCES = test_action.c +test_action_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) +# # Condition api test_condition_SOURCES = test_condition.c test_condition_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) @@ -244,3 +281,7 @@ test_kernel_probe_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) # Event expression to bytecode test test_event_expr_to_bytecode_SOURCES = test_event_expr_to_bytecode.c test_event_expr_to_bytecode_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(LIBCOMMON) + +# Log level rule api +test_log_level_rule_SOURCES = test_log_level_rule.c +test_log_level_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS) diff --git a/tests/unit/test_action.c b/tests/unit/test_action.c new file mode 100644 index 000000000..6e526c5ee --- /dev/null +++ b/tests/unit/test_action.c @@ -0,0 +1,120 @@ +/* + * test_action.c + * + * Unit tests for the action API. + * + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#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 14 + +static +void test_action_incr_value(void) +{ + int ret; + struct lttng_action *action = NULL; + struct lttng_action *action_from_buffer = NULL; + const char *map_name = "my_map_name"; + const char *session_name = "my_session_name"; + const char *first_part_key = "first_part_🥇_"; + const char *second_part_key = "_🥈_second_part"; + struct lttng_map_key *key = NULL; + enum lttng_action_status action_status; + enum lttng_map_key_status key_status; + struct lttng_payload buffer; + const struct lttng_map_key *key_from_buffer; + const struct lttng_map_key_token *token; + + lttng_payload_init(&buffer); + + /* Test key creation */ + key = lttng_map_key_create(); + ok(key, "Key created"); + + key_status = lttng_map_key_append_token_string(key, first_part_key); + ok(key_status == LTTNG_MAP_KEY_STATUS_OK, "Key append first string"); + + key_status = lttng_map_key_append_token_variable(key, LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME); + ok(key_status == LTTNG_MAP_KEY_STATUS_OK, "Key append event name variable"); + + key_status = lttng_map_key_append_token_string(key, second_part_key); + ok(key_status == LTTNG_MAP_KEY_STATUS_OK, "Key append second string"); + + /*Test incr value action creation */ + action = lttng_action_incr_value_create(); + ok(action, "Incr-value action created"); + + action_status = lttng_action_incr_value_set_session_name(action, session_name); + ok(action_status == LTTNG_ACTION_STATUS_OK, "incr-value set session name"); + + action_status = lttng_action_incr_value_set_map_name(action, map_name); + ok(action_status == LTTNG_ACTION_STATUS_OK, "incr-value set map name"); + + action_status = lttng_action_incr_value_set_key(action, key); + ok(action_status == LTTNG_ACTION_STATUS_OK, "incr-value set key"); + + /* Test incr value action serialization */ + ret = lttng_action_serialize(action, &buffer); + ok(ret == 0, "Incr value action serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + + (void) lttng_action_create_from_payload( + &view, &action_from_buffer); + } + ok(action_from_buffer, "Incr value action created from payload is non-null"); + + action_status = lttng_action_incr_value_get_key(action, &key_from_buffer); + ok(key_from_buffer, "Retrived key from incr value action"); + + token = lttng_map_key_get_token_at_index(key_from_buffer, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First key token is a string"); + + token = lttng_map_key_get_token_at_index(key_from_buffer, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Second key token is a variable"); + + token = lttng_map_key_get_token_at_index(key_from_buffer, 2); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "Third key token is a string"); + + lttng_payload_reset(&buffer); + + lttng_action_destroy(action); + lttng_action_destroy(action_from_buffer); + lttng_map_key_destroy(key); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_action_incr_value(); + return exit_status(); +} diff --git a/tests/unit/test_condition.c b/tests/unit/test_condition.c index 3b0144a0a..539e0b0b6 100644 --- a/tests/unit/test_condition.c +++ b/tests/unit/test_condition.c @@ -20,8 +20,10 @@ #include #include #include -#include +#include +#include #include +#include #include #include @@ -30,7 +32,7 @@ int lttng_opt_quiet = 1; int lttng_opt_verbose; int lttng_opt_mi; -#define NUM_TESTS 13 +#define NUM_TESTS 15 static void test_condition_event_rule(void) @@ -45,10 +47,16 @@ void test_condition_event_rule(void) 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" }; + uint64_t _error_count = 420, _error_counter_index = 9999, error_count, error_counter_index; + struct lttng_log_level_rule *log_level_rule_at_least_as_severe = NULL; struct lttng_payload buffer; lttng_payload_init(&buffer); + /* Create log level rule */ + log_level_rule_at_least_as_severe = lttng_log_level_rule_at_least_as_severe_as_create(LTTNG_LOGLEVEL_WARNING); + assert(log_level_rule_at_least_as_severe); + tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_UST); ok(tracepoint, "tracepoint UST_DOMAIN"); @@ -58,8 +66,7 @@ void test_condition_event_rule(void) status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting filter"); - status = lttng_event_rule_tracepoint_set_log_level_range_lower_bound( - tracepoint, LTTNG_LOGLEVEL_WARNING); + status = lttng_event_rule_tracepoint_set_log_level_rule(tracepoint, log_level_rule_at_least_as_severe); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting log level range"); for (i = 0; i < 3; i++) { @@ -69,10 +76,14 @@ void test_condition_event_rule(void) "Setting exclusion pattern"); } - condition = lttng_condition_event_rule_create(tracepoint); + condition = lttng_condition_on_event_create(tracepoint); ok(condition, "Created condition"); - condition_status = lttng_condition_event_rule_get_rule( + /* Set the error count information */ + lttng_condition_on_event_set_error_count(condition, _error_count); + lttng_condition_on_event_set_error_counter_index(condition, _error_counter_index); + + condition_status = lttng_condition_on_event_get_rule( condition, &tracepoint_tmp); ok(condition_status == LTTNG_CONDITION_STATUS_OK, "Getting event rule from event rule condition"); @@ -94,10 +105,17 @@ void test_condition_event_rule(void) ok(lttng_condition_is_equal(condition, condition_from_buffer), "Serialized and de-serialized conditions are equal"); + /* Error count info is not considered in is_equal so test it separately */ + error_count = lttng_condition_on_event_get_error_count(condition_from_buffer); + error_counter_index = lttng_condition_on_event_get_error_counter_index(condition_from_buffer); + ok(error_count == _error_count, "Error count is the same. Got %" PRIu64 " Expected %" PRIu64, error_count, _error_count); + ok(error_counter_index == _error_counter_index, "Error count index is the same. Got %" PRIu64 " Expected %" PRIu64, error_count, _error_count); + lttng_payload_reset(&buffer); lttng_event_rule_destroy(tracepoint); lttng_condition_destroy(condition); lttng_condition_destroy(condition_from_buffer); + lttng_log_level_rule_destroy(log_level_rule_at_least_as_severe); } int main(int argc, const char *argv[]) diff --git a/tests/unit/test_event_rule.c b/tests/unit/test_event_rule.c index 53f16dbbd..050b9d119 100644 --- a/tests/unit/test_event_rule.c +++ b/tests/unit/test_event_rule.c @@ -18,14 +18,14 @@ #include #include #include -#include -#include +#include +#include #include #include #include #include -#include -#include +#include +#include #include #include #include @@ -37,7 +37,7 @@ int lttng_opt_quiet = 1; int lttng_opt_verbose; int lttng_opt_mi; -#define NUM_TESTS 187 +#define NUM_TESTS 144 struct tracepoint_test { enum lttng_domain_type type; @@ -47,17 +47,17 @@ struct tracepoint_test { static void test_event_rule_tracepoint_by_domain(const struct tracepoint_test *test) { - 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, type; - enum lttng_loglevel_type log_level_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_log_level_rule *log_level_rule = NULL; + const struct lttng_log_level_rule *log_level_rule_return = NULL; struct lttng_payload payload; type = test->type; @@ -65,6 +65,9 @@ void test_event_rule_tracepoint_by_domain(const struct tracepoint_test *test) lttng_payload_init(&payload); + log_level_rule = lttng_log_level_rule_exactly_create(LTTNG_LOGLEVEL_INFO); + assert(log_level_rule); + tracepoint = lttng_event_rule_tracepoint_create(type); ok(tracepoint, "tracepoint object."); @@ -84,28 +87,13 @@ void test_event_rule_tracepoint_by_domain(const struct tracepoint_test *test) ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter."); ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal."); - status = lttng_event_rule_tracepoint_set_log_level_all(tracepoint); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting all log level."); - status = lttng_event_rule_tracepoint_get_log_level_type(tracepoint, &log_level_type); - ok(log_level_type == LTTNG_EVENT_LOGLEVEL_ALL, "getting loglevel type all."); - status = lttng_event_rule_tracepoint_get_log_level(tracepoint, &ret); - ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset loglevel value."); - - status = lttng_event_rule_tracepoint_set_log_level(tracepoint, LTTNG_LOGLEVEL_INFO); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting single loglevel."); - status = lttng_event_rule_tracepoint_get_log_level_type(tracepoint, &log_level_type); - ok(log_level_type == LTTNG_EVENT_LOGLEVEL_SINGLE, "getting loglevel type single."); - status = lttng_event_rule_tracepoint_get_log_level(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_log_level_range_lower_bound(tracepoint, LTTNG_LOGLEVEL_WARNING); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel."); - status = lttng_event_rule_tracepoint_get_log_level_type(tracepoint, &log_level_type); - ok(log_level_type == LTTNG_EVENT_LOGLEVEL_RANGE, "getting loglevel type range."); - status = lttng_event_rule_tracepoint_get_log_level(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_get_log_level_rule(tracepoint, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset log level rule."); + + status = lttng_event_rule_tracepoint_set_log_level_rule(tracepoint, log_level_rule); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting log level rule."); + status = lttng_event_rule_tracepoint_get_log_level_rule(tracepoint, &log_level_rule_return); + ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get log level rule."); if (test->support_exclusion) { int i; @@ -154,6 +142,7 @@ void test_event_rule_tracepoint_by_domain(const struct tracepoint_test *test) lttng_payload_reset(&payload); lttng_event_rule_destroy(tracepoint); lttng_event_rule_destroy(tracepoint_from_buffer); + lttng_log_level_rule_destroy(log_level_rule); } static @@ -225,7 +214,7 @@ static void test_event_rule_syscall(void) lttng_event_rule_destroy(syscall_from_buffer); } -static void test_event_rule_uprobe(void) +static void test_event_rule_userspace_probe(void) { struct lttng_event_rule *uprobe = NULL; struct lttng_event_rule *uprobe_from_buffer = NULL; @@ -261,14 +250,10 @@ static void test_event_rule_uprobe(void) lttng_payload_init(&payload); - uprobe = lttng_event_rule_uprobe_create(); + uprobe = lttng_event_rule_userspace_probe_create(probe_location); ok(uprobe, "uprobe event rule object creation."); - status = lttng_event_rule_uprobe_set_location(uprobe, probe_location); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting uprobe event rule location."); - - status = lttng_event_rule_uprobe_get_location( + status = lttng_event_rule_userspace_probe_get_location( uprobe, &probe_location_tmp); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting uprobe event rule location."); @@ -276,10 +261,10 @@ static void test_event_rule_uprobe(void) probe_location, probe_location_tmp), "Location is equal."); - status = lttng_event_rule_uprobe_set_name(uprobe, probe_name); + status = lttng_event_rule_userspace_probe_set_event_name(uprobe, probe_name); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting uprobe event rule name: %s.", probe_name); - status = lttng_event_rule_uprobe_get_name(uprobe, &tmp); + status = lttng_event_rule_userspace_probe_get_event_name(uprobe, &tmp); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting uprobe name."); ok(!strcmp(probe_name, tmp), "Uprobe name are equal."); @@ -306,7 +291,7 @@ end: lttng_userspace_probe_location_lookup_method_destroy(lookup_method); } -static void test_event_rule_kprobe_by_location( +static void test_event_rule_kernel_probe_by_location( const struct lttng_kernel_probe_location *location) { struct lttng_event_rule *kprobe = NULL; @@ -323,21 +308,18 @@ static void test_event_rule_kprobe_by_location( lttng_payload_init(&payload); - kprobe = lttng_event_rule_kprobe_create(); + kprobe = lttng_event_rule_kernel_probe_create(location); ok(kprobe, "kprobe event rule object creation."); - status = lttng_event_rule_kprobe_set_location(kprobe, location); - ok(status == LTTNG_EVENT_RULE_STATUS_OK, - "Setting kprobe event rule location."); - status = lttng_event_rule_kprobe_get_location(kprobe, &_location); + status = lttng_event_rule_kernel_probe_get_location(kprobe, &_location); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting kprobe event rule location."); ok(lttng_kernel_probe_location_is_equal(location, _location), "Locations are equal."); - status = lttng_event_rule_kprobe_set_name(kprobe, probe_name); + status = lttng_event_rule_kernel_probe_set_event_name(kprobe, probe_name); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Setting kprobe event rule name: %s.", probe_name); - status = lttng_event_rule_kprobe_get_name(kprobe, &tmp); + status = lttng_event_rule_kernel_probe_get_event_name(kprobe, &tmp); ok(status == LTTNG_EVENT_RULE_STATUS_OK, "Getting kprobe name."); ok(!strcmp(probe_name, tmp), "kprobe name are equal."); @@ -361,7 +343,7 @@ static void test_event_rule_kprobe_by_location( lttng_event_rule_destroy(kprobe_from_buffer); } -static void test_event_rule_kprobe(void) +static void test_event_rule_kernel_probe(void) { struct lttng_kernel_probe_location *address_location = NULL; struct lttng_kernel_probe_location *symbol_location = NULL; @@ -371,8 +353,8 @@ static void test_event_rule_kprobe(void) assert(address_location); assert(symbol_location); - test_event_rule_kprobe_by_location(address_location); - test_event_rule_kprobe_by_location(symbol_location); + test_event_rule_kernel_probe_by_location(address_location); + test_event_rule_kernel_probe_by_location(symbol_location); lttng_kernel_probe_location_destroy(address_location); lttng_kernel_probe_location_destroy(symbol_location); @@ -383,7 +365,7 @@ int main(int argc, const char *argv[]) plan_tests(NUM_TESTS); test_event_rule_tracepoint(); test_event_rule_syscall(); - test_event_rule_uprobe(); - test_event_rule_kprobe(); + test_event_rule_userspace_probe(); + test_event_rule_kernel_probe(); return exit_status(); } diff --git a/tests/unit/test_log_level_rule.c b/tests/unit/test_log_level_rule.c new file mode 100644 index 000000000..9eccc7946 --- /dev/null +++ b/tests/unit/test_log_level_rule.c @@ -0,0 +1,187 @@ +/* + * Unit tests for the log level rule API. + * + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#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 29 + +static void test_log_level_rule_error(void) +{ + int level = 9000; + struct lttng_log_level_rule *exactly = + lttng_log_level_rule_exactly_create(level); + struct lttng_log_level_rule *at_least_as_severe = + lttng_log_level_rule_at_least_as_severe_as_create( + level); + + ok(lttng_log_level_rule_get_type(NULL) == LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN, "Get type on invalid pointer"); + + ok(lttng_log_level_rule_exactly_get_level(NULL, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (NULL, NULL) returns invalid"); + ok(lttng_log_level_rule_exactly_get_level(exactly, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (valid, NULL) returns invalid"); + ok(lttng_log_level_rule_exactly_get_level(NULL, &level) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_exactly_get_level (NULL, valid) returns invalid"); + + ok(lttng_log_level_rule_at_least_as_severe_as_get_level(NULL, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (NULL, NULL) returns invalid"); + ok(lttng_log_level_rule_at_least_as_severe_as_get_level(exactly, NULL) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (valid, NULL) returns invalid"); + ok(lttng_log_level_rule_at_least_as_severe_as_get_level(NULL, &level) == LTTNG_LOG_LEVEL_RULE_STATUS_INVALID, "lttng_log_level_rule_at_least_as_severe_as_get_level (NULL, valid) returns invalid"); + + lttng_log_level_rule_destroy(exactly); + lttng_log_level_rule_destroy(at_least_as_severe); +} + +static +void test_log_level_rule_serialize_deserialize(const struct lttng_log_level_rule *rule) +{ + struct lttng_log_level_rule *log_level_rule_from_buffer = NULL; + struct lttng_payload payload; + + lttng_payload_init(&payload); + + ok(lttng_log_level_rule_serialize(rule, &payload) == 0, "Serializing."); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &payload, 0, -1); + + ok(lttng_log_level_rule_create_from_payload( + &view, &log_level_rule_from_buffer) > 0, + "Deserializing."); + } + + ok(lttng_log_level_rule_is_equal(rule, log_level_rule_from_buffer), "Serialized and from buffer are equal"); + + lttng_log_level_rule_destroy(log_level_rule_from_buffer); +} + +static +void test_log_level_rule_is_equal_exactly(void) +{ + int level = 9000, no_eq_level = 420; + struct lttng_log_level_rule *a, *b, *different_level, *different_type; + + /* Identical log level rules. */ + a = lttng_log_level_rule_exactly_create(level); + b = lttng_log_level_rule_exactly_create(level); + + /* Different level, same type. */ + different_level = lttng_log_level_rule_exactly_create(no_eq_level); + + /* Different type */ + different_type = lttng_log_level_rule_at_least_as_severe_as_create(level); + + assert(a && b && different_level && different_type); + + ok(lttng_log_level_rule_is_equal(a, a), "Same object is equal"); + ok(lttng_log_level_rule_is_equal(a, b), "Object a and b are equal"); + ok(!lttng_log_level_rule_is_equal(a, different_level), " Object of different levels are not equal"); + ok(!lttng_log_level_rule_is_equal(a, different_type), " Object of different types are not equal"); + + lttng_log_level_rule_destroy(a); + lttng_log_level_rule_destroy(b); + lttng_log_level_rule_destroy(different_level); + lttng_log_level_rule_destroy(different_type); +} + +static +void test_log_level_rule_is_equal_at_least_as_severe_as(void) +{ + int level = 9000, no_eq_level = 420; + struct lttng_log_level_rule *a, *b, *different_level, *different_type; + + /* Identical log level rules. */ + a = lttng_log_level_rule_at_least_as_severe_as_create(level); + b = lttng_log_level_rule_at_least_as_severe_as_create(level); + + /* Different level, same type. */ + different_level = lttng_log_level_rule_at_least_as_severe_as_create(no_eq_level); + + /* Different type */ + different_type = lttng_log_level_rule_exactly_create(level); + + assert(a && b && different_level && different_type); + + ok(lttng_log_level_rule_is_equal(a, a), "Same object is equal"); + ok(lttng_log_level_rule_is_equal(a, b), "Object a and b are equal"); + ok(!lttng_log_level_rule_is_equal(a, different_level), " Object of different levels are not equal"); + ok(!lttng_log_level_rule_is_equal(a, different_type), " Object of different types are not equal"); + + lttng_log_level_rule_destroy(a); + lttng_log_level_rule_destroy(b); + lttng_log_level_rule_destroy(different_level); + lttng_log_level_rule_destroy(different_type); +} + +static void test_log_level_rule_exactly(void) +{ + int level = 9000; + int _level; + struct lttng_log_level_rule *exactly = NULL; + enum lttng_log_level_rule_status status; + + exactly = lttng_log_level_rule_exactly_create(level); + + ok(exactly, "Log level exactly allocated"); + ok(lttng_log_level_rule_get_type(exactly) == + LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY, + "Log level rule exactly type"); + + status = lttng_log_level_rule_exactly_get_level(exactly, &_level); + ok(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK, "Get the level"); + ok(_level == level, "Level property is valid"); + + test_log_level_rule_is_equal_exactly(); + test_log_level_rule_serialize_deserialize(exactly); +} + +static void test_log_level_rule_at_least_as_severe_as(void) +{ + int level = 9000; + int _level; + struct lttng_log_level_rule *at_least_as_severe_as = NULL; + enum lttng_log_level_rule_status status; + + at_least_as_severe_as = lttng_log_level_rule_at_least_as_severe_as_create(level); + + ok(at_least_as_severe_as, "Log level at_least_as_severe_as allocated"); + ok(lttng_log_level_rule_get_type(at_least_as_severe_as) == + LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS, + "Log level rule at_least_as_severe_as type"); + + status = lttng_log_level_rule_at_least_as_severe_as_get_level(at_least_as_severe_as, &_level); + ok(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK, "Get the level"); + ok(_level == level, "Level property is valid"); + + test_log_level_rule_is_equal_at_least_as_severe_as(); + test_log_level_rule_serialize_deserialize(at_least_as_severe_as); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_log_level_rule_exactly(); + test_log_level_rule_at_least_as_severe_as(); + test_log_level_rule_error(); + return exit_status(); +} diff --git a/tests/unit/test_map.c b/tests/unit/test_map.c new file mode 100644 index 000000000..f8f6f8474 --- /dev/null +++ b/tests/unit/test_map.c @@ -0,0 +1,439 @@ +/* + * test_map.c + * + * Unit tests for the map API. + * + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "lttng/domain.h" +#include "lttng/map/map.h" +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#define NUM_TESTS 70 + +static +void test_map_key_value_pair_serialize_deserialize(void) +{ + struct lttng_map_key_value_pair *kv; + struct lttng_map_key_value_pair *kv_from_payload; + struct lttng_payload buffer; + enum lttng_map_status map_status; + const char *kv_from_payload_key, *key = "ma_clé"; + int64_t kv_from_payload_value, value = 133121; + int ret; + + diag("Simple lttng_map_key_value_pair tests"); + + lttng_payload_init(&buffer); + + kv = lttng_map_key_value_pair_create(key, value); + ok(kv, "Key-value pair created"); + + /* Test incr value action serialization */ + ret = lttng_map_key_value_pair_serialize(kv, &buffer); + ok(ret == 0, "Key-value pair serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + + (void) lttng_map_key_value_pair_create_from_payload( + &view, &kv_from_payload); + } + ok(kv_from_payload, "Key-value pair created from payload is non-null"); + + map_status = lttng_map_key_value_pair_get_key(kv_from_payload, + &kv_from_payload_key); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key-value pair 1 key"); + ok(strcmp(kv_from_payload_key, key) == 0, "Key-value pair from payload has correct key"); + + map_status = lttng_map_key_value_pair_get_value(kv_from_payload, + &kv_from_payload_value); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key-value pair 1 value"); + ok(kv_from_payload_value == value, "Key-value pair from payload has correct value"); + + lttng_payload_reset(&buffer); + lttng_map_key_value_pair_destroy(kv); + lttng_map_key_value_pair_destroy(kv_from_payload); +} + +static +void test_map_key_value_pair_list_serialize_deserialize(void) +{ + struct lttng_map_key_value_pair *kv; + const struct lttng_map_key_value_pair *kv_from_payload = NULL; + + struct lttng_map_key_value_pair_list *kv_pair_list; + struct lttng_map_key_value_pair_list *kv_pair_list_from_payload; + + struct lttng_payload buffer; + enum lttng_map_status map_status; + const char *kv_from_payload_key, *key1 = "ma_clé", *key2 = "autre_clé"; + int64_t kv_from_payload_value, value1 = 123456, value2 = 98765; + enum lttng_map_key_value_pair_list_type list_type = + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID; + uint64_t identifier = 3192112; + bool summed_all_cpus = true; + unsigned int kv_count; + int ret; + + diag("Simple lttng_map_key_value_pair_list tests"); + + lttng_payload_init(&buffer); + + kv_pair_list = lttng_map_key_value_pair_list_create(list_type, + summed_all_cpus); + ok(kv_pair_list, "Key-value pair_list list created"); + + map_status = lttng_map_key_value_pair_list_set_identifier(kv_pair_list, + identifier); + ok(kv_pair_list, "Key-value set identifier"); + + kv = lttng_map_key_value_pair_create(key1, value1); + ok(kv, "Key-value pair 1 created"); + + map_status = lttng_map_key_value_pair_list_append_key_value(kv_pair_list, kv); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value 1 appended to list"); + + kv = lttng_map_key_value_pair_create(key2, value2); + ok(kv, "Key-value pair 2 created"); + + map_status = lttng_map_key_value_pair_list_append_key_value(kv_pair_list, kv); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value 2 appended to list"); + + /* Test incr value action serialization */ + ret = lttng_map_key_value_pair_list_serialize(kv_pair_list, &buffer); + ok(ret == 0, "Key-value pair_list list serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + + (void) lttng_map_key_value_pair_list_create_from_payload( + &view, &kv_pair_list_from_payload); + } + ok(kv_pair_list_from_payload, "Key-value pair list created from payload is non-null"); + + ok(lttng_map_key_value_pair_list_get_summed_all_cpu(kv_pair_list_from_payload) == summed_all_cpus, + "Got the expected summed all cpu state"); + + ok(lttng_map_key_value_pair_list_get_type(kv_pair_list_from_payload) == list_type, + "Got the expected list type"); + ok(lttng_map_key_value_pair_list_get_identifer(kv_pair_list_from_payload) == identifier, + "Got the expected list identifier"); + + map_status = lttng_map_key_value_pair_list_get_count( + kv_pair_list_from_payload, &kv_count); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got key value pair count"); + ok(kv_count == 2, "Got the right key value pair count"); + + kv_from_payload = lttng_map_key_value_pair_list_get_at_index( + kv_pair_list_from_payload, 0); + ok(kv_from_payload, "Key-value pair 1 created from payload"); + + map_status = lttng_map_key_value_pair_get_key(kv_from_payload, + &kv_from_payload_key); + ok(strcmp(kv_from_payload_key, key1) == 0, "Key-value pair 1 from payload has correct key"); + + map_status = lttng_map_key_value_pair_get_value(kv_from_payload, + &kv_from_payload_value); + ok(kv_from_payload_value == value1, "Key-value pair 1 from payload has correct value"); + + kv_from_payload = lttng_map_key_value_pair_list_get_at_index( + kv_pair_list_from_payload, 1); + ok(kv_from_payload, "Key-value pair 2 created from payload"); + + map_status = lttng_map_key_value_pair_get_key(kv_from_payload, + &kv_from_payload_key); + ok(strcmp(kv_from_payload_key, key2) == 0, "Key-value pair 2 from payload has correct key"); + + map_status = lttng_map_key_value_pair_get_value(kv_from_payload, + &kv_from_payload_value); + ok(kv_from_payload_value == value2, "Key-value pair 2 from payload has correct value"); + + lttng_payload_reset(&buffer); + lttng_map_key_value_pair_list_destroy(kv_pair_list); + lttng_map_key_value_pair_list_destroy(kv_pair_list_from_payload); +} + +static +void test_map_content_serialize_deserialize(void) +{ + struct lttng_map_content *map_content, *map_content_from_payload; + enum lttng_map_status map_status; + struct lttng_payload buffer; + struct lttng_map_key_value_pair *kv1, *kv2; + const char *key1 = "ma_clé", *key2 = "autre_clé"; + uint64_t value1 = 123456, value2 = 98765; + enum lttng_map_key_value_pair_list_type list_type1 = + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID; + enum lttng_map_key_value_pair_list_type list_type2 = + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID; + enum lttng_map_key_value_pair_list_type list_type3 = + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED; + uint64_t id1 = 958323, id2 = 121942; + struct lttng_map_key_value_pair_list *kv_pair_list1, *kv_pair_list2, *kv_pair_list3; + const struct lttng_map_key_value_pair_list *kv_pair_list1_from_payload; + const struct lttng_map_key_value_pair_list *kv_pair_list2_from_payload; + const struct lttng_map_key_value_pair_list *kv_pair_list3_from_payload; + unsigned int list_count; + enum lttng_buffer_type buffer_type = LTTNG_BUFFER_PER_UID; + int ret; + + diag("Simple lttng_map_content tests"); + + lttng_payload_init(&buffer); + + kv_pair_list1 = lttng_map_key_value_pair_list_create(list_type1, true); + map_status = lttng_map_key_value_pair_list_set_identifier(kv_pair_list1, id1); + + kv_pair_list2 = lttng_map_key_value_pair_list_create(list_type2, false); + map_status = lttng_map_key_value_pair_list_set_identifier(kv_pair_list2, id2); + + kv_pair_list3 = lttng_map_key_value_pair_list_create(list_type3, true); + + kv1 = lttng_map_key_value_pair_create(key1, value1); + map_status = lttng_map_key_value_pair_list_append_key_value(kv_pair_list1, kv1); + + kv2 = lttng_map_key_value_pair_create(key2, value2); + map_status = lttng_map_key_value_pair_list_append_key_value(kv_pair_list2, kv2); + + map_content = lttng_map_content_create(buffer_type); + + map_status = lttng_map_content_append_key_value_list(map_content, kv_pair_list1); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value list 1 appended to map_content"); + + map_status = lttng_map_content_append_key_value_list(map_content, kv_pair_list2); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value list 2 appended to map_content"); + + map_status = lttng_map_content_append_key_value_list(map_content, kv_pair_list3); + ok(map_status == LTTNG_MAP_STATUS_OK, "Key value list 3 appended to map_content"); + + ret = lttng_map_content_serialize(map_content, &buffer); + ok(ret == 0, "Map list serialized"); + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_content_create_from_payload( + &view, &map_content_from_payload); + } + + ok(map_content, "map content created from payload is non-null"); + map_status = lttng_map_content_get_count(map_content_from_payload, &list_count); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got key value pair count"); + ok(list_count == 3, "Got the expected key-value list count"); + + ok(lttng_map_content_get_buffer_type(map_content_from_payload) == buffer_type, + "Got the expected buffer type"); + + kv_pair_list1_from_payload = lttng_map_content_get_at_index(map_content_from_payload, 0); + ok(kv_pair_list1_from_payload, "Key-value pair list created from payload is non-null"); + + ok(lttng_map_key_value_pair_list_get_type(kv_pair_list1_from_payload) == list_type1, + "Got the expected list type"); + + ok(lttng_map_key_value_pair_list_get_identifer(kv_pair_list1_from_payload) == id1, + "Got the expected list identifier"); + + kv_pair_list2_from_payload = lttng_map_content_get_at_index(map_content_from_payload, 1); + ok(kv_pair_list2_from_payload, "Key-value pair list created from payload is non-null"); + + ok(lttng_map_key_value_pair_list_get_type(kv_pair_list2_from_payload) == list_type2, + "Got the expected list type"); + + ok(lttng_map_key_value_pair_list_get_identifer(kv_pair_list2_from_payload) == id2, + "Got the expected list identifier"); + + kv_pair_list3_from_payload = lttng_map_content_get_at_index(map_content_from_payload, 2); + ok(kv_pair_list3_from_payload, "Key-value pair list created from payload is non-null"); + + ok(lttng_map_key_value_pair_list_get_type(kv_pair_list3_from_payload) == list_type3, + "Got the expected list type"); + + lttng_payload_reset(&buffer); + lttng_map_content_destroy(map_content); + lttng_map_content_destroy(map_content_from_payload); +} + +static +void test_map(void) +{ + int ret; + struct lttng_payload buffer; + struct lttng_map *map, *map_from_payload = NULL; + enum lttng_map_status map_status; + const char *map_name = "map_name", *map_name_from_payload; + unsigned int dimension_count = 1; + uint64_t first_dim_size = 423; + uint64_t dimension_sizes[1] = {first_dim_size}; + enum lttng_domain_type domain = LTTNG_DOMAIN_UST; + enum lttng_buffer_type buffer_type = LTTNG_BUFFER_PER_UID; + enum lttng_map_bitness bitness = LTTNG_MAP_BITNESS_32BITS; + enum lttng_map_boundary_policy boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + bool coalesce_hits = true; + + + diag("Simple lttng_map tests"); + lttng_payload_init(&buffer); + + map_status = lttng_map_create(map_name, dimension_count, + dimension_sizes, domain, buffer_type, bitness, + boundary_policy, coalesce_hits, &map); + ok(map_status == LTTNG_MAP_STATUS_OK, "Created map"); + + lttng_map_set_is_enabled(map, true); + + ret = lttng_map_serialize(map, &buffer); + ok(ret == 0, "Map serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_create_from_payload( + &view, &map_from_payload); + } + ok(map_from_payload, "Map created from payload"); + + ok(lttng_map_get_dimension_count(map_from_payload) == dimension_count, + "Got the expected dimension count from payload"); + + ok(lttng_map_get_is_enabled(map_from_payload) == 1, + "Got the expected enabled state from payload"); + + ok(lttng_map_get_bitness(map_from_payload) == bitness, + "Got the expected bitness from payload"); + + ok(lttng_map_get_domain(map_from_payload) == domain, + "Got the expected domain from payload"); + + ok(lttng_map_get_buffer_type(map_from_payload) == buffer_type, + "Got the expected buffer type from payload"); + + ok(lttng_map_get_boundary_policy(map_from_payload) == boundary_policy, + "Got the expected boundary policy from payload"); + + ok(lttng_map_get_coalesce_hits(map_from_payload) == coalesce_hits, + "Got the expected coalesce hits value from payload"); + + map_status = lttng_map_get_name(map_from_payload, &map_name_from_payload); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got map name from payload"); + ok(strcmp(map_name_from_payload, map_name) == 0, + "Got the expected map name from payload"); + + lttng_map_destroy(map); + lttng_map_destroy(map_from_payload); + lttng_payload_reset(&buffer); +} + +static +void test_map_list(void) +{ + int ret; + struct lttng_payload buffer; + enum lttng_map_status map_status; + struct lttng_map *map1, *map2; + const struct lttng_map *map1_from_payload = NULL, *map2_from_payload = NULL; + struct lttng_map_list *map_list, *map_list_from_payload = NULL; + const char *map1_name = "map_name_1", *map1_name_from_payload; + const char *map2_name = "map_name_2", *map2_name_from_payload; + unsigned int dimension_count = 1, map_count = 0; + uint64_t first_dim_size = 423; + uint64_t dimension_sizes[1] = {first_dim_size}; + enum lttng_domain_type domain = LTTNG_DOMAIN_KERNEL; + enum lttng_buffer_type buffer_type1 = LTTNG_BUFFER_PER_PID, buffer_type2 = LTTNG_BUFFER_PER_UID; + enum lttng_map_bitness bitness = LTTNG_MAP_BITNESS_64BITS; + enum lttng_map_boundary_policy boundary_policy = LTTNG_MAP_BOUNDARY_POLICY_OVERFLOW; + bool coalesce_hits = false; + + diag("Simple lttng_map_list tests"); + + lttng_payload_init(&buffer); + + map_status = lttng_map_create(map1_name, dimension_count, + dimension_sizes, domain, buffer_type1, bitness, + boundary_policy, coalesce_hits, &map1); + ok(map_status == LTTNG_MAP_STATUS_OK, "Created map 1"); + lttng_map_set_is_enabled(map1, true); + + map_status = lttng_map_create(map2_name, dimension_count, + dimension_sizes, domain, buffer_type2, bitness, + boundary_policy, coalesce_hits, &map2); + ok(map_status == LTTNG_MAP_STATUS_OK, "Created map 2"); + lttng_map_set_is_enabled(map2, true); + + map_list = lttng_map_list_create(); + ok(map_list, "Map list created"); + + map_status = lttng_map_list_add(map_list, map1); + ok(map_status == LTTNG_MAP_STATUS_OK, "Map 1 added to map list"); + + map_status = lttng_map_list_add(map_list, map2); + ok(map_status == LTTNG_MAP_STATUS_OK, "Map 1 added to map list"); + + ret = lttng_map_list_serialize(map_list, &buffer); + ok(ret == 0, "Map list serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_list_create_from_payload( + &view, &map_list_from_payload); + } + + map_status = lttng_map_list_get_count(map_list, &map_count); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got map count from payload"); + ok(map_count == 2, "Got the right map count from payload"); + + map1_from_payload = lttng_map_list_get_at_index(map_list, 0); + ok(map1_from_payload, "Got first map from payload"); + map_status = lttng_map_get_name(map1_from_payload, &map1_name_from_payload); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got map 1 name from payload"); + ok(strcmp(map1_name_from_payload, map1_name) == 0, + "Got right map 1 name from payload"); + ok(lttng_map_get_is_enabled(map1_from_payload) == 1, + "Got right map 1 enabled state from payload"); + + map2_from_payload = lttng_map_list_get_at_index(map_list, 1); + ok(map2_from_payload, "Got first map from payload"); + map_status = lttng_map_get_name(map2_from_payload, &map2_name_from_payload); + ok(map_status == LTTNG_MAP_STATUS_OK, "Got map 2 name from payload"); + ok(strcmp(map2_name_from_payload, map2_name) == 0, + "Got right map 2 name from payload"); + ok(lttng_map_get_is_enabled(map2_from_payload) == 1, + "Got right map 2 enabled state from payload"); + + lttng_map_destroy(map1); + lttng_map_destroy(map2); + lttng_map_list_destroy(map_list); + lttng_map_list_destroy(map_list_from_payload); + lttng_payload_reset(&buffer); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + + test_map(); + test_map_list(); + test_map_key_value_pair_serialize_deserialize(); + test_map_key_value_pair_list_serialize_deserialize(); + test_map_content_serialize_deserialize(); + + return exit_status(); +} diff --git a/tests/unit/test_map_key.c b/tests/unit/test_map_key.c new file mode 100644 index 000000000..bdc156220 --- /dev/null +++ b/tests/unit/test_map_key.c @@ -0,0 +1,132 @@ +/* + * test_map_key.c + * + * Unit tests for the map-key API. + * + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#define NUM_TESTS 38 + +const char *key_str1 = "simple_string"; +const char *key_str2 = "${EVENT_NAME}"; +const char *key_str3 = "foo_${EVENT_NAME}"; +const char *key_str4 = "foo_${EVENT_NAME}_bar"; +const char *key_str5 = "${EVENT_NAME}_bar_${EVENT_NAME}"; +const char *key_str6 = "foo_${NON_EXISTING_VAR}"; +const char *key_str7 = "foo_${}"; +const char *key_str8 = "foo_${PROVIDER_NAME}"; + +static +void test_map_key(void) +{ + struct lttng_map_key *key; + enum lttng_map_key_status status; + const struct lttng_map_key_token *token; + const struct lttng_map_key_token_variable *var_token; + unsigned int count; + + key = lttng_map_key_parse_from_string(key_str6); + ok(!key, "Failed to create key from \"%s\" as expected", key_str6); + + key = lttng_map_key_parse_from_string(key_str7); + ok(!key, "Failed to create key from \"%s\" as expected", key_str7); + + key = lttng_map_key_parse_from_string(key_str1); + ok(key, "Created key from \"%s\"", key_str1); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str1"); + ok(count == 1, "Got correct token count for key_str1"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First token of string type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str2); + ok(key, "Created key from \"%s\"", key_str2); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str2"); + ok(count == 1, "Got correct token count for key_str2"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "First token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str3); + ok(key, "Created key from \"%s\"", key_str3); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str3"); + ok(count == 2, "Got correct token count for key_str3"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First token of string type"); + token = lttng_map_key_get_token_at_index(key, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Second token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str4); + ok(key, "Created key from \"%s\"", key_str4); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str4"); + ok(count == 3, "Got correct token count for key_str4"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First token of string type"); + token = lttng_map_key_get_token_at_index(key, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Second token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + token = lttng_map_key_get_token_at_index(key, 2); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "Third token of string type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str5); + ok(key, "Created key from \"%s\"", key_str5); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str5"); + ok(count == 3, "Got correct token count for key_str5"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "First token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + token = lttng_map_key_get_token_at_index(key, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "Second token of string type"); + token = lttng_map_key_get_token_at_index(key, 2); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Third token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME, "EVENT_NAME variable type"); + lttng_map_key_destroy(key); + + key = lttng_map_key_parse_from_string(key_str8); + ok(key, "Created key from \"%s\"", key_str8); + status = lttng_map_key_get_token_count(key, &count); + ok(status == LTTNG_MAP_KEY_STATUS_OK, "Got count for key_str8"); + ok(count == 2, "Got correct token count for key_str8"); + token = lttng_map_key_get_token_at_index(key, 0); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_STRING, "First token of string type"); + token = lttng_map_key_get_token_at_index(key, 1); + ok(token->type == LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE, "Second token of variable type"); + var_token = (typeof(var_token)) token; + ok(var_token->type == LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME, "PROVIDER_NAME variable type"); + lttng_map_key_destroy(key); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + test_map_key(); + return exit_status(); +} diff --git a/tests/unit/test_map_query.c b/tests/unit/test_map_query.c new file mode 100644 index 000000000..76dd8b633 --- /dev/null +++ b/tests/unit/test_map_query.c @@ -0,0 +1,291 @@ +/* + * test_map_query.c + * + * Unit tests for the map query API. + * + * Copyright (C) 2021 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#define NUM_TESTS 56 + +static +void test_map_query_key_filter_all_cpus_all_uids_64_no_sum(void) +{ + int ret; + struct lttng_payload buffer; + struct lttng_map_query *query, *query_from_payload = NULL; + const char *filter = "pitarifique_key"; + const char *filter_from_payload; + enum lttng_map_query_status status; + + enum lttng_map_query_config_cpu config_cpu = + LTTNG_MAP_QUERY_CONFIG_CPU_ALL; + enum lttng_map_query_config_buffer config_buffer = + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_ALL; + enum lttng_map_query_config_app_bitness config_bitness = + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + + diag("Map query, all cpus, all uids, 64bits, no sum"); + lttng_payload_init(&buffer); + + query = lttng_map_query_create(config_cpu, config_buffer, + config_bitness); + ok(query, "Map query created succesfully"); + + status = lttng_map_query_add_key_filter(query, filter); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Key filter created succesfully"); + + /* Add a cpu manually with a _CONFIG_CPU_ALL should fail. */ + status = lttng_map_query_add_cpu(query, 121); + ok(status == LTTNG_MAP_QUERY_STATUS_INVALID, "Adding a cpu failed as expected"); + + /* Add a pid manually with a _CONFIG_BUFFER_UST_UID_ should fail. */ + status = lttng_map_query_add_pid(query, 931214); + ok(status == LTTNG_MAP_QUERY_STATUS_INVALID, "Adding a PID of uid map query failed as expected"); + + ret = lttng_map_query_serialize(query, &buffer); + ok(ret == 0, "Map query serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_query_create_from_payload( + &view, &query_from_payload); + } + + ok(query_from_payload, "Map query created from payload"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "Getting app bitness config from payload"); + + ok(lttng_map_query_get_config_buffer(query_from_payload) == + config_buffer, "Buffer config"); + + ok(lttng_map_query_get_config_cpu(query_from_payload) == + config_cpu, "CPU config"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "App bitness config"); + + status = lttng_map_query_get_key_filter(query_from_payload, &filter_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Key filter"); + ok(strcmp(filter_from_payload, filter) == 0, "Key filter"); + + lttng_map_query_destroy(query); + lttng_map_query_destroy(query_from_payload); + lttng_payload_reset(&buffer); +} + +static +void test_map_query_key_some_cpu_some_uid_summed_by_uid(void) +{ + int ret; + struct lttng_payload buffer; + struct lttng_map_query *query, *query_from_payload = NULL; + unsigned int cpu_count, uid_count; + bool sum_by_cpu = false, sum_by_uid = true; + uid_t uid1 = 12131, uid1_from_payload; + int cpu1 = 494581, cpu1_from_payload; + enum lttng_map_query_status status; + + enum lttng_map_query_config_cpu config_cpu = + LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET; + enum lttng_map_query_config_buffer config_buffer = + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET; + enum lttng_map_query_config_app_bitness config_bitness = + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + + diag("Map query some cpus, some uids, summed by uid"); + lttng_payload_init(&buffer); + + query = lttng_map_query_create(config_cpu, config_buffer, + config_bitness); + ok(query, "Map query created succesfully"); + + status = lttng_map_query_set_sum_by_cpu(query, sum_by_cpu); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Setting sum-by-cpu option"); + + status = lttng_map_query_set_sum_by_uid(query, sum_by_uid); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Setting sum-by-uid option"); + + status = lttng_map_query_add_cpu(query, cpu1); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Adding a cpu %d", cpu1); + + status = lttng_map_query_add_uid(query, uid1); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Adding a uid %d", uid1); + + /* Add a pid manually with a _CONFIG_BUFFER_UST_UID_ should fail. */ + status = lttng_map_query_add_pid(query, 931214); + ok(status == LTTNG_MAP_QUERY_STATUS_INVALID, "Adding a PID of uid map query failed as expected"); + + ret = lttng_map_query_serialize(query, &buffer); + ok(ret == 0, "Map query serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_query_create_from_payload( + &view, &query_from_payload); + } + + ok(query_from_payload, "Map query created from payload"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "Getting app bitness config from payload"); + + ok(lttng_map_query_get_config_sum_by_cpu(query_from_payload) == + sum_by_cpu, "Getting sum-by-cpu config from payload"); + + ok(lttng_map_query_get_config_sum_by_uid(query_from_payload) == + sum_by_uid, "Getting sum-by-uid config from payload"); + + ok(lttng_map_query_get_config_buffer(query_from_payload) == + config_buffer, "Buffer config"); + + ok(lttng_map_query_get_config_cpu(query_from_payload) == + config_cpu, "CPU config"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "App bitness config"); + + status = lttng_map_query_get_cpu_count(query, &cpu_count); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting cpu count"); + ok(cpu_count == 1, "Getting cpu count"); + + status = lttng_map_query_get_cpu_at_index(query, 0, &cpu1_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting cpu count"); + ok(cpu1_from_payload == cpu1, "Getting cpu value"); + + status = lttng_map_query_get_uid_count(query, &uid_count); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting uid count"); + ok(uid_count == 1, "Getting uid count"); + + status = lttng_map_query_get_uid_at_index(query, 0, &uid1_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting uid count"); + ok(uid1_from_payload == uid1, "Getting uid value"); + + lttng_map_query_destroy(query); + lttng_map_query_destroy(query_from_payload); + lttng_payload_reset(&buffer); +} + +static +void test_map_query_key_one_cpu_some_pid_summed_by_cpu(void) +{ + int ret; + struct lttng_payload buffer; + struct lttng_map_query *query, *query_from_payload = NULL; + unsigned int cpu_count, pid_count; + pid_t pid1 = 12131, pid1_from_payload; + int cpu1 = 494581, cpu1_from_payload; + bool sum_by_cpu = true, sum_by_pid = false; + enum lttng_map_query_status status; + + enum lttng_map_query_config_cpu config_cpu = + LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET; + enum lttng_map_query_config_buffer config_buffer = + LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_PID_SUBSET; + enum lttng_map_query_config_app_bitness config_bitness = + LTTNG_MAP_QUERY_CONFIG_APP_BITNESS_ALL; + + diag("Map query one cpu, some pid, summed by cpu"); + lttng_payload_init(&buffer); + + query = lttng_map_query_create(config_cpu, config_buffer, + config_bitness); + ok(query, "Map query created succesfully"); + + status = lttng_map_query_set_sum_by_cpu(query, sum_by_cpu); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Setting sum-by-cpu option"); + + status = lttng_map_query_set_sum_by_pid(query, sum_by_pid); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Setting sum-by-pid option"); + + status = lttng_map_query_add_cpu(query, cpu1); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Adding a cpu %d", cpu1); + + status = lttng_map_query_add_pid(query, pid1); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Adding a pid %d", pid1); + + /* Add a pid manually with a _CONFIG_BUFFER_UST_PID_ should fail. */ + status = lttng_map_query_add_uid(query, 931214); + ok(status == LTTNG_MAP_QUERY_STATUS_INVALID, "Adding a UID of pid map query failed as expected"); + + ret = lttng_map_query_serialize(query, &buffer); + ok(ret == 0, "Map query serialized"); + + { + struct lttng_payload_view view = + lttng_payload_view_from_payload(&buffer, 0, -1); + (void) lttng_map_query_create_from_payload( + &view, &query_from_payload); + } + + ok(query_from_payload, "Map query created from payload"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "Getting app bitness config from payload"); + + ok(lttng_map_query_get_config_sum_by_cpu(query_from_payload) == + sum_by_cpu, "Getting sum-by-cpu config from payload"); + + ok(lttng_map_query_get_config_sum_by_pid(query_from_payload) == + sum_by_pid, "Getting sum-by-pid config from payload"); + + ok(lttng_map_query_get_config_buffer(query_from_payload) == + config_buffer, "Getting buffer config from payload"); + + ok(lttng_map_query_get_config_cpu(query_from_payload) == + config_cpu, "Getting CPU config from payload"); + + ok(lttng_map_query_get_config_app_bitness(query_from_payload) == + config_bitness, "App bitness config from payload"); + + status = lttng_map_query_get_cpu_count(query, &cpu_count); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting cpu count from payload"); + ok(cpu_count == 1, "Getting cpu count from payload"); + + status = lttng_map_query_get_cpu_at_index(query, 0, &cpu1_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting cpu count from payload"); + ok(cpu1_from_payload == cpu1, "Getting cpu value from payload"); + + status = lttng_map_query_get_pid_count(query, &pid_count); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting pid count from payload"); + ok(pid_count == 1, "Getting pid count from payload"); + + status = lttng_map_query_get_pid_at_index(query, 0, &pid1_from_payload); + ok(status == LTTNG_MAP_QUERY_STATUS_OK, "Getting pid count from payload"); + ok(pid1_from_payload == pid1, "Getting pid value from payload"); + + lttng_map_query_destroy(query); + lttng_map_query_destroy(query_from_payload); + lttng_payload_reset(&buffer); +} + +int main(int argc, const char *argv[]) +{ + plan_tests(NUM_TESTS); + + test_map_query_key_filter_all_cpus_all_uids_64_no_sum(); + test_map_query_key_some_cpu_some_uid_summed_by_uid(); + test_map_query_key_one_cpu_some_pid_summed_by_cpu(); + + return exit_status(); +} diff --git a/tests/unit/test_ust_data.c b/tests/unit/test_ust_data.c index ec61ffbad..330d49e59 100644 --- a/tests/unit/test_ust_data.c +++ b/tests/unit/test_ust_data.c @@ -125,7 +125,8 @@ static void test_create_ust_event(void) ev.type = LTTNG_EVENT_TRACEPOINT; ev.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; - ret = trace_ust_create_event(&ev, NULL, NULL, NULL, false, &event); + ret = trace_ust_create_event(0, ev.name, NULL, ev.type, ev.loglevel_type, + ev.loglevel, NULL, NULL, NULL, false, &event); ok(ret == LTTNG_OK, "Create UST event"); @@ -181,7 +182,8 @@ static void test_create_ust_event_exclusion(void) strncpy(LTTNG_EVENT_EXCLUSION_NAME_AT(exclusion, 1), random_name, LTTNG_SYMBOL_NAME_LEN); - ret = trace_ust_create_event(&ev, NULL, NULL, exclusion, false, &event); + ret = trace_ust_create_event(0, ev.name, NULL, ev.type, ev.loglevel_type, + ev.loglevel, NULL, NULL, exclusion, false, &event); exclusion = NULL; ok(ret != LTTNG_OK, "Create UST event with identical exclusion names fails"); @@ -219,7 +221,8 @@ static void test_create_ust_event_exclusion(void) strncpy(LTTNG_EVENT_EXCLUSION_NAME_AT(exclusion_copy, 1), LTTNG_EVENT_EXCLUSION_NAME_AT(exclusion, 1), LTTNG_SYMBOL_NAME_LEN); - ret = trace_ust_create_event(&ev, NULL, NULL, exclusion, false, &event); + ret = trace_ust_create_event(0, ev.name, NULL, ev.type, ev.loglevel_type, + ev.loglevel, NULL, NULL, exclusion, false, &event); exclusion = NULL; ok(ret == LTTNG_OK, "Create UST event with different exclusion names"); 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 9ab650933..1a2882f99 100644 --- a/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c +++ b/tests/utils/testapp/gen-syscall-events/gen-syscall-events.c @@ -93,7 +93,6 @@ int main(int argc, char **argv) if (argc != 4) { fprintf(stderr, "Error: Missing argument\n"); fprintf(stderr, "USAGE: %s PATH_WAIT_FILE PATH1_TO_OPEN PATH2_TO_OPEN\n", argv[0]); - fprintf(stderr, "USAGE: %s PATH_WAIT_FILE\n", argv[0]); ret = -1; goto error; } diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh index a1e142487..de2cdc324 100644 --- a/tests/utils/utils.sh +++ b/tests/utils/utils.sh @@ -325,6 +325,28 @@ function lttng_disable_kernel_syscall_fail() lttng_disable_kernel_syscall 1 "$@" } +function lttng_enable_kernel_function_event () +{ + local expected_to_fail="$1" + local sess_name="$2" + local target="$3" + local event_name="$4" + + "$TESTDIR/../src/bin/lttng/$LTTNG_BIN" enable-event --kernel --function="$target" "$event_name" -s "$sess_name" > "$OUTPUT_DEST" 2> "$ERROR_OUTPUT_DEST" + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test $ret -ne "0" + ok $? "Enable kernel function event for session $sess_name failed as expected" + else + ok $ret "Enable kernel function event for session $sess_name" + fi +} + +function lttng_enable_kernel_function_event_ok () +{ + lttng_enable_kernel_function_event 0 "$@" +} + function lttng_enable_kernel_userspace_probe_event () { local expected_to_fail="$1" @@ -543,6 +565,10 @@ function start_lttng_sessiond_opt() local withtap=$1 local load_path=$2 + # The rest of the arguments will be passed directly to lttng-sessiond. + shift + shift + local env_vars="" local consumerd="" @@ -587,10 +613,10 @@ function start_lttng_sessiond_opt() # Have a load path ? if [ -n "$load_path" ]; then # shellcheck disable=SC2086 - env $env_vars --load "$load_path" --background "$consumerd" + env $env_vars --load "$load_path" --background "$consumerd" "$@" else # shellcheck disable=SC2086 - env $env_vars --background "$consumerd" + env $env_vars --background "$consumerd" "$@" fi #$DIR/../src/bin/lttng-sessiond/$SESSIOND_BIN --background --consumerd32-path="$DIR/../src/bin/lttng-consumerd/lttng-consumerd" --consumerd64-path="$DIR/../src/bin/lttng-consumerd/lttng-consumerd" --verbose-consumer >>/tmp/sessiond.log 2>&1 status=$? @@ -1491,7 +1517,8 @@ function lttng_load() local expected_to_fail=$1 local opts=$2 - $TESTDIR/../src/bin/lttng/$LTTNG_BIN load $opts 1> $OUTPUT_DEST 2> $ERROR_OUTPUT_DEST + diag "$TESTDIR/../src/bin/lttng/$LTTNG_BIN load $opts" + $TESTDIR/../src/bin/lttng/$LTTNG_BIN load $opts ret=$? if [[ $expected_to_fail -eq "1" ]]; then test $ret -ne "0" @@ -2104,3 +2131,85 @@ function lttng_clear_all () $TESTDIR/../src/bin/lttng/$LTTNG_BIN clear --all 1> $OUTPUT_DEST 2> $ERROR_OUTPUT_DEST ok $? "Clear all lttng sessions" } + +function lttng_add_trigger() +{ + local expected_to_fail="$1" + local trigger_name="$2" + shift 2 + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN add-trigger --id "$trigger_name" "$@" 1> /dev/null 2> /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Add trigger $trigger_name failed as expected" + else + ok $ret "Add trigger $trigger_name" + fi +} + +function lttng_remove_trigger() +{ + local expected_to_fail="$1" + local trigger_name="$2" + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN remove-trigger "$trigger_name" 1> /dev/null 2> /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Remove trigger $trigger_name failed as expected" + else + ok $ret "Remove trigger $trigger_name" + fi +} + +function lttng_add_trigger_ok() +{ + lttng_add_trigger 0 "$@" +} + +function lttng_add_trigger_fail() +{ + lttng_add_trigger 1 "$@" +} + +function lttng_remove_trigger_ok() +{ + lttng_remove_trigger 0 "$@" +} + +function lttng_add_map() +{ + local expected_to_fail="$1" + local map_name="$2" + local session_name="$3" + local domain="$4" + local bitness="$5" + local buf_option="$6" + local coalesced="$7" + + local args=("$domain" "--bitness=$bitness" "$buf_option" "--session=$session_name" "$map_name") + if [ -n "$buf_option" ] + then + args+=("$buf_option") + fi + + if [ -n "$coalesced" ] + then + args+=("$coalesced") + fi + + "$FULL_LTTNG_BIN" add-map ${args[@]} > /dev/null + ret=$? + if [[ $expected_to_fail -eq "1" ]]; then + test "$ret" -ne "0" + ok $? "Map $domain --bitness $bitness creating failed as expected" + else + ok $ret "Map $domain --bitness $bitness created succesfully" + fi +} + +function lttng_add_map_ok() +{ + lttng_add_map 0 "$@" +} -- 2.34.1